In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >
Share
Shulou(Shulou.com)05/31 Report--
This article introduces the knowledge of "analyzing the ProcedureCreate function in PostgreSQL CreateFunction". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
I. data structure
Form_pg_language
Plpgsql language definition structure
/ *-* 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 is a set of variable objects and must conform to the varlena convention, that is, the first int32 of an object contains the total size of the object in bytes. * however, be sure to use the VARSIZE and SET_VARSIZE functions to scope the structure * * CAUTION: if you change the header for ordinary arrays you will also * need to change the headers for oidvector and int2vector! * / typedef struct {/ / variable header int32 vl_len_; / * varlena header (do not touch directly!) * / / Dimension int ndim / * # of dimensions * / / points to the offset of the data. 0 means there is no bitmap int32 dataoffset; / * offset to data, or 0 if no bitmap * / / element type OID Oid elemtype; / * element type OID * /} ArrayType
DefElem
Typedef struct DefElem {NodeTag type; char * defnamespace; / * NULL if unqualified name * / char * defname; Node * arg; / * a (Value *) or a (TypeName *) * / DefElemAction defaction; / * unspecified action, or SET/ADD/DROP * / int location; / * token location, or-1 if unknown * /} DefElem
FunctionParameter
Typedef enum FunctionParameterMode {/ * the assigned enum values appear in pg_proc, don't change'em! * / FUNC_PARAM_IN = 'FunctionParameterMode, / * input only * / FUNC_PARAM_OUT =' oasis, / * output only * / FUNC_PARAM_INOUT = 'baked, / * both * / FUNC_PARAM_VARIADIC =' vested, / * variadic (always input) * / FUNC_PARAM_TABLE ='t'/ * table function output column * /} FunctionParameterMode Typedef struct FunctionParameter {NodeTag type; char * name; / * parameter name, or NULL if not given * / TypeName * argType; / * TypeName for parameter type * / FunctionParameterMode mode; / * IN/OUT/etc * / Node * defexpr; / * raw default expr, or NULL if not given * /} FunctionParameter Second, source code interpretation / *-* ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc.h. * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig * are either arrays of corresponding types or NULL. These variables are of type Datum instead of ArrayType *, * to avoid introducing array.h into pg_proc.h *-- * / ObjectAddressProcedureCreate (const char * procedureName, Oid procNamespace, bool replace) Bool returnsSet, Oid returnType, Oid proowner, Oid languageObjectId, Oid languageValidator, const char * prosrc, const char * probin, char prokind, bool security_definer, bool isLeakProof, bool isStrict, char volatility, char parallel, oidvector * parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames List * parameterDefaults, Datum trftypes, Datum proconfig, Oid prosupport, float4 procost, float4 prorows) {Oid retval / / return value type int parameterCount;// input parameter int allParamCount;// all parameters, if no output parameter, 0 Oid * allParams;// all parameter types, if no output parameter, NULL char * paramModes = NULL;// parameter type bool genericInParam = false; bool genericOutParam = false; bool anyrangeInParam = false; bool internalInParam = false; bool internalOutParam = false; Oid variadicType = InvalidOid Acl * proacl = NULL;//ACL structure Relation rel;// relation HeapTuple tup;//tuple HeapTuple oldtup;// former pg_proc tuple bool nulls [natts _ pg_proc]; / / whether it is null Datum values [natts _ pg_proc]; / / value bool replaces [natts _ pg_proc]; / / whether to replace NameData procname;// name TupleDesc tupDesc;//tuple descriptor bool is_update;//? ObjectAddress myself, referenced; int iBank / temporary variable Oid trfid; / * * sanity checks * / Assert (PointerIsValid (prosrc)); / / number of parameters parameterCount = parameterTypes- > dim1; / / # define FUNC_MAX_ARGS 100 if (parameterCount)
< 0 || parameterCount >FUNC_MAX_ARGS) ereport (ERROR, (errcode (ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural ("functions cannot have more than d argument", "functions cannot have more than d arguments", FUNC_MAX_ARGS, FUNC_MAX_ARGS) / * note: the above is correct, we do NOT count output arguments (output parameters are not calculated) * / / * Deconstruct array inputs * / / reconstruct array input: all parameter types if (allParameterTypes! = PointerGetDatum (NULL)) {/ * We expect the array to be a 1murD OID array; verify that. We don't * need to use deconstruct_array () since the array data is just going * to look like a C array of OID values. * We expect the array to be an one-dimensional OID array, which needs to be verified. * because the data in the array looks like an OID array in C language, hiding does not require the use of the deconstruct_array () function * / ArrayType * allParamArray = (ArrayType *) DatumGetPointer (allParameterTypes) / / get the dimension of the array / / # define ARR_DIMS (a)\ ((int *) ((char *) (a) + sizeof (ArrayType) / / # define ARR_NDIM (a) ((a)-> ndim) / / # define ARR_HASNULL (a) ((a)-> dataoffset! = 0) allParamCount = ARR_DIMS (allParamArray) [0] If (ARR_NDIM (allParamArray)! = 1 | | allParamCount = parameterCount); / * we assume caller got the contents right * /} else {/ / are input parameters, no output parameters allParamCount = parameterCount; allParams = parameterTypes- > values;} if (parameterModes! = PointerGetDatum (NULL)) {/ / parameter mode (input / output, etc.) / * We expect the array to be a 1Muk D CHAR array; verify that. We don't * need to use deconstruct_array () since the array data is just going * to look like a C array of char values. * / ArrayType * modesArray = (ArrayType *) DatumGetPointer (parameterModes); if (ARR_NDIM (modesArray)! = 1 | | ARR_DIMS (modesArray) [0]! = allParamCount | | ARR_HASNULL (modesArray) | | ARR_ELEMTYPE (modesArray)! = CHAROID) elog (ERROR, "parameterModes is not a 1lam D char array"); paramModes = (char *) ARR_DATA_PTR (modesArray);} / * * Detect whether we have polymorphic or INTERNAL arguments. The first * loop checks input arguments, the second output arguments. * check for polymorphisms or INTERNAL parameters. * two cycles: the first to check the input parameters and the second to check the output parameters * / for (I = 0; I
< parameterCount; i++) { switch (parameterTypes->Values [I]) {case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case ANYRANGEOID: genericInParam = true; anyrangeInParam = true; break; case INTERNALOID: internalInParam = true; break;}} if (allParameterTypes! = PointerGetDatum (NULL)) {for (I = 0; I
< allParamCount; i++) { if (paramModes == NULL || paramModes[i] == PROARGMODE_IN || paramModes[i] == PROARGMODE_VARIADIC) continue; /* ignore input-only params */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case ANYRANGEOID: genericOutParam = true; anyrangeOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. ANYRANGE return type is even stricter: must have an * ANYRANGE input (since we can't deduce the specific range type from * ANYELEMENT). Also, do not allow return type INTERNAL unless at least * one input argument is INTERNAL. * 至少存在一个多态输入参数的情况下才允许返回多态类型. * ANYRANGE返回类型更为严格:必须含有一个ANYRANGE输入(因为无法从ANYELEMENT中规约特殊的范围类型.) * 同时,除非至少有一个INTERNAL输入参数类型,否则不允许返回INTERNAL类型. */ if ((IsPolymorphicType(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); if ((returnType == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); if (paramModes != NULL) { /* * Only the last input parameter can be variadic; if it is, save its * element type. Errors here are just elog since caller should have * checked this already. * 只有最后一个输入参数可以是variadic.如是,则存储元素类型. */ for (i = 0; i < allParamCount; i++) { switch (paramModes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); switch (allParams[i]) { case ANYOID: variadicType = ANYOID; break; case ANYARRAYOID: variadicType = ANYELEMENTOID; break; default: variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; } break; default: elog(ERROR, "invalid parameter mode '%c'", paramModes[i]); break; } } } /* * All seems OK; prepare the data to be inserted into pg_proc. * 检查完毕,写入到pg_proc中 */ //#define Natts_pg_proc 29 for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = false; values[i] = (Datum) 0; replaces[i] = true; } //#define Anum_pg_proc_oid 1 //#define Anum_pg_proc_proname 2 //... //#define Anum_pg_proc_proacl 29 namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost); values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows); values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType); values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(prosupport); values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = true; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = true; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = true; if (parameterDefaults != NIL) values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; if (trftypes != PointerGetDatum(NULL)) values[Anum_pg_proc_protrftypes - 1] = trftypes; else nulls[Anum_pg_proc_protrftypes - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); else nulls[Anum_pg_proc_probin - 1] = true; if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else nulls[Anum_pg_proc_proconfig - 1] = true; /* proacl will be determined later */ rel = table_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ //检查是否已存在 oldtup = SearchSysCache3(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace)); if (HeapTupleIsValid(oldtup)) { //-------- 已存在 /* There is one; okay to replace it? */ //获取原记录(pg_proc记录) Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); Datum proargnames; bool isnull; const char *dropcmd; if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(oldproc->Oid, proowner)) aclcheck_error (ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, procedureName) / * Not okay to change routine kind * / / cannot change the type (for example, it is not possible to create a func with the same name if it is originally proc) if (oldproc- > prokind! = prokind) ereport (ERROR, (errcode (ERRCODE_WRONG_OBJECT_TYPE), errmsg ("cannot change routine kind"), (oldproc- > prokind = = PROKIND_AGGREGATE? Errdetail ("\" s\ "is an aggregate function.", procedureName): oldproc- > prokind = = PROKIND_FUNCTION? Errdetail ("\" s\ "is a function.", procedureName): oldproc- > prokind = = PROKIND_PROCEDURE? Errdetail ("\" s\ "is a procedure.", procedureName): oldproc- > prokind = = PROKIND_WINDOW? Errdetail ("\"% s\ "is a window function.", procedureName): 0)); dropcmd = (prokind = = PROKIND_PROCEDURE? "DROP PROCEDURE": prokind = = PROKIND_AGGREGATE? "DROP AGGREGATE": "DROP FUNCTION"); / * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. * it is not possible to change the return type, because existing rules\ views and so on may depend on the return type. * In case of a procedure, a changing return type means that whether * the procedure has output parameters was changed. Since there is no * user visible return type, we produce a more specific error message. * in the case of a stored procedure, changing the return type means that the existing output parameters of the procedure have changed. * hiding produces more detailed error messages due to the existence of a non-user-visible return type. * / if (returnType! = oldproc- > prorettype | | returnsSet! = oldproc- > proretset) ereport (ERROR, (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION), prokind = = PROKIND_PROCEDURE? Errmsg ("cannot change whether a procedure has output parameters"): errmsg ("cannot change return type of existing function"), / * * translator: first% s is DROP FUNCTION, DROP PROCEDURE, or DROP * AGGREGATE * / errhint ("Use% s% s first.", dropcmd, format_procedure (oldproc- > oid) / * * If it returns RECORD, check for possible change of record type * implied by OUT parameters * if the RECORD type is returned, check for possible record type changes indicated by the OUT parameter. * / if (returnType = = RECORDOID) {/ / return RECORD type TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t (oldtup); newdesc = build_function_result_tupdesc_d (prokind, allParameterTypes, parameterModes, parameterNames) If (olddesc = = NULL & & newdesc = = NULL) / * ok, both are runtime-defined RECORDs * / Else if (olddesc = = NULL | | newdesc = = NULL | |! equalTupleDescs (olddesc, newdesc)) ereport (ERROR, (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg ("cannot change return type of existing function"), errdetail ("Row type defined by OUT parameters is different."), / * translator: first% s is DROP FUNCTION or DROP PROCEDURE * / errhint ("Use% s% s first." Dropcmd, format_procedure (oldproc- > oid) } / * If there were any named input parameters, check to make sure the * names have not been changed, as this could break existing calls. We * allow adding names to formerly unnamed parameters, though. * if there is a named input parameter, make sure the name does not change, otherwise it will break the existing call. * it is allowed to add names to unnamed parameters. * / proargnames = SysCacheGetAttr (PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargnames, & isnull); if (! isnull) {Datum proargmodes; char * * old_arg_names; char * * new_arg_names; int nasty new argery names; int j Proargmodes = SysCacheGetAttr (PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargmodes, & isnull); if (isnull) proargmodes = PointerGetDatum (NULL) / * just to be sure * / n_old_arg_names = get_func_input_arg_names (proargnames, proargmodes, & old_arg_names) N_new_arg_names = get_func_input_arg_names (parameterNames, parameterModes, & new_arg_names); for (j = 0; j
< n_old_arg_names; j++) { if (old_arg_names[j] == NULL) continue; if (j >= n_new_arg_names | | new_arg_ name [j] = = NULL | | strcmp (old_arg_names [j], new_arg_ name [j])! = 0) ereport (ERROR, (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg ("cannot change name of input parameter\"% s\ ", old_arg_ name [j])) / * translator: first% s is DROP FUNCTION or DROP PROCEDURE * / errhint ("Use% s s first.", dropcmd, format_procedure (oldproc- > oid) }} / * * If there are existing defaults, check compatibility: redefinition * must not remove any defaults nor change their types. (Removing a * default might cause a function to fail to satisfy an existing call. * Changing type would only be possible if the associated parameter is * polymorphic, and in such cases a change of default type might alter * the resolved output type of existing calls.) * there is defaults (default parameter). Check compatibility: * redefinition should not delete the existing default definition or change the type, otherwise it will break the existing call. * / if (oldproc- > pronargdefaults! = 0) {/ / default judgment Datum proargdefaults; List * oldDefaults; ListCell * oldlc; ListCell * newlc; if (list_length (parameterDefaults)
< oldproc->Pronargdefaults) ereport (ERROR, (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg ("cannot remove parameter defaults from existing function"), / * translator: first% s is DROP FUNCTION or DROP PROCEDURE * / errhint ("Use% s% s first.", dropcmd, format_procedure (oldproc- > oid) Proargdefaults = SysCacheGetAttr (PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, & isnull); Assert (! isnull); oldDefaults = castNode (List, stringToNode (TextDatumGetCString (proargdefaults); Assert (list_length (oldDefaults) = = oldproc- > pronargdefaults); / * new list can have more defaults than old, advance over'em * / newlc = list_head (parameterDefaults) For (I = list_length (parameterDefaults)-oldproc- > pronargdefaults; I > 0; iMurray -) newlc = lnext (newlc); foreach (oldlc, oldDefaults) {Node * oldDef = (Node *) lfirst (oldlc); Node * newDef = (Node *) lfirst (newlc) If (exprType (oldDef)! = exprType (newDef)) ereport (ERROR, (errcode (ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg ("cannot change data type of existing parameter default value"), / * translator: first% s is DROP FUNCTION or DROP PROCEDURE * / errhint ("Use% s first.", dropcmd Format_procedure (oldproc- > oid) Newlc = lnext (newlc);} / * * Do not change existing oid, ownership or permissions, either. Note * dependency-update code below has to agree with this decision. * do not change the existing oid, host or permissions, etc. * Note that the following dependency update code must follow this convention. * / replaces [Anum _ pg_proc_oid-1] = false; places [Anum _ pg_proc_proowner-1] = false; replaces [Anum _ pg_proc_proacl-1] = false; / * Okay, do it... * / tup = heap_modify_tuple (oldtup, tupDesc, values, nulls, replaces); CatalogTupleUpdate (rel, & tup- > t_self, tup); ReleaseSysCache (oldtup); is_update = true;} else {/ /-does not exist / * Creating a new procedure * / / create a new procedure Oid newOid / * First, get default permissions and set up proacl * / / first: get the default permission and set proacl proacl = get_user_default_acl (OBJECT_FUNCTION, proowner, procNamespace); if (proacl! = NULL) values [Anum _ pg_proc_proacl-1] = PointerGetDatum (proacl); else nulls [Anum _ pg_proc_proacl-1] = true / / get new OID newOid = GetNewOidWithIndex (rel, ProcedureOidIndexId, Anum_pg_proc_oid); / / set OID values in pg_proc [Anum _ pg_proc_oid-1] = ObjectIdGetDatum (newOid); / / construct tuple tup = heap_form_tuple (tupDesc, values, nulls); / / perform insert operation CatalogTupleInsert (rel, tup); is_update = false } / / get pg_proc structure retval = ((Form_pg_proc) GETSTRUCT (tup))-> oid; / * * Create dependencies for the new function. If we are updating an * existing function, first delete any existing pg_depend entries. * (However, since we are not changing ownership or permissions, the * shared dependencies do * not* need to change, and we leave them alone.) * create dependencies for new functions. * if you are updating an existing function, delete the existing pg_depend entry first. * / if (is_update) / / remove dependencies deleteDependencyRecordsFor (ProcedureRelationId, retval, true); myself.classId = ProcedureRelationId; myself.objectId = retval; myself.objectSubId = 0; / * dependency on namespace * / / dependency: namespace referenced.classId = NamespaceRelationId; referenced.objectId = procNamespace; referenced.objectSubId = 0; recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); / * dependency on implementation language * / / dependency: language referenced.classId = LanguageRelationId; referenced.objectId = languageObjectId Referenced.objectSubId = 0; recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); / * dependency on return type * / / dependency: return type referenced.classId = TypeRelationId; referenced.objectId = returnType; referenced.objectSubId = 0; recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL) / * dependency on transform used by return type, if any * / / dependency: conversion rules of return type if ((trfid = get_transform_oid (returnType, languageObjectId, true)) {referenced.classId = TransformRelationId; referenced.objectId = trfid; referenced.objectSubId = 0; recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL);} / * dependency on parameter types * / / dependency: parameter type for (I = 0; I
< allParamCount; i++) { referenced.classId = TypeRelationId; referenced.objectId = allParams[i]; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on transform used by parameter type, if any */ if ((trfid = get_transform_oid(allParams[i], languageObjectId, true))) { referenced.classId = TransformRelationId; referenced.objectId = trfid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } } /* dependency on parameter default expressions */ //依赖:默认表达式 if (parameterDefaults) recordDependencyOnExpr(&myself, (Node *) parameterDefaults, NIL, DEPENDENCY_NORMAL); /* dependency on support function, if any */ //依赖:支持的函数 if (OidIsValid(prosupport)) { referenced.classId = ProcedureRelationId; referenced.objectId = prosupport; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ //依赖:owner if (!is_update) recordDependencyOnOwner(ProcedureRelationId, retval, proowner); /* dependency on any roles mentioned in ACL */ //依赖:ACL中标明的角色 if (!is_update) recordDependencyOnNewAcl(ProcedureRelationId, retval, 0, proowner, proacl); /* dependency on extension */ //依赖:扩展 recordDependencyOnCurrentExtension(&myself, is_update); heap_freetuple(tup); /* Post creation hook for new function */ //调用对象创建后的钩子函数 InvokeObjectPostCreateHook(ProcedureRelationId, retval, 0); //关闭pg_proc table_close(rel, RowExclusiveLock); /* Verify function body */ //验证函数body if (OidIsValid(languageValidator)) { ArrayType *set_items = NULL; int save_nestlevel = 0; /* Advance command counter so new tuple can be seen by validator */ //增加命令计数 CommandCounterIncrement(); /* * Set per-function configuration parameters so that the validation is * done with the environment the function expects. However, if * check_function_bodies is off, we don't do this, because that would * create dump ordering hazards that pg_dump doesn't know how to deal * with. (For example, a SET clause might refer to a not-yet-created * text search configuration.) This means that the validator * shouldn't complain about anything that might depend on a GUC * parameter when check_function_bodies is off. */ if (check_function_bodies) { //检查函数体 //获取函数设定的参数 set_items = (ArrayType *) DatumGetPointer(proconfig); if (set_items) /* Need a new GUC nesting level */ { save_nestlevel = NewGUCNestLevel(); ProcessGUCArray(set_items, (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION, GUC_ACTION_SAVE); } } //调用语言校验器 OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval)); if (set_items) AtEOXact_GUC(true, save_nestlevel); } return myself;}三、跟踪分析 测试脚本 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$$declarebegin 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 ProcedureCreateBreakpoint 1 at 0x5bd665: file pg_proc.c, line 99.(gdb) cContinuing.Breakpoint 1, ProcedureCreate (procedureName=0x1173ab0 "func_test", procNamespace=2200, replace=true, returnsSet=false, returnType=2249, proowner=10, languageObjectId=13581, languageValidator=13580, prosrc=0x11745c8 "\ndeclare\nbegin\n raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;\n pio_v3 := 'pio_v3 i/o';\n po_v4 := 100;\n po_v5 := 'po_v5 out';\nend;\n", probin=0x0, prokind=102 'f', security_definer=false, isLeakProof=false, isStrict=false, volatility=118 'v', parallel=117 'u', parameterTypes=0x119a3d0, allParameterTypes=18458616, parameterModes=18457432, parameterNames=18456792, parameterDefaults=0x0, trftypes=0, proconfig=0, prosupport=0, procost=100, prorows=0) at pg_proc.c:9999 char *paramModes = NULL;(gdb) 输入参数 [local:/data/run/pg12]:5120 pg12@testdb=# \d pg_type Table "pg_catalog.pg_type" Column | Type | Collation | Nullable | Default ----------------+--------------+-----------+----------+--------- oid | oid | | not null | typname | name | | not null | typnamespace | oid | | not null | typowner | oid | | not null | typlen | smallint | | not null | typbyval | boolean | | not null | typtype | "char" | | not null | typcategory | "char" | | not null | typispreferred | boolean | | not null | typisdefined | boolean | | not null | typdelim | "char" | | not null | typrelid | oid | | not null | typelem | oid | | not null | typarray | oid | | not null | typinput | regproc | | not null | typoutput | regproc | | not null | typreceive | regproc | | not null | [local:/data/run/pg12]:5120 pg12@testdb=# select oid,typname from pg_type where oid=2249; oid | typname ------+--------- 2249 | record(1 row)[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_namespace Table "pg_catalog.pg_namespace" Column | Type | Collation | Nullable | Default ----------+-----------+-----------+----------+--------- oid | oid | | not null | nspname | name | | not null | nspowner | oid | | not null | nspacl | aclitem[] | | | Indexes: "pg_namespace_nspname_index" UNIQUE, btree (nspname) "pg_namespace_oid_index" UNIQUE, btree (oid)[local:/data/run/pg12]:5120 pg12@testdb=# select oid,nspname from pg_namespace where oid=2200; oid | nspname ------+--------- 2200 | public(1 row)[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_user View "pg_catalog.pg_user" Column | Type | Collation | Nullable | Default --------------+--------------------------+-----------+----------+--------- usename | name | | | usesysid | oid | | | usecreatedb | boolean | | | usesuper | boolean | | | userepl | boolean | | | usebypassrls | boolean | | | passwd | text | | | valuntil | timestamp with time zone | | | useconfig | text[] | C | | [local:/data/run/pg12]:5120 pg12@testdb=# select usename,usesysid from pg_user where usesysid=10; usename | usesysid ---------+---------- pg12 | 10(1 row)[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_language Table "pg_catalog.pg_language" Column | Type | Collation | Nullable | Default ---------------+-----------+-----------+----------+--------- oid | oid | | not null | lanname | name | | not null | lanowner | oid | | not null | lanispl | boolean | | not null | lanpltrusted | boolean | | not null | lanplcallfoid | oid | | not null | laninline | oid | | not null | lanvalidator | oid | | not null | lanacl | aclitem[] | | | Indexes: "pg_language_name_index" UNIQUE, btree (lanname) "pg_language_oid_index" UNIQUE, btree (oid)[local:/data/run/pg12]:5120 pg12@testdb=# select oid,lanname,lanowner from pg_language where oid=13581; oid | lanname | lanowner -------+---------+---------- 13581 | plpgsql | 10(1 row) 初始化本地临时变量 99 char *paramModes = NULL;(gdb) n100 bool genericInParam = false;(gdb) 101 bool genericOutParam = false;(gdb) 102 bool anyrangeInParam = false;(gdb) 103 bool anyrangeOutParam = false;(gdb) 104 bool internalInParam = false;(gdb) 105 bool internalOutParam = false;(gdb) 106 Oid variadicType = InvalidOid;(gdb) 107 Acl *proacl = NULL;(gdb) 125 Assert(PointerIsValid(prosrc));(gdb) 127 parameterCount = parameterTypes->Dim1; (gdb)
Get the number of parameters (3 input parameters, type 26-oid, data type int4,varchar,varchar)
(gdb) n128 if (parameterCount
< 0 || parameterCount >FUNC_MAX_ARGS) (gdb) p parameterCount$1 = 3 (gdb) p * parameterTypes$2 = {vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0, values = 0x119a3e8} (gdb) (gdb) p * parameterTypes- > values$3 = 23 (gdb) p parameterTypes- > values [1] $4 = 1043 (gdb) p parameterTypes- > values [2] $5 = 1043 (gdb) # # [local:/data/run/pg12]: 5120 pg12@testdb=# select oid,typname from pg_type where oid=26 Oid | typname-+-26 | oid (1 row) [local:/data/run/pg12]: 5120 pg12@testdb=# select oid,typname from pg_type where oid in (23 rows 1043); oid | typname-+-23 | int4 1043 | varchar (2 rows)
Refactoring array input: all parameter types
(gdb) n138 if (allParameterTypes! = PointerGetDatum (NULL)) (gdb) 145 ArrayType * allParamArray = (ArrayType *) DatumGetPointer (allParameterTypes); (gdb) 147 allParamCount = ARR_DIMS (allParamArray) [0]; (gdb) 148if (ARR_NDIM (allParamArray)! = 1 | (gdb) 150 ARR_HASNULL (allParamArray) | | (gdb) 149allParamCount = parameterCount) (gdb) p * allParamArray$6 = {vl_len_ = 176, ndim = 1, dataoffset = 0, elemtype = 26} (gdb) p allParamArray [1] $7 = {vl_len_ = 5, ndim = 1, dataoffset = 23, elemtype = 1043} (gdb) p allParamCount$8 = 5 (gdb) p * ARR_DIMS (allParamArray) $9 = 5 (gdb) p allParamArray [2] $10 = {vl_len_ = 1043, ndim = 23, dataoffset = 1043, elemtype = 2139062142} p allParamArray [3] $11 = {vl_len_ = 2139062143, ndim = 2139062143 Dataoffset = 2139062143, elemtype = 2139062143} (gdb) n163 if (parameterModes! = PointerGetDatum (NULL)) (gdb)
Processing parameter mode: parameter mode (input / output, etc.)
(gdb) N170 ArrayType * modesArray = (ArrayType *) DatumGetPointer (parameterModes) (gdb) 172if (ARR_NDIM (modesArray)! = 1 | | (gdb) 173ARR_DIMS (modesArray) [0]! = allParamCount | | (gdb) 172if (ARR_NDIM (modesArray)! = 1 | | (gdb) 174ARR_HASNULL (modesArray) | | (gdb) 173ARR_DIMS (modesArray) [0]! = allParamCount | | (gdb) 175ARR_ELEMTYPE (modesArray)! = CHAROID) (gdb) 174ARR _ HASNULL (modesArray) | | (gdb) 177paramModes = (char *) ARR_DATA_PTR (modesArray) (gdb) 184 for (I = 0; I)
< parameterCount; i++)(gdb) p paramModes[0]$12 = 105 'i'(gdb) p paramModes[1]$13 = 105 'i'(gdb) p paramModes[2]$14 = 98 'b'(gdb) p paramModes[3]$15 = 111 'o'(gdb) p paramModes[4]$16 = 111 'o'(gdb) 检查是否存在多态或者INTERNAL参数. 两趟循环:第一趟检查输入参数,第二趟检查输出参数 (gdb) n186 switch (parameterTypes->Values [I] (gdb) 184 for (I = 0; I)
< parameterCount; i++)(gdb) 186 switch (parameterTypes->Values [I] (gdb) 184 for (I = 0; I)
< parameterCount; i++)(gdb) 186 switch (parameterTypes->Values [I] (gdb) 184 for (I = 0; I)
< parameterCount; i++)(gdb) 204 if (allParameterTypes != PointerGetDatum(NULL))(gdb) 206 for (i = 0; i < allParamCount; i++)(gdb) 208 if (paramModes == NULL ||(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 208 if (paramModes == NULL ||(gdb) 211 continue; /* ignore input-only params */(gdb) 206 for (i = 0; i < allParamCount; i++)(gdb) 208 if (paramModes == NULL ||(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 208 if (paramModes == NULL ||(gdb) 211 continue; /* ignore input-only params */(gdb) 206 for (i = 0; i < allParamCount; i++)(gdb) 208 if (paramModes == NULL ||(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 208 if (paramModes == NULL ||(gdb) 210 paramModes[i] == PROARGMODE_VARIADIC)(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 213 switch (allParams[i])(gdb) 206 for (i = 0; i < allParamCount; i++)(gdb) 208 if (paramModes == NULL ||(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 208 if (paramModes == NULL ||(gdb) 210 paramModes[i] == PROARGMODE_VARIADIC)(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 213 switch (allParams[i])(gdb) 206 for (i = 0; i < allParamCount; i++)(gdb) 208 if (paramModes == NULL ||(gdb) p allParamCount$17 = 5(gdb) n209 paramModes[i] == PROARGMODE_IN ||(gdb) 208 if (paramModes == NULL ||(gdb) 210 paramModes[i] == PROARGMODE_VARIADIC)(gdb) 209 paramModes[i] == PROARGMODE_IN ||(gdb) 213 switch (allParams[i])(gdb) 206 for (i = 0; i < allParamCount; i++)(gdb) 239 if ((IsPolymorphicType(returnType) || genericOutParam)(gdb) 至少存在一个多态输入参数的情况下才允许返回多态类型. ANYRANGE返回类型更为严格:必须含有一个ANYRANGE输入(因为无法从ANYELEMENT中规约特殊的范围类型.) 同时,除非至少有一个INTERNAL输入参数类型,否则不允许返回INTERNAL类型. (gdb) n246 if ((returnType == ANYRANGEOID || anyrangeOutParam) &&(gdb) 253 if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)(gdb) 259 if (paramModes != NULL)(gdb) 只有最后一个输入参数可以是variadic.如是,则存储元素类型. 266 for (i = 0; i < allParamCount; i++)(gdb) n268 switch (paramModes[i])(gdb) 272 if (OidIsValid(variadicType))(gdb) 274 break;(gdb) 266 for (i = 0; i < allParamCount; i++)(gdb) 268 switch (paramModes[i])(gdb) 272 if (OidIsValid(variadicType))(gdb) 274 break;(gdb) 266 for (i = 0; i < allParamCount; i++)(gdb) 268 switch (paramModes[i])(gdb) 272 if (OidIsValid(variadicType))(gdb) 274 break;(gdb) 266 for (i = 0; i < allParamCount; i++)(gdb) 268 switch (paramModes[i])(gdb) 278 break;(gdb) 266 for (i = 0; i < allParamCount; i++)(gdb) 268 switch (paramModes[i])(gdb) 278 break;(gdb) 266 for (i = 0; i < allParamCount; i++)(gdb) 308 for (i = 0; i < Natts_pg_proc; ++i)(gdb) 检查完毕,写入到pg_proc中,初始化values 308 for (i = 0; i < Natts_pg_proc; ++i)(gdb) 310 nulls[i] = false;(gdb) 311 values[i] = (Datum) 0;(gdb) 312 replaces[i] = true;(gdb) ... 赋值 308 for (i = 0; i < Natts_pg_proc; ++i)(gdb) 315 namestrcpy(&procname, procedureName);(gdb) 316 values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);(gdb) p procedureName$20 = 0x1173ab0 "func_test"(gdb) n317 values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);(gdb) 318 values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);(gdb) 319 values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);(gdb) 320 values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);(gdb) 321 values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);(gdb) 322 values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);(gdb) 323 values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(prosupport);(gdb) 324 values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);(gdb) 325 values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);(gdb) 326 values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);(gdb) 327 values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);(gdb) 328 values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);(gdb) 329 values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);(gdb) 330 values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel);(gdb) 331 values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);(gdb) 332 values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));(gdb) 333 values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);(gdb) 334 values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);(gdb) 335 if (allParameterTypes != PointerGetDatum(NULL))(gdb) 336 values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;(gdb) 339 if (parameterModes != PointerGetDatum(NULL))(gdb) 340 values[Anum_pg_proc_proargmodes - 1] = parameterModes;(gdb) 343 if (parameterNames != PointerGetDatum(NULL))(gdb) 344 values[Anum_pg_proc_proargnames - 1] = parameterNames;(gdb) 347 if (parameterDefaults != NIL)(gdb) 350 nulls[Anum_pg_proc_proargdefaults - 1] = true;(gdb) 351 if (trftypes != PointerGetDatum(NULL))(gdb) 354 nulls[Anum_pg_proc_protrftypes - 1] = true;(gdb) 355 values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);(gdb) 356 if (probin)(gdb) 359 nulls[Anum_pg_proc_probin - 1] = true;(gdb) 360 if (proconfig != PointerGetDatum(NULL))(gdb) 363 nulls[Anum_pg_proc_proconfig - 1] = true;(gdb) 366 rel = table_open(ProcedureRelationId, RowExclusiveLock);(gdb) 367 tupDesc = RelationGetDescr(rel);(gdb) 判断是否已存在 (gdb) p values[28]$21 = 0(gdb) p values[0]$22 = 0(gdb) p values[2]$23 = 2200(gdb) n370 oldtup = SearchSysCache3(PROCNAMEARGSNSP,(gdb) p *tupDesc$24 = {natts = 29, tdtypeid = 81, tdtypmod = -1, tdrefcount = 1, constr = 0x7fbeb44493b8, attrs = 0x7fbeb44483b8}(gdb) n375 if (HeapTupleIsValid(oldtup))(gdb) 378 Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);(gdb) 383 if (!replace)(gdb) p oldproc$25 = (Form_pg_proc) 0x7fbeb4388bf8(gdb) p *oldproc$26 = {oid = 16387, proname = {data = "func_test", '\000' }, pronamespace = 2200, proowner = 10, prolang = 13581, procost = 100, prorows = 0, provariadic = 0, prosupport = 0, prokind = 102 'f', prosecdef = false, proleakproof = false, proisstrict = false, proretset = false, provolatile = 118 'v', proparallel = 117 'u', pronargs = 3, pronargdefaults = 0, prorettype = 2249, proargtypes = { vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0, values = 0x7fbeb4388c80}}(gdb) 执行相关判断:如返回类型,参数类型,参数个数等 (gdb) n388 if (!pg_proc_ownercheck(oldproc->Oid, proowner) (gdb) 393if (oldproc- > prokind! = prokind) (gdb) 407dropcmd = (prokind = = PROKIND_PROCEDURE? "DROP PROCEDURE": (gdb) 419 if (returnType! = oldproc- > prorettype | | (gdb) 420 returnsSet! = oldproc- > proretset) (gdb) 419 if (returnType! = oldproc- > prorettype | | (gdb) 439 if (returnType = = RECORDOID) (gdb) 444 olddesc = build_function_result_tupdesc_t (oldtup) (gdb) 445 newdesc = build_function_result_tupdesc_d (prokind, (gdb) 449 if (olddesc = = NULL & & newdesc = = NULL) (gdb) 451 else if (olddesc = = NULL | | newdesc = = NULL | | (gdb) 452! equalTupleDescs (olddesc, newdesc)) (gdb) 451 else if (olddesc = = NULL | | newdesc = = NULL | | (gdb) 468 proargnames = SysCacheGetAttr (PROCNAMEARGSNSP, oldtup, (gdb) 471 if (! isnull) (gdb) 480 proargmodes = SysCacheGetAttr Oldtup, (gdb) 483if (isnull) (gdb) 486n_old_arg_names = get_func_input_arg_names (proargnames, (gdb) 489 n_new_arg_names = get_func_input_arg_names (parameterNames, (gdb) 492for (j = 0) J
< n_old_arg_names; j++)(gdb) 494 if (old_arg_names[j] == NULL)(gdb) 496 if (j >= n_new_arg_names | | new_arg_ namesake [j] = NULL | | (gdb) 497new_arg_ (old_arg_names [j], new_arg_ namespace [j])! = 0) (gdb) 496if (j > = n_new_arg_names | | new_arg_ namespace [j] = = NULL | | (gdb) 492Namename (j = 0; j)
< n_old_arg_names; j++)(gdb) 494 if (old_arg_names[j] == NULL)(gdb) 496 if (j >= n_new_arg_names | | new_arg_ namesake [j] = NULL | | (gdb) 497new_arg_ (old_arg_names [j], new_arg_ namespace [j])! = 0) (gdb) 496if (j > = n_new_arg_names | | new_arg_ namespace [j] = = NULL | | (gdb) 492Namename (j = 0; j)
< n_old_arg_names; j++)(gdb) 494 if (old_arg_names[j] == NULL)(gdb) 496 if (j >= n_new_arg_names | | new_arg_ namesake [j] = NULL | | (gdb) 497new_arg_ (old_arg_names [j], new_arg_ namespace [j])! = 0) (gdb) 496if (j > = n_new_arg_names | | new_arg_ namespace [j] = = NULL | | (gdb) 492Namename (j = 0; j)
< n_old_arg_names; j++)(gdb) 517 if (oldproc->Pronargdefaults! = 0) (gdb) 568 replaces [Anum _ pg_proc_oid-1] = false; (gdb) 569 replaces [Anum _ pg_proc_proowner-1] = false; (gdb) 570 replaces [Anum _ pg_proc_proacl-1] = false; (gdb) 573 tup = heap_modify_tuple (oldtup, tupDesc, values, nulls, replaces); (gdb) 574 CatalogTupleUpdate (rel, & tup- > t_self, tup); (gdb)
Update tuple
(gdb) n576 ReleaseSysCache (oldtup); (gdb) 577 is_update = true; (gdb) 601 retval = ((Form_pg_proc) GETSTRUCT (tup))-> oid; (gdb)
Dealing with functional dependencies
(gdb) p retval$27 = 16387 (gdb) n610 deleteDependencyRecordsFor (ProcedureRelationId, retval, true); (gdb) 612 myself.classId = ProcedureRelationId; (gdb) 613 myself.objectId = retval; (gdb) 614 myself.objectSubId = 0; (gdb) 617 referenced.classId = NamespaceRelationId; (gdb) 618 referenced.objectId = procNamespace; (gdb) 619 referenced.objectSubId = 0; (gdb) 620 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); (gdb) 623 referenced.classId = LanguageRelationId; (gdb) 624 referenced.objectId = languageObjectId (gdb) 625 referenced.objectSubId = 0; (gdb) 626 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); (gdb) 629 referenced.classId = TypeRelationId; (gdb) 630 referenced.objectId = returnType; (gdb) 631 referenced.objectSubId = 0; (gdb) 632 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); (gdb) 635 if ((trfid = get_transform_oid (returnType, languageObjectId, true)) (gdb) 644 for (I = 0; I < allParamCount) (gdb) 646 referenced.classId = TypeRelationId; (gdb) 647 referenced.objectId = allParams [I]; (gdb) 648 referenced.objectSubId = 0; (gdb) 649 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); (gdb) 652 if ((trfid = get_transform_oid (allParams [I], languageObjectId, true)) (gdb) 644 for (I = 0; I < allParamCount; ionization +) (gdb) 646 referenced.classId = TypeRelationId (gdb) 647 referenced.objectId = allParams [I]; (gdb) 648 referenced.objectSubId = 0; (gdb) 649 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); (gdb) 652 if ((trfid = get_transform_oid (allParams [I], languageObjectId, true)) (gdb) 644 for (I = 0; I < allParamCount; ionization +) (gdb) 646 referenced.classId = TypeRelationId; (gdb) 647 referenced.objectId = allParams [I]; (gdb) 648 referenced.objectSubId = 0 (gdb) 649 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL); (gdb) 652 if ((trfid = get_transform_oid (allParams [I], languageObjectId, true) (gdb) 644 for (I = 0; I < allParamCount; ionization +) (gdb) 646 referenced.classId = TypeRelationId; (gdb) 647 referenced.objectId = allParams [I]; (gdb) 648 referenced.objectSubId = 0; (gdb) 649 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL) (gdb) 652 if ((trfid = get_transform_oid (allParams [I], languageObjectId, true)) (gdb) 644 for (I = 0; I < allParamCount; iTunes +) (gdb) 646 referenced.classId = TypeRelationId; (gdb) 647 referenced.objectId = allParams [I]; (gdb) 648 referenced.objectSubId = 0; (gdb) 649 recordDependencyOn (& myself, & referenced, DEPENDENCY_NORMAL) (gdb) 652 if ((trfid = get_transform_oid (allParams [I], languageObjectId, true)) (gdb) 644 for (I = 0; I < allParamCount; ionization +) (gdb) 662 if (parameterDefaults) (gdb) 667 if (OidIsValid (prosupport)) (gdb) 676 if (! is_update) (gdb) 680 if (! is_update) (gdb) 685 recordDependencyOnCurrentExtension (& myself, is_update); (gdb) 687 heap_freetuple (tup); (gdb) 690 InvokeObjectPostCreateHook (ProcedureRelationId, retval, 0) (gdb) 692table_close (rel, RowExclusiveLock); (gdb)
Perform function body verification
695 if (OidIsValid (languageValidator)) (gdb) 697 ArrayType * set_items = NULL; (gdb) 698 int save_nestlevel = 0; (gdb) 701 CommandCounterIncrement (); (gdb) 713 if (check_function_bodies) (gdb) (gdb) n715 set_items = (ArrayType *) DatumGetPointer (proconfig); (gdb) 716 if (set_items) / * Need a new GUC nesting level * / (gdb) 726 OidFunctionCall1 (languageValidator, ObjectIdGetDatum (retval)) (gdb) 728 if (set_items) (gdb) 732 return myself; (gdb) 733} (gdb)
Complete the call
(gdb) 733} (gdb) CreateFunction (pstate=0x1199c88, stmt=0x11748c8) at functioncmds.c:11761176} (gdb) "analyzing ProcedureCreate functions in PostgreSQL CreateFunction" ends here. Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 257
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.