ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(241)- plpgsql(CreateFunction)

PostgreSQL 源码解读(241)- plpgsql(CreateFunction)

原创 PostgreSQL 作者:husthxd 时间:2020-02-19 17:48:42 0 删除 编辑

本节简单介绍了PostgreSQL创建函数的过程,其实现函数是CreateFunction。

一、数据结构

Form_pg_language
plpgsql语言定义结构体

/* ----------------
 *      pg_language definition.  cpp turns this into
 *      typedef struct FormData_pg_language
 * ----------------
 */
CATALOG(pg_language,2612,LanguageRelationId)
{
    Oid         oid;            /* oid */
    /* Language name */
    NameData    lanname;
    /* Language's owner */
    Oid         lanowner BKI_DEFAULT(PGUID);
    /* Is a procedural language */
    bool        lanispl BKI_DEFAULT(f);
    /* PL is trusted */
    bool        lanpltrusted BKI_DEFAULT(f);
    /* Call handler, if it's a PL */
    Oid         lanplcallfoid BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    /* Optional anonymous-block handler function */
    Oid         laninline BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    /* Optional validation function */
    Oid         lanvalidator BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
#ifdef CATALOG_VARLEN           /* variable-length fields start here */
    /* Access privileges */
    aclitem     lanacl[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_language;
/* ----------------
 *      Form_pg_language corresponds to a pointer to a tuple with
 *      the format of pg_language relation.
 * ----------------
 */
typedef FormData_pg_language *Form_pg_language;

ArrayType


/*
 * Arrays are varlena objects, so must meet the varlena convention that
 * the first int32 of the object contains the total object size in bytes.
 * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
 * Arrays是可变对象集,必须符合varlena约定,即对象的第一个int32包含对象的总大小(以字节为单位)。
 * 但是,一定要确保使用VARSIZE和SET_VARSIZE函数范围该结构体
 *
 * CAUTION: if you change the header for ordinary arrays you will also
 * need to change the headers for oidvector and int2vector!
 */
typedef struct
{
    //可变的header
    int32       vl_len_;        /* varlena header (do not touch directly!) */
    //维度
    int         ndim;           /* # of dimensions */
    //指向数据的偏移量,如为0则表示没有位图
    int32       dataoffset;     /* offset to data, or 0 if no bitmap */
    //元素类型的OID
    Oid         elemtype;       /* element type OID */
} ArrayType;

二、源码解读


/*
 * CreateFunction
 *   Execute a CREATE FUNCTION (or CREATE PROCEDURE) utility statement.
 *   执行CREATE FUNCTION (or CREATE PROCEDURE)语句
 */
ObjectAddress
CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
{
  char     *probin_str;
  char     *prosrc_str;
  Oid     prorettype;
  bool    returnsSet;
  char     *language;
  Oid     languageOid;
  Oid     languageValidator;
  Node     *transformDefElem = NULL;
  char     *funcname;
  Oid     namespaceId;//命名空间ID(schema ID)
  AclResult aclresult;//权限检查
  oidvector  *parameterTypes;
  ArrayType  *allParameterTypes;
  ArrayType  *parameterModes;
  ArrayType  *parameterNames;
  List     *parameterDefaults;
  Oid     variadicArgType;
  List     *trftypes_list = NIL;
  ArrayType  *trftypes;
  Oid     requiredResultType;
  bool    isWindowFunc,
        isStrict,
        security,
        isLeakProof;
  char    volatility;
  ArrayType  *proconfig;
  float4    procost;
  float4    prorows;
  Oid     prosupport;
  HeapTuple languageTuple;
  Form_pg_language languageStruct;
  List     *as_clause;
  char    parallel;
  /* Convert list of names to a name and namespace */
  //转换名称列表为函数名称和命名空间
  namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
                          &funcname);
  /* Check we have creation rights in target namespace */
  //检查权限是否足够(是否可以在目标命名空间中创建对象)
  aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
  if (aclresult != ACLCHECK_OK)
    aclcheck_error(aclresult, OBJECT_SCHEMA,
             get_namespace_name(namespaceId));
  /* Set default attributes */
  //设置默认的属性
  isWindowFunc = false;//是否窗口函数
  isStrict = false;//是否严格的函数
  security = false;//安全性
  isLeakProof = false;//
  volatility = PROVOLATILE_VOLATILE;
  proconfig = NULL;
  procost = -1;       /* indicates not set */
  prorows = -1;       /* indicates not set */
  prosupport = InvalidOid;
  parallel = PROPARALLEL_UNSAFE;//非并行安全
  /* Extract non-default attributes from stmt->options list */
  //从stmt->options链表中抽取非默认属性
  compute_function_attributes(pstate,
                stmt->is_procedure,//是否过程?
                stmt->options,//选项
                &as_clause, &language, &transformDefElem,
                &isWindowFunc, &volatility,
                &isStrict, &security, &isLeakProof,
                &proconfig, &procost, &prorows,
                &prosupport, &parallel);
  /* Look up the language and validate permissions */
  //检索语言并验证授权(比如pl/python这类语言,不一定会支持)
  languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
  if (!HeapTupleIsValid(languageTuple))
    ereport(ERROR,
        (errcode(ERRCODE_UNDEFINED_OBJECT),
         errmsg("language \"%s\" does not exist", language),
         (PLTemplateExists(language) ?
          errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
  //语言结构体和OID
  languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
  languageOid = languageStruct->oid;
  if (languageStruct->lanpltrusted)
  {
    /* if trusted language, need USAGE privilege */
    //可信语言,需要USAGE权限
    AclResult aclresult;
    aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE);
    if (aclresult != ACLCHECK_OK)
      aclcheck_error(aclresult, OBJECT_LANGUAGE,
               NameStr(languageStruct->lanname));
  }
  else
  {
    /* if untrusted language, must be superuser */
    //非可信语言,必须是超级用户才能创建
    if (!superuser())
      aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_LANGUAGE,
               NameStr(languageStruct->lanname));
  }
  languageValidator = languageStruct->lanvalidator;
  ReleaseSysCache(languageTuple);
  /*
   * Only superuser is allowed to create leakproof functions because
   * leakproof functions can see tuples which have not yet been filtered out
   * by security barrier views or row level security policies.
   * 只有超级用户才允许创建leakproof函数
   */
  if (isLeakProof && !superuser())
    ereport(ERROR,
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
         errmsg("only superuser can define a leakproof function")));
  if (transformDefElem)
  {
    ListCell   *lc;
    foreach(lc, castNode(List, transformDefElem))
    {
      //获取类型ID
      Oid     typeid = typenameTypeId(NULL,
                        lfirst_node(TypeName, lc));
      //基础类型
      Oid     elt = get_base_element_type(typeid);
      //如有基础类型则用基础类型,否则用类型ID
      typeid = elt ? elt : typeid;
      //
      get_transform_oid(typeid, languageOid, false);
      //写入到trftypes_list中
      trftypes_list = lappend_oid(trftypes_list, typeid);
    }
  }
  /*
   * Convert remaining parameters of CREATE to form wanted by
   * ProcedureCreate.
   * 转换CREATE中剩下的参数,用于ProcedureCreate调用
   */
  interpret_function_parameter_list(pstate,
                    stmt->parameters,
                    languageOid,
                    stmt->is_procedure ? OBJECT_PROCEDURE : OBJECT_FUNCTION,
                    &parameterTypes,
                    &allParameterTypes,
                    &parameterModes,
                    &parameterNames,
                    &parameterDefaults,
                    &variadicArgType,
                    &requiredResultType);
  if (stmt->is_procedure)
  {
    //过程
    Assert(!stmt->returnType);
    prorettype = requiredResultType ? requiredResultType : VOIDOID;
    returnsSet = false;
  }
  else if (stmt->returnType)
  {
    //存在返回类型:显式的RETURNS语句
    /* explicit RETURNS clause */
    //获取返回类型
    compute_return_type(stmt->returnType, languageOid,
              &prorettype, &returnsSet);
    if (OidIsValid(requiredResultType) && prorettype != requiredResultType)
      ereport(ERROR,
          (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
           errmsg("function result type must be %s because of OUT parameters",
              format_type_be(requiredResultType))));
  }
  else if (OidIsValid(requiredResultType))
  {
    /* default RETURNS clause from OUT parameters */
    //通过OUT参数作为返回参数
    prorettype = requiredResultType;
    returnsSet = false;
  }
  else
  {
    //没有指定结果类型
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("function result type must be specified")));
    /* Alternative possibility: default to RETURNS VOID */
    prorettype = VOIDOID;
    returnsSet = false;
  }
  if (list_length(trftypes_list) > 0)
  {
    //处理类型链表
    ListCell   *lc;
    Datum    *arr;
    int     i;
    arr = palloc(list_length(trftypes_list) * sizeof(Datum));
    i = 0;
    foreach(lc, trftypes_list)
      arr[i++] = ObjectIdGetDatum(lfirst_oid(lc));
    trftypes = construct_array(arr, list_length(trftypes_list),
                   OIDOID, sizeof(Oid), true, 'i');
  }
  else
  {
    /* store SQL NULL instead of empty array */
    trftypes = NULL;
  }
  //解析AS语句
  interpret_AS_clause(languageOid, language, funcname, as_clause,
            &prosrc_str, &probin_str);
  /*
   * Set default values for COST and ROWS depending on other parameters;
   * reject ROWS if it's not returnsSet.  NB: pg_dump knows these default
   * values, keep it in sync if you change them.
   * 基于其他参数设置COST和ROWS的默认值
   */
  if (procost < 0)
  {
    /* SQL and PL-language functions are assumed more expensive */
    //SQL和PL-XXX函数假定成本更高
    if (languageOid == INTERNALlanguageId ||
      languageOid == ClanguageId)
      procost = 1;
    else
      procost = 100;
  }
  if (prorows < 0)
  {
    if (returnsSet)
      prorows = 1000;
    else
      prorows = 0;    /* dummy value if not returnsSet */
  }
  else if (!returnsSet)
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
         errmsg("ROWS is not applicable when function does not return a set")));
  /*
   * And now that we have all the parameters, and know we're permitted to do
   * so, go ahead and create the function.
   * 这时候已准备好所有的参数,并且已验证具备相应的权限,创建函数
   */
  return ProcedureCreate(funcname,
               namespaceId,
               stmt->replace,
               returnsSet,
               prorettype,
               GetUserId(),
               languageOid,
               languageValidator,
               prosrc_str,  /* converted to text later */
               probin_str,  /* converted to text later */
               stmt->is_procedure ? PROKIND_PROCEDURE : (isWindowFunc ? PROKIND_WINDOW : PROKIND_FUNCTION),
               security,
               isLeakProof,
               isStrict,
               volatility,
               parallel,
               parameterTypes,
               PointerGetDatum(allParameterTypes),
               PointerGetDatum(parameterModes),
               PointerGetDatum(parameterNames),
               parameterDefaults,
               PointerGetDatum(trftypes),
               PointerGetDatum(proconfig),
               prosupport,
               procost,
               prorows);
}

