国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)

這篇具有很好參考價值的文章主要介紹了Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

相關
《Postgresql源碼(78)plpgsql中調用call proc()時的參數傳遞和賦值(pl參數)》
《Postgresql源碼(79)plpgsql中多層調用時參數傳遞關鍵點分析(pl參數)》
《Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)》

本文涉及模塊:語法分析語義分析、查詢重寫

函數調用時在語義分析階段,transform函數對函數入參進行分析,直觀上需要完成幾步工作:

  1. 檢查是否有函數能匹配上調用輸入的參數列表
  2. 如果匹配不上,是參數個數匹配不上,還是參數類型匹配不上?
  • 如果是個數,用默認參數拼接后能否匹配?【默認參數拼接】
  • 如果是類型,經過類型轉換后能否匹配?【類型轉換】
  1. 如果匹配上了多個,那么需要應該執(zhí)行哪個函數?【多態(tài)】

PG對于上述問題都有了完善的處理邏輯,本篇嘗試分析該過程的處理細節(jié)。

總結

總入口:transformCallStmt

【1】transformCallStmt

  • 頂層函數transformCallStmt負責組裝CallStmt結構({type=T_CallStmt,FuncCall,FuncExpr,List outargs}
  • transformCallStmt組裝步驟:
    1. 調用ParseFuncOrColumn生成CallStmt->FuncExpr、生成CallStmt->FuncExpr->args(不包含指向參數和默認參數)
    2. 調用expand_function_arguments補充CallStmt->FuncExpr->args,加入指向參數和默認參數。
    3. 自己拼接List outargs記錄輸出參數

Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)

【2】CallStmt是如何使用的
(《Postgresql源碼(79)plpgsql中多層調用時參數傳遞關鍵點分析(pl參數)》問題四:內層ExecuteCallStmt如何構造fcinfo->args?)

  • 第一步:ExecuteCallStmt時遍歷CallStmt->FuncExpr->args,把其中的值直接填入fcinfo->args[i].value使用。
  • 第二步:進入pl后,從fcinfo拿到的是緊湊的參數值數組,pl會使用傳入的緊湊數組,把非out值依次賦值。
  • 基于第二步推論:給pl的參數值數組必須每一個in參數都有值,多了少了都會有問題。所以頂層函數必須構造準確的參數值數組CallStmt->FuncExpr->args。

【3】對比Oracle

  • 考慮幾種情況:
    • 情況一:func(入,出,默,默)
      • 調用失敗:call func(值):非默認參數必須全部有值,與Oracle行為一致
      • 調用成功:call func(值,值)
      • 調用成功:call func(值,值,值)
  • 考慮幾種PG不可能發(fā)生的情況(PG要求默認參數后面必須全部是默認參數)(PG要求OUT不能有默認值)(推論:默認參數后面不能有OUT參數)
  • Oracle行為:
    • 情況一:func(入a,出b,默c,出d)
      • 調用失敗:func(值)
      • 調用失?。篺unc(值,變量)
      • 調用成功:func(值,變量,d=>變量)
    • 情況二:func(默a,入b)
      • 調用失?。篶all func(值)
      • 調用成功:call func(值,值)
      • 調用成功:call func(b=>值)
    • 情況三:func(默a,出b)
      • 調用失?。篶all func(值)
      • 調用失?。篶all func(值,值)
      • 調用成功:call func(值,變量)
      • 調用成功:call func(b=>變量)

Oracle的IN OUT類型不能有默認參數,PG可以。
Oracle的OUT參數必須給個變量,否則執(zhí)行肯定報錯。

【4】PG目前的多態(tài)邏輯總結

  • 第一步:ParseFuncOrColumn調用func_get_detail調用FuncnameGetCandidates
    • FuncnameGetCandidates用名字找候選者
    • FuncnameGetCandidates對同名候選者做參數個數檢查:
      • 如果 (proallargtypes個數) > (傳入的全部參數個數):參數不夠,需要補默認
        • 如果(傳入的全部參數個數+默認參數個數) < (proallargtypes個數):補上默認參數就夠用了!
      • 如果:(proallargtypes個數) <= (傳入的全部參數個數):參數直接夠用
    • FuncnameGetCandidates對指向性參數列表調用MatchNamedCall返回argnumbers數組表示映射關系,數組嚴格按位置對應入參,值表示函數參數列表中應該指向的位置。在返回候選函數的參數類型數組時,會用映射關系找到正確的類型順序記錄到候選函數參數類型列表中。(沒有指向型時不走MatchNamedCall且argnumbers數組為空)
  • 第二步:ParseFuncOrColumn返回func_get_detail
    • 【找到嚴格匹配候選者】遍歷FuncnameGetCandidates返回結果,如果能和argtypes嚴格匹配,即找到best_candidate,PGPROC中拉出默認參數列表,刪除掉沒用的,結果放到*argdefaults返回
    • 【沒有嚴格匹配候選者】遍歷FuncnameGetCandidates返回結果,沒有候選者能和argtypes嚴格匹配
      • 首先判斷這是不是一個強制轉換:例如 select int(3.1),如果是的可以當做強制轉換返回
      • 如果不是強制轉換,這里肯定是參數類型對不上了,這里就開始進行【多態(tài)判斷】
        • 判斷入參類型能不能通過轉換 變成 候選者的參數類型:func_match_argtypes
          • 如果只有一個候選者可以匹配, best_candidate = 當前候選者
          • 如果有多個候選者經過轉換可以匹配,選擇一個:func_select_candidate

1 用例

CREATE or replace PROCEDURE tp13(
  a in integer, 
  b out integer,
  c out integer,
  d inout integer default 400,
  e in integer default 500)
LANGUAGE plpgsql
AS $$
BEGIN
  raise notice 'a: %', a;
  raise notice 'b: %', b;
  raise notice 'c: %', c;
  raise notice 'd: %', d;
  raise notice 'e: %', e;
END;
$$;

call tp13 (1,2,3,4,5);
call tp13 (1,2,3,e=>5);

2 頂層函數transformCallStmt

transformCallStmt函數負責轉換所有函數調用節(jié)點,例如:

  • call proc1();
  • select func1();

transformCallStmt函數負責生成CallStmt結構:

typedef struct CallStmt
{
	NodeTag		type;
	FuncCall   *funccall;		/* from the parser */
	FuncExpr   *funcexpr;		/* transformed call, with only input args */
	List	   *outargs;		/* transformed output-argument expressions */
} CallStmt;

CallStmt結構在之前的函數參數分析文章中反復提到過:

  • 其中:FuncCall的args使用A_Const保存全部參數信息(未解析)
  • 其中:FuncExpr的args使用Const只保存IN參數信息(已解析)

截取一部分:Postgresql源碼(79)plpgsql中多層調用時參數傳遞關鍵點分析(pl參數)


Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)


transformCallStmt內部有兩個關鍵調用負責生成CallStmt->FuncExpr結構:
Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)

3 調用ParseFuncOrColumn生成FuncExpr(多態(tài)實現)

ParseFuncOrColumn
  func_get_detail            // 從系統(tǒng)表中找到函數,多態(tài)實現在這里
    FuncnameGetCandidates    // 第一步:找候選者1】用名字匹配遍歷每一個結果
      【2】對于某個結果,拿到PG_PROC參數類型列表proallargtypes
      【3】對于某個結果,檢查參數數目夠不夠?
        【3.1】對于全指向參數或混合型參數輸入
          如果 (proallargtypes個數) >  (傳入的全部參數個數):參數不夠,需要補默認
            如果 (傳入的全部參數個數+默認參數個數) < (proallargtypes個數):補上默認參數就夠用了!
            如果 (傳入的全部參數個數+默認參數個數) >=(proallargtypes個數):補上默認參數也不夠,不使用當前函數。
          如果 (proallargtypes個數) <= (傳入的全部參數個數):參數夠用
          MatchNamedCall判斷指向參數列表是否能匹配當前函數
            例如:call tp13 (1,2,3,e=>5);
            tp13(a in integer, b out integer,c out integer,d inout integer default 400,e in integer default 500)
            MatchNamedCall返回argnumbers數組表示映射關系:
            argnumbers = [0,1,2,4,3]
            給的第一個參數對應當前函數的參數列表中的0位置:a
            給的第二個參數對應當前函數的參數列表中的1位置:b
            給的第三個參數對應當前函數的參數列表中的2位置:c
            給的第四個參數對應當前函數的參數列表中的4位置:e
            只給了4個參數進來,第五個位置補充一個需要默認參數的3位置:d
        【3.2】對于全非指向參數輸入
           只需要判斷參數個數就好了,和上面邏輯類似不在贅述
  
  func_get_detail
    【找到嚴格匹配候選者】遍歷FuncnameGetCandidates返回結果,如果能和argtypes嚴格匹配,即找到best_candidate
      PGPROC中拉出默認參數列表,刪除掉沒用的,結果放到*argdefaults返回

    【沒有嚴格匹配候選者】遍歷FuncnameGetCandidates返回結果,沒有候選者能和argtypes嚴格匹配
      首先判斷這是不是一個強制轉換:例如 select int(3.1),如果是的可以當做強制轉換返回
      如果不是強制轉換,這里肯定是參數類型對不上了,這里就開始進行【多態(tài)判斷】
        判斷入參類型能不能通過轉換 變成 候選者的參數類型:func_match_argtypes
          如果只有一個候選者可以匹配, best_candidate = 當前候選者
          如果有多個候選者經過轉換可以匹配,選擇一個:func_select_candidate
    
    

4 混合參數位置映射關系計算MatchNamedCall

上面給出的結果

            MatchNamedCall返回argnumbers數組表示映射關系:
            argnumbers = [0,1,2,4,3]
            給的第一個參數對應當前函數的參數列表中的0位置:a
            給的第二個參數對應當前函數的參數列表中的1位置:b
            給的第三個參數對應當前函數的參數列表中的2位置:c
            給的第四個參數對應當前函數的參數列表中的4位置:e
            只給了4個參數進來,第五個位置補充一個需要默認參數的3位置:d

涉及代碼分析

執(zhí)行:call tp13 (1,2,3,e=>5);

static bool
MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
			   bool include_out_arguments, int pronargs,
			   int **argnumbers)
{

入參nargs:4(由ParseFuncOrColumn在上層計算參數列表中所有元素)
入參argnames:只記錄指向參數List,只有一個元素char:“e”
入參include_out_arguments:有out參數
入參pronargs:5(pg_proc記錄函數需要五個參數)

	Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
	int			numposargs = nargs - list_length(argnames);
	int			pronallargs;
	Oid		   *p_argtypes;
	char	  **p_argnames;
	char	   *p_argmodes;
	bool		arggiven[FUNC_MAX_ARGS];
	bool		isnull;
	int			ap;				/* call args position */
	int			pp;				/* proargs position */
	ListCell   *lc;

	/* Ignore this function if its proargnames is null */
	(void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargnames,
						   &isnull);
	if (isnull)
		return false;

	/* OK, let's extract the argument names and types */
	pronallargs = get_func_arg_info(proctup,
									&p_argtypes, &p_argnames, &p_argmodes);

給輸出的映射關系數組申請5個int位置

	/* initialize state for matching */
	*argnumbers = (int *) palloc(pronargs * sizeof(int));
	memset(arggiven, false, pronargs * sizeof(bool));

numposargs=nargs - list_length(argnames);
非指向參數個數 = 3 = 給了4個參數 - 有1個參數是指向型
非指向參數可以會直接記錄到argnumbers數組中,同時在arggiven數組中記錄已經給了的參數位置

===================================
函數要求: a in integer, b out integer,c out integer,d inout integer default 400,e in integer default 500
調用傳入: call tp13 (1,2,3,e=>5);
argnumbers:[0,1,2,x,x]
arggiven:[true,true,true,x,x]
===================================

	/* there are numposargs positional args before the named args */
	for (ap = 0; ap < numposargs; ap++)
	{
		(*argnumbers)[ap] = ap;
		arggiven[ap] = true;
	}

檢查指向參數,指向參數的位置由pp偏移

===================================
函數要求: a in integer, b out integer,c out integer,d inout integer default 400,e in integer default 500
調用傳入: call tp13 (1,2,3,e=>5);
argnumbers:[0,1,2,4,x]
arggiven:[true,true,true,false,true]
===================================

	/* now examine the named args */
	foreach(lc, argnames)
	{
		char	   *argname = (char *) lfirst(lc);
		bool		found;
		int			i;

		pp = 0;
		found = false;
		for (i = 0; i < pronallargs; i++)
		{
			/* consider only input params, except with include_out_arguments */
			if (!include_out_arguments &&
				p_argmodes &&
				(p_argmodes[i] != FUNC_PARAM_IN &&
				 p_argmodes[i] != FUNC_PARAM_INOUT &&
				 p_argmodes[i] != FUNC_PARAM_VARIADIC))
				continue;
			if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
			{

指向參數的位置如果有 非指向參數了,直接棄用返回

				if (arggiven[pp])
					return false;
				arggiven[pp] = true;
				(*argnumbers)[ap] = pp;
				found = true;
				break;
			}
			/* increase pp only for considered parameters */
			pp++;
		}
		/* if name isn't in proargnames, fail */
		if (!found)
			return false;
		ap++;
	}

開始檢查默認參數夠不夠?

第一個需要默認參數的是first_arg_with_default=5-2=3

從numposargs開始遍歷(非指向參數個數 = 3 = 給了4個參數 - 有1個參數是指向型

從numposargs開始是合理的,因為pos參數已經給了,不應該再去判斷有沒有default。

	/* Check for default arguments */
	if (nargs < pronargs)
	{
		int			first_arg_with_default = pronargs - procform->pronargdefaults;

		for (pp = numposargs; pp < pronargs; pp++)
		{
			if (arggiven[pp])
				continue;
			/* fail if arg not given and no default available */

如果pp的位置比第一個默認參數還要小,肯定是缺默認參數了,直接放棄返回。
例如:函數(i,i,d,d,d)調用時(i,i)是可以的,調用時(i)就會報錯。

			if (pp < first_arg_with_default)
				return false;
			(*argnumbers)[ap++] = pp;
		}
	}

	Assert(ap == pronargs);		/* processed all function parameters */

	return true;
}

5 調用expand_function_arguments生成FuncExpr->args

expand_function_arguments的邏輯就很簡單了,只是把參數解析后拼接到FuncExpr->args中

(其實這件事情上面的函數已經做過了,但是只是用于參數類型匹配檢測,并沒有真正拼接到FuncExpr->args)文章來源地址http://www.zghlxwxcb.cn/news/detail-466464.html

expand_function_arguments
	...
  	/* If so, we must apply reorder_function_arguments */
	if (has_named_args)
	{
		args = reorder_function_arguments(args, pronargs, func_tuple);
		/* Recheck argument types and add casts if needed */
		recheck_cast_function_args(args, result_type,
								   proargtypes, pronargs,
								   func_tuple);
	}
	else if (list_length(args) < pronargs)
	{
		/* No named args, but we seem to be short some defaults */
		args = add_function_defaults(args, pronargs, func_tuple);
		/* Recheck argument types and add casts if needed */
		recheck_cast_function_args(args, result_type,
								   proargtypes, pronargs,
								   func_tuple);
	}

到了這里,關于Postgresql源碼(84)語義分析——函數調用結構CallStmt的構造與函數多態(tài)的實現(pl參數)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • PG-DBA培訓07:PostgreSQL體系結構深入與源碼解析

    PostgreSQL體系結構深入解析,PostgreSQL數據庫源碼解析,initdb源碼解析 PostgreSQL數據庫體系架構 PostgreSQL數據庫存儲結構 PostgreSQL數據庫進程結構 PostgreSQL數據庫內存結構 PostgreSQL數據庫源碼解析 使用gdb跟蹤分析PostgreSQL源碼 PostgreSQL源碼解析之initdb初始化過程 PostgreSQL源碼解析之PG啟動

    2024年02月15日
    瀏覽(25)
  • postgresql 內核源碼分析 btree索引的增刪查代碼基本原理流程分析,索引膨脹的原因在這里

    ? 專欄內容 : postgresql內核源碼分析 手寫數據庫toadb 并發(fā)編程 ? 開源貢獻 : toadb開源庫 個人主頁 :我的主頁 管理社區(qū) :開源數據庫 座右銘:天行健,君子以自強不息;地勢坤,君子以厚德載物. 在postgresql最常用的索引就是btree,它支持范圍和等值查詢。 本文主要介紹

    2024年02月11日
    瀏覽(24)
  • 5. 函數調用過程匯編分析

    5. 函數調用過程匯編分析

    __cdecl 調用方式 __stdcall 調用方式 __fastcall 調用方式 不同的編譯器實現不一樣,上述情況只是VC++6.0的編譯實現 即便是在同一個編譯器,開啟優(yōu)化和關閉優(yōu)化也不一樣 即便是同一個編譯器同一種模式,32位和64位下情況也會不一樣 參考 GCC GNU 文檔屬性描述 fastcall On x86-32 target

    2024年01月22日
    瀏覽(22)
  • postgresql 內核源碼分析 btree索引插入分析,索引頁面分裂流程,多舉措進行并發(fā)優(yōu)化,對異常進行保護處理

    postgresql 內核源碼分析 btree索引插入分析,索引頁面分裂流程,多舉措進行并發(fā)優(yōu)化,對異常進行保護處理

    ? 專欄內容 : postgresql內核源碼分析 手寫數據庫toadb 并發(fā)編程 ? 開源貢獻 : toadb開源庫 個人主頁 :我的主頁 管理社區(qū) :開源數據庫 座右銘:天行健,君子以自強不息;地勢坤,君子以厚德載物. B樹索引在PostgreSQL中得到了廣泛應用,它是一種自平衡樹數據結構,可以維

    2024年02月08日
    瀏覽(34)
  • ARM64函數調用流程分析

    ARM64函數調用流程分析

    ARM64 程序調用標準 下圖是介紹一個簡單函數調用的示例,在該示例中簡單介紹了棧的使用。 2.1.1 main的C代碼實現 2.1.2 main函數對應匯編及其分析 0000000000000114 main: main函數的入口 114: a9be7bfd stp x29, x30, [sp, #-32]! 將sp = sp - 32,為main函數開一個32Byte的??臻g,然后將x29(FP),X30(LR)寄

    2024年02月11日
    瀏覽(18)
  • 【Linux 內核源碼分析筆記】系統(tǒng)調用

    【Linux 內核源碼分析筆記】系統(tǒng)調用

    在Linux內核中,系統(tǒng)調用是用戶空間程序與內核之間的接口,它允許用戶空間程序請求內核執(zhí)行特權操作或訪問受保護的內核資源。系統(tǒng)調用提供了一種安全可控的方式,使用戶程序能夠利用內核功能而不直接訪問底層硬件。 系統(tǒng)調用: 通過系統(tǒng)調用,用戶程序可以請求內核

    2024年02月03日
    瀏覽(30)
  • X86_64函數調用匯編程序分析

    X86_64函數調用匯編程序分析

    %rdi, %rsi, %rdx, %rcx, %r8, %r9分別用于函數調用過程中的前6個參數,對于6的參數存放在棧中傳遞 %rsp用做棧指針寄存器,指向棧頂 %rbp用作??蚣拇嫫鳎赶驐5?%rax用做函數返回值的第一個寄存器 2.1.1 main的C代碼實現 2.1.2 main函數對應匯編及其分析 這段匯編代碼實現了一個簡單的

    2024年02月09日
    瀏覽(18)
  • Day 84:網絡結構與參數

    單層數據 多層管理

    2024年02月11日
    瀏覽(16)
  • Postgresql源碼(110)分析dsm動態(tài)共享內存分配與共享內存mq實例(dsm/toc接口備忘錄)

    相關 《Postgresql源碼(90)共享內存申請CreateSharedMemoryAndSemaphores》 《Linux內存映射函數mmap與匿名內存塊》 《Linux共享內存與子進程繼承》 用dsm框架的流程 評估共享內存大?。憾啻斡胹hm_toc_estimate_chunk、shm_toc_estimate_keys向estimate中增加數據結構,最后用shm_toc_estimate得出剛才增加

    2024年02月14日
    瀏覽(28)
  • 《Linux內核源碼分析》(2)進程原理及系統(tǒng)調用

    《Linux內核源碼分析》(2)進程原理及系統(tǒng)調用

    操作系統(tǒng)的作用 :作為硬件的使用層,提供使用硬件資源的能力, 進程的作用 :作為操作系統(tǒng)使用層,提供使用操作系統(tǒng)抽象出的資源層的能力 進程、線程和程序的區(qū)別 :進程指計算機中已運行的程序。進程本身不是基本的運行單位,而是線程的容器。 程序本身只是指令

    2024年02月07日
    瀏覽(36)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包