三、跟踪分析

测试脚本


create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)
returns record 
as
$$
declare
begin
  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;
  pio_v3 := 'pio_v3 i/o';
  po_v4 := 100;
  po_v5 := 'po_v5 out';
end;
$$ LANGUAGE plpgsql;

启动GDB跟踪

(gdb) b CreateFunction 
Breakpoint 1 at 0x670b94: file functioncmds.c, line 929.
(gdb) c
Continuing.
Breakpoint 1, CreateFunction (pstate=0x2b02cb8, stmt=0x2add8f8) at functioncmds.c:929
929     Node       *transformDefElem = NULL;
(gdb) bt
#0  CreateFunction (pstate=0x2b02cb8, stmt=0x2add8f8) at functioncmds.c:929
#1  0x00000000008f61a6 in ProcessUtilitySlow (pstate=0x2b02cb8, pstmt=0x2addc78, 
    queryString=0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, 
    params=0x0, queryEnv=0x0, dest=0x2addd70, completionTag=0x7fffef099ca0 "")
    at utility.c:1478
#2  0x00000000008f5069 in standard_ProcessUtility (pstmt=0x2addc78, 
    queryString=0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, 
    params=0x0, queryEnv=0x0, dest=0x2addd70, completionTag=0x7fffef099ca0 "")
    at utility.c:927
#3  0x00000000008f418f in ProcessUtility (pstmt=0x2addc78, 
    queryString=0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, 
    params=0x0, queryEnv=0x0, dest=0x2addd70, completionTag=0x7fffef099ca0 "")
    at utility.c:360
#4  0x00000000008f3188 in PortalRunUtility (portal=0x2b43278, pstmt=0x2addc78, 
---Type <return> to continue, or q <return> to quit---
    isTopLevel=true, setHoldSnapshot=false, dest=0x2addd70, completionTag=0x7fffef099ca0 "")
    at pquery.c:1175
#5  0x00000000008f339e in PortalRunMulti (portal=0x2b43278, isTopLevel=true, 
    setHoldSnapshot=false, dest=0x2addd70, altdest=0x2addd70, completionTag=0x7fffef099ca0 "")
    at pquery.c:1321
#6  0x00000000008f28d3 in PortalRun (portal=0x2b43278, count=9223372036854775807, 
    isTopLevel=true, run_once=true, dest=0x2addd70, altdest=0x2addd70, 
    completionTag=0x7fffef099ca0 "") at pquery.c:796
#7  0x00000000008ec882 in exec_simple_query (
    query_string=0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="...) at postgres.c:1215
#8  0x00000000008f0b04 in PostgresMain (argc=1, argv=0x2b09318, dbname=0x2b09160 "testdb", 
    username=0x2ad8b28 "pg12") at postgres.c:4247
#9  0x0000000000846fa8 in BackendRun (port=0x2afdb10) at postmaster.c:4437
#10 0x0000000000846786 in BackendStartup (port=0x2afdb10) at postmaster.c:4128
#11 0x00000000008429b4 in ServerLoop () at postmaster.c:1704
#12 0x000000000084226a in PostmasterMain (argc=1, argv=0x2ad6ae0) at postmaster.c:1377
#13 0x0000000000762364 in main (argc=1, argv=0x2ad6ae0) at main.c:228
(gdb) 
(gdb)

输入参数,pstate是ParseState结构体,stmt类型是CreateFunctionStmt结构体

(gdb) p *pstate
$1 = {parentParseState = 0x0, 
  p_sourcetext = 0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., p_rtable = 0x0, p_joinexprs = 0x0, 
  p_joinlist = 0x0, p_namespace = 0x0, p_lateral_active = false, p_ctenamespace = 0x0, 
  p_future_ctes = 0x0, p_parent_cte = 0x0, p_target_relation = 0x0, 
  p_target_rangetblentry = 0x0, p_is_insert = false, p_windowdefs = 0x0, 
  p_expr_kind = EXPR_KIND_NONE, p_next_resno = 1, p_multiassign_exprs = 0x0, 
  p_locking_clause = 0x0, p_locked_from_parent = false, p_resolve_unknowns = true, 
  p_queryEnv = 0x0, p_hasAggs = false, p_hasWindowFuncs = false, p_hasTargetSRFs = false, 
  p_hasSubLinks = false, p_hasModifyingCTE = false, p_last_srf = 0x0, 
  p_pre_columnref_hook = 0x0, p_post_columnref_hook = 0x0, p_paramref_hook = 0x0, 
  p_coerce_param_hook = 0x0, p_ref_hook_state = 0x0}
(gdb) p *stmt
$2 = {type = T_CreateFunctionStmt, is_procedure = false, replace = true, 
  funcname = 0x2adcb58, parameters = 0x2adcd60, returnType = 0x2add580, options = 0x2add818}

获取namespace

(gdb) n
939     List       *trftypes_list = NIL;
(gdb) 
957     namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
(gdb) 
961     aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
(gdb) p namespaceId
$3 = 2200
(gdb)

namespace是public

[local:/data/run/pg12]:5120 pg12@testdb=# select * from pg_namespace;
  oid  |      nspname       | nspowner |         nspacl          
-------+--------------------+----------+-------------------------
    99 | pg_toast           |       10 | 
 12314 | pg_temp_1          |       10 | 
 12315 | pg_toast_temp_1    |       10 | 
    11 | pg_catalog         |       10 | {pg12=UC/pg12,=U/pg12}
  2200 | public             |       10 | {pg12=UC/pg12,=UC/pg12}
 13291 | information_schema |       10 | {pg12=UC/pg12,=U/pg12}
(6 rows)

执行权限检查,初始化属性默认值

(gdb) n
962     if (aclresult != ACLCHECK_OK)
(gdb) p aclresult
$4 = ACLCHECK_OK
(gdb) n
967     isWindowFunc = false;
(gdb) 
968     isStrict = false;
(gdb) 
969     security = false;
(gdb) 
970     isLeakProof = false;
(gdb) 
971     volatility = PROVOLATILE_VOLATILE;
(gdb) 
972     proconfig = NULL;
(gdb) 
973     procost = -1;               /* indicates not set */
(gdb) 
974     prorows = -1;               /* indicates not set */
(gdb) 
975     prosupport = InvalidOid;
(gdb) 
976     parallel = PROPARALLEL_UNSAFE;
(gdb) 
979     compute_function_attributes(pstate,

调用compute_function_attributes从stmt->options链表中抽取非默认属性

(gdb) 
989     languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
(gdb) p *language
$33 = 112 'p'
(gdb) p *transformDefElem
Cannot access memory at address 0x0
(gdb) p isWindowFunc
$34 = false
(gdb) p volatility
$35 = 118 'v'
(gdb) p isStrict
$36 = false
(gdb) p security
$37 = false
(gdb) p isLeakProof
$38 = false
(gdb) p proconfig
$39 = (ArrayType *) 0x0
(gdb) p procost
$40 = -1
(gdb) p prorows
$41 = -1
(gdb) p prosupport
$42 = 0
(gdb) p parallel
$43 = 117 'u'
(gdb) p stmt->options
$44 = (List *) 0x2add818
(gdb) p *stmt->options
$45 = {type = T_List, length = 2, head = 0x2add7f0, tail = 0x2add8d0}
(gdb) p *(Node *)stmt->options->head->data.ptr_value
$46 = {type = T_DefElem}
(gdb) p *(DefElem *)stmt->options->head->data.ptr_value
$47 = {type = T_DefElem, defnamespace = 0x0, defname = 0xbbf727 "as", arg = 0x2add760, 
  defaction = DEFELEM_UNSPEC, location = 134}
(gdb) p *((DefElem *)stmt->options->head->data.ptr_value)->arg
$48 = {type = T_List}
(gdb) p *pstate
$5 = {parentParseState = 0x0, 
  p_sourcetext = 0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., p_rtable = 0x0, p_joinexprs = 0x0, 
  p_joinlist = 0x0, p_namespace = 0x0, p_lateral_active = false, p_ctenamespace = 0x0, 
  p_future_ctes = 0x0, p_parent_cte = 0x0, p_target_relation = 0x0, 
  p_target_rangetblentry = 0x0, p_is_insert = false, p_windowdefs = 0x0, 
  p_expr_kind = EXPR_KIND_NONE, p_next_resno = 1, p_multiassign_exprs = 0x0, 
  p_locking_clause = 0x0, p_locked_from_parent = false, p_resolve_unknowns = true, 
  p_queryEnv = 0x0, p_hasAggs = false, p_hasWindowFuncs = false, p_hasTargetSRFs = false, 
  p_hasSubLinks = false, p_hasModifyingCTE = false, p_last_srf = 0x0, 
  p_pre_columnref_hook = 0x0, p_post_columnref_hook = 0x0, p_paramref_hook = 0x0, 
  p_coerce_param_hook = 0x0, p_ref_hook_state = 0x0}
(gdb) x/1024c pstate->p_sourcetext
0x2adbf08:  99 'c'  114 'r' 101 'e' 97 'a'  116 't' 101 'e' 32 ' '  111 'o'
0x2adbf10:  114 'r' 32 ' '  114 'r' 101 'e' 112 'p' 108 'l' 97 'a'  99 'c'
0x2adbf18:  101 'e' 32 ' '  102 'f' 117 'u' 110 'n' 99 'c'  116 't' 105 'i'
0x2adbf20:  111 'o' 110 'n' 32 ' '  102 'f' 117 'u' 110 'n' 99 'c'  95 '_'
0x2adbf28:  116 't' 101 'e' 115 's' 116 't' 40 '('  112 'p' 105 'i' 95 '_'
0x2adbf30:  118 'v' 49 '1'  32 ' '  105 'i' 110 'n' 32 ' '  105 'i' 110 'n'
0x2adbf38:  116 't' 44 ','  112 'p' 105 'i' 95 '_'  118 'v' 50 '2'  32 ' '
0x2adbf40:  118 'v' 97 'a'  114 'r' 99 'c'  104 'h' 97 'a'  114 'r' 44 ','
0x2adbf48:  112 'p' 105 'i' 111 'o' 95 '_'  118 'v' 51 '3'  32 ' '  105 'i'
0x2adbf50:  110 'n' 111 'o' 117 'u' 116 't' 32 ' '  118 'v' 97 'a'  114 'r'
0x2adbf58:  99 'c'  104 'h' 97 'a'  114 'r' 44 ','  112 'p' 111 'o' 95 '_'
0x2adbf60:  118 'v' 52 '4'  32 ' '  111 'o' 117 'u' 116 't' 32 ' '  105 'i'
0x2adbf68:  110 'n' 116 't' 44 ','  112 'p' 111 'o' 95 '_'  118 'v' 53 '5'
0x2adbf70:  32 ' '  111 'o' 117 'u' 116 't' 32 ' '  118 'v' 97 'a'  114 'r'
0x2adbf78:  99 'c'  104 'h' 97 'a'  114 'r' 41 ')'  10 '\n' 114 'r' 101 'e'
0x2adbf80:  116 't' 117 'u' 114 'r' 110 'n' 115 's' 32 ' '  114 'r' 101 'e'
0x2adbf88:  99 'c'  111 'o' 114 'r' 100 'd' 32 ' '  10 '\n' 97 'a'  115 's'
0x2adbf90:  10 '\n' 36 '$'  36 '$'  10 '\n' 100 'd' 101 'e' 99 'c'  108 'l'
0x2adbf98:  97 'a'  114 'r' 101 'e' 10 '\n' 98 'b'  101 'e' 103 'g' 105 'i'
0x2adbfa0:  110 'n' 10 '\n' 32 ' '  32 ' '  114 'r' 97 'a'  105 'i' 115 's'
---Type <return> to continue, or q <return> to quit---
0x2adbfa8:  101 'e' 32 ' '  110 'n' 111 'o' 116 't' 105 'i' 99 'c'  101 'e'
0x2adbfb0:  32 ' '  39 '\'' 112 'p' 105 'i' 95 '_'  118 'v' 49 '1'  32 ' '
0x2adbfb8:  58 ':'  61 '='  32 ' '  37 '%'  44 ','  112 'p' 105 'i' 95 '_'
0x2adbfc0:  118 'v' 50 '2'  32 ' '  58 ':'  61 '='  32 ' '  37 '%'  44 ','
0x2adbfc8:  112 'p' 105 'i' 95 '_'  118 'v' 51 '3'  32 ' '  58 ':'  61 '='
0x2adbfd0:  32 ' '  37 '%'  39 '\'' 44 ','  112 'p' 105 'i' 95 '_'  118 'v'
0x2adbfd8:  49 '1'  44 ','  112 'p' 105 'i' 95 '_'  118 'v' 50 '2'  44 ','
0x2adbfe0:  112 'p' 105 'i' 111 'o' 95 '_'  118 'v' 51 '3'  59 ';'  10 '\n'
0x2adbfe8:  32 ' '  32 ' '  112 'p' 105 'i' 111 'o' 95 '_'  118 'v' 51 '3'
0x2adbff0:  32 ' '  58 ':'  61 '='  32 ' '  39 '\'' 112 'p' 105 'i' 111 'o'
0x2adbff8:  95 '_'  118 'v' 51 '3'  32 ' '  105 'i' 47 '/'  111 'o' 39 '\''
0x2adc000:  59 ';'  10 '\n' 32 ' '  32 ' '  112 'p' 111 'o' 95 '_'  118 'v'
0x2adc008:  52 '4'  32 ' '  58 ':'  61 '='  32 ' '  49 '1'  48 '0'  48 '0'
0x2adc010:  59 ';'  10 '\n' 32 ' '  32 ' '  112 'p' 111 'o' 95 '_'  118 'v'
0x2adc018:  53 '5'  32 ' '  58 ':'  61 '='  32 ' '  39 '\'' 112 'p' 111 'o'
0x2adc020:  95 '_'  118 'v' 53 '5'  32 ' '  111 'o' 117 'u' 116 't' 39 '\''
0x2adc028:  59 ';'  10 '\n' 101 'e' 110 'n' 100 'd' 59 ';'  10 '\n' 36 '$'
0x2adc030:  36 '$'  32 ' '  76 'L'  65 'A'  78 'N'  71 'G'  85 'U'  65 'A'
0x2adc038:  71 'G'  69 'E'  32 ' '  112 'p' 108 'l' 112 'p' 103 'g' 115 's'
0x2adc040:  113 'q' 108 'l' 59 ';'  0 '\000'    0 '\000'    127 '\177'  127 '\1
---Type <return> to continue, or q <return> to quit---^CQuit
(gdb) 
(gdb) n

获取language

990     if (!HeapTupleIsValid(languageTuple))
(gdb) p languageTuple
$7 = (HeapTuple) 0x7fcc407e0bf8
(gdb) p *languageTuple
$8 = {t_len = 120, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 4}, 
  t_tableOid = 2612, t_data = 0x7fcc407e0c20}
(gdb) n
997     languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
(gdb) 
998     languageOid = languageStruct->oid;
(gdb) p *languageStruct
$9 = {oid = 13581, lanname = {data = "plpgsql", '\000' <repeats 56 times>}, lanowner = 10, 
  lanispl = true, lanpltrusted = true, lanplcallfoid = 13578, laninline = 13579, 
  lanvalidator = 13580}
(gdb) n
1000        if (languageStruct->lanpltrusted)
(gdb) 
1005            aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE);
(gdb) 
1006            if (aclresult != ACLCHECK_OK)
(gdb) 
1018        languageValidator = languageStruct->lanvalidator;
(gdb) 
1020        ReleaseSysCache(languageTuple);
(gdb) 
1027        if (isLeakProof && !superuser())
(gdb) 
1032        if (transformDefElem)
(gdb) 
1056                                          stmt->is_procedure ? OBJECT_PROCEDURE : OBJECT_FUNCTION,
(gdb)

转换CREATE中剩下的参数,用于ProcedureCreate调用

1053        interpret_function_parameter_list(pstate,
(gdb) 
1065        if (stmt->is_procedure)
(gdb) p *pstate
$10 = {parentParseState = 0x0, 
  p_sourcetext = 0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., p_rtable = 0x0, p_joinexprs = 0x0, 
  p_joinlist = 0x0, p_namespace = 0x0, p_lateral_active = false, p_ctenamespace = 0x0, 
  p_future_ctes = 0x0, p_parent_cte = 0x0, p_target_relation = 0x0, 
  p_target_rangetblentry = 0x0, p_is_insert = false, p_windowdefs = 0x0, 
  p_expr_kind = EXPR_KIND_NONE, p_next_resno = 1, p_multiassign_exprs = 0x0, 
  p_locking_clause = 0x0, p_locked_from_parent = false, p_resolve_unknowns = true, 
  p_queryEnv = 0x0, p_hasAggs = false, p_hasWindowFuncs = false, p_hasTargetSRFs = false, 
  p_hasSubLinks = false, p_hasModifyingCTE = false, p_last_srf = 0x0, 
  p_pre_columnref_hook = 0x0, p_post_columnref_hook = 0x0, p_paramref_hook = 0x0, 
  p_coerce_param_hook = 0x0, p_ref_hook_state = 0x0}
(gdb) p *stmt
$11 = {type = T_CreateFunctionStmt, is_procedure = false, replace = true, 
  funcname = 0x2adcb58, parameters = 0x2adcd60, returnType = 0x2add580, options = 0x2add818}
(gdb) p *stmt->parameters
$12 = {type = T_List, length = 5, head = 0x2adcd38, tail = 0x2add4b0}
(gdb) p parameterTypes
$13 = (oidvector *) 0x2bed1a8
(gdb) p *parameterTypes
$14 = {vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0, 
  values = 0x2bed1c0}
(gdb) x/144h 0x2bed1c0
0x2bed1c0:  23 '\027'   0 '\000'    19 '\023'   0 '\000'    19 '\023'     0 '\000'  126 '~' 127 '\177'
0x2bed1d0:  127 '\177'  127 '\177'  127 '\177'  127 '\177'  127 '\177'    127 '\177'    127 '\177'  127 '\177'
0x2bed1e0:  127 '\177'  127 '\177'  127 '\177'  127 '\177'  64 '@'  0 '\000'    0 '\000'    0 '\000'
0x2bed1f0:  44 ','  0 '\000'    0 '\000'    0 '\000'    -96 '\240'  -80 '\260'  0 '\000'    0 '\000'
0x2bed200:  -80 '\260'  0 '\000'    1 '\001'    0 '\000'    0 '\000'      0 '\000'  26 '\032'   0 '\000'
0x2bed210:  5 '\005'    0 '\000'    1 '\001'    0 '\000'    23 '\027'     0 '\000'  19 '\023'   0 '\000'
0x2bed220:  19 '\023'   0 '\000'    23 '\027'   0 '\000'    19 '\023'     0 '\000'  126 '~' 127 '\177'
0x2bed230:  127 '\177'  127 '\177'  127 '\177'  127 '\177'  127 '\177'    127 '\177'    127 '\177'  127 '\177'
0x2bed240:  32 ' '  0 '\000'    0 '\000'    0 '\000'    29 '\035'   0 '\000'    0 '\000'    0 '\000'
0x2bed250:  -96 '\240'  -80 '\260'  0 '\000'    0 '\000'    116 't' 0 '\000'    1 '\001'    0 '\000'
---Type <return> to continue, or q <return> to quit---^CQuit
(gdb) n
1071        else if (stmt->returnType)
(gdb) p *stmt->returnType
$15 = {type = T_TypeName, names = 0x2add548, typeOid = 0, setof = false, pct_type = false, 
  typmods = 0x0, typemod = -1, arrayBounds = 0x0, location = 126}
(gdb) p *stmt->returnType->names
$16 = {type = T_List, length = 1, head = 0x2add520, tail = 0x2add520}
(gdb) n
1074            compute_return_type(stmt->returnType, languageOid,
(gdb) 
1076            if (OidIsValid(requiredResultType) && prorettype != requiredResultType)
(gdb) p *prorettype
Cannot access memory at address 0x8c9
(gdb) p prorettype
$17 = 2249
(gdb) p returnsSet
$18 = false
(gdb) p 
$19 = false
(gdb) p *allParameterTypes
$20 = {vl_len_ = 176, ndim = 1, dataoffset = 0, elemtype = 26}
(gdb) p parameterModes
$21 = (ArrayType *) 0x2bed258
(gdb) p *parameterModes
$22 = {vl_len_ = 116, ndim = 1, dataoffset = 0, elemtype = 18}
(gdb) p *parameterNames
$23 = {vl_len_ = 336, ndim = 1, dataoffset = 0, elemtype = 25}
(gdb) p *parameterDefaults
Cannot access memory at address 0x0
(gdb) p *variadicArgType
Cannot access memory at address 0x0
(gdb) p *requiredResultType
Cannot access memory at address 0x8c9
(gdb) p requiredResultType
$24 = 2249
(gdb) n
1098        if (list_length(trftypes_list) > 0)
(gdb) p trftypes_list
$25 = (List *) 0x0
(gdb) n
1114            trftypes = NULL;
(gdb) 
1117        interpret_AS_clause(languageOid, language, funcname, as_clause,
(gdb) 
1125        if (procost < 0)
(gdb) p *prosrc_str
$26 = 10 '\n'
(gdb) p *probin_str
Cannot access memory at address 0x0
(gdb) p as_clause
$27 = (List *) 0x2add760
(gdb) p *as_clause
$28 = {type = T_List, length = 1, head = 0x2add738, tail = 0x2add738}
(gdb) p *as_clause->head
$29 = {data = {ptr_value = 0x2add710, int_value = 44947216, oid_value = 44947216}, next = 0x0}
(gdb) p (Node *)as_clause->head->data.ptr_value
$30 = (Node *) 0x2add710
(gdb) p (Node **)as_clause->head->data.ptr_value
$31 = (Node **) 0x2add710
(gdb) p *as_clause->head->data.ptr_value
Attempt to dereference a generic pointer.
(gdb) p (Node *)as_clause->head->data.ptr_value
$32 = (Node *) 0x2add710
(gdb) n
1128            if (languageOid == INTERNALlanguageId ||
(gdb) 
1132                procost = 100;
(gdb) 
1134        if (prorows < 0)
(gdb) 
1136            if (returnsSet)
(gdb) 
1139                prorows = 0;        /* dummy value if not returnsSet */
(gdb) 
1150        return ProcedureCreate(funcname,
(gdb) 
1160                               stmt->is_procedure ? PROKIND_PROCEDURE : (isWindowFunc ? PROKIND_WINDOW : PROKIND_FUNCTION),
(gdb) 
1150        return ProcedureCreate(funcname,
(gdb) 
1152                               stmt->replace,
(gdb) 
1150        return ProcedureCreate(funcname,
(gdb) 
1176    }
(gdb) 
ProcessUtilitySlow (pstate=0x2b02cb8, pstmt=0x2addc78, 
    queryString=0x2adbf08 "create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, 
    params=0x0, queryEnv=0x0, dest=0x2addd70, completionTag=0x7fffef099ca0 "")
    at utility.c:1479
1479                    break;
(gdb)

DONE!

四、参考资料

N/A

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2676266/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
ITPUB数据库版块资深版主,对Oracle、PostgreSQL有深入研究。

注册时间:2007-12-28

  • 博文量
    1490
  • 访问量
    3927721