分析PostgreSQL创建函数的过程
发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,本篇内容主要讲解"分析PostgreSQL创建函数的过程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"分析PostgreSQL创建函数的过程"吧!一、数据
千家信息网最后更新 2025年02月01日分析PostgreSQL创建函数的过程
本篇内容主要讲解"分析PostgreSQL创建函数的过程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"分析PostgreSQL创建函数的过程"吧!
一、数据结构
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;
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 = 'i', /* input only */ FUNC_PARAM_OUT = 'o', /* output only */ FUNC_PARAM_INOUT = 'b', /* both */ FUNC_PARAM_VARIADIC = 'v', /* 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;
CatCache
/* function computing a datum's hash */typedef uint32 (*CCHashFN) (Datum datum);/* function computing equality of two datums */typedef bool (*CCFastEqualFN) (Datum a, Datum b);typedef struct catcache{ //cache ID int id; /* cache identifier --- see syscache.h */ //cache的hash槽 int cc_nbuckets; /* # of hash buckets in this cache */ //元组描述符 TupleDesc cc_tupdesc; /* tuple descriptor (copied from reldesc) */ //hash桶 dlist_head *cc_bucket; /* hash buckets */ //每个key的hash函数 CCHashFN cc_hashfunc[CATCACHE_MAXKEYS]; /* hash function for each key */ //每个key的快速等值函数 CCFastEqualFN cc_fastequal[CATCACHE_MAXKEYS]; /* fast equal function for * each key */ //每个key的属性编号 int cc_keyno[CATCACHE_MAXKEYS]; /* AttrNumber of each key */ //CatCList结构体链表 dlist_head cc_lists; /* list of CatCList structs */ //cache中元组数 int cc_ntup; /* # of tuples currently in this cache */ //keys数 int cc_nkeys; /* # of keys (1..CATCACHE_MAXKEYS) */ //cache元组相关的relation const char *cc_relname; /* name of relation the tuples come from */ //relation OID Oid cc_reloid; /* OID of relation the tuples come from */ //匹配缓存keys的索引OID Oid cc_indexoid; /* OID of index matching cache keys */ //是否可跨库共享? bool cc_relisshared; /* is relation shared across databases? */ //链表链接 slist_node cc_next; /* list link */ //用于heap扫描的预计算key信息 ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap * scans */ /* * Keep these at the end, so that compiling catcache.c with CATCACHE_STATS * doesn't break ABI for other modules * 这些项放在最后面,以便使用CATCACHE_STATS选项编译catcache.c时不需要终止其他模块的ABI */#ifdef CATCACHE_STATS //检索次数 long cc_searches; /* total # searches against this cache */ //匹配次数 long cc_hits; /* # of matches against existing entry */ //未命中次数 long cc_neg_hits; /* # of matches against negative entry */ //未命中成功加载次数 long cc_newloads; /* # of successful loads of new entry */ /* * cc_searches - (cc_hits + cc_neg_hits + cc_newloads) is number of failed * searches, each of which will result in loading a negative entry\\ * cc_searches - (cc_hits + cc_neg_hits + cc_newloads)是cache检索失败次数 */ //验证失效次数 long cc_invals; /* # of entries invalidated from cache */ //链表检索次数 long cc_lsearches; /* total # list-searches */ // long cc_lhits; /* # of matches against existing lists */#endif} CatCache;
二、源码解读
HeapTupleSearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3){ //执行检查 Assert(cacheId >= 0 && cacheId < SysCacheSize && PointerIsValid(SysCache[cacheId])); Assert(SysCache[cacheId]->cc_nkeys == 3); //直接调用SearchCatCache3 return SearchCatCache3(SysCache[cacheId], key1, key2, key3);}HeapTupleSearchCatCache3(CatCache *cache, Datum v1, Datum v2, Datum v3){ //直接调用SearchCatCacheInternal return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);}/* * Work-horse for SearchCatCache/SearchCatCacheN. * 通用函数:SearchCatCache/SearchCatCacheN调用 */static inline HeapTupleSearchCatCacheInternal(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4){ //#define CATCACHE_MAXKEYS 4 Datum arguments[CATCACHE_MAXKEYS]; uint32 hashValue; Index hashIndex; dlist_iter iter; dlist_head *bucket; CatCTup *ct; /* Make sure we're in an xact, even if this ends up being a cache hit */ //确保处于事务中 Assert(IsTransactionState()); Assert(cache->cc_nkeys == nkeys); /* * one-time startup overhead for each cache */ if (unlikely(cache->cc_tupdesc == NULL)) CatalogCacheInitializeCache(cache);#ifdef CATCACHE_STATS cache->cc_searches++;#endif /* Initialize local parameter array */ //初始化本地参数数组 arguments[0] = v1; arguments[1] = v2; arguments[2] = v3; arguments[3] = v4; /* * find the hash bucket in which to look for the tuple * 检索hash桶 */ hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4); hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); /* * scan the hash bucket until we find a match or exhaust our tuples * 扫描hash桶 * * Note: it's okay to use dlist_foreach here, even though we modify the * dlist within the loop, because we don't continue the loop afterwards. * 就算在循环过程中更新了dlist也可以用dlist_foreach,因为不再往后循环了 */ bucket = &cache->cc_bucket[hashIndex]; dlist_foreach(iter, bucket) { ct = dlist_container(CatCTup, cache_elem, iter.cur); if (ct->dead)//忽略已废弃的条目 continue; /* ignore dead entries */ if (ct->hash_value != hashValue)//跳过hash不同的项 continue; /* quickly skip entry if wrong hash val */ //不同的元组,跳过 if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments)) continue; /* * We found a match in the cache. Move it to the front of the list * for its hashbucket, in order to speed subsequent searches. (The * most frequently accessed elements in any hashbucket will tend to be * near the front of the hashbucket's list.) * 命中,放在链表的头部 */ dlist_move_head(bucket, &ct->cache_elem); /* * If it's a positive entry, bump its refcount and return it. If it's * negative, we can report failure to the caller. * 正向项,增加refcount并返回 */ if (!ct->negative) { ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d", cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS cache->cc_hits++;#endif return &ct->tuple; } else { CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d", cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS cache->cc_neg_hits++;#endif return NULL; } } return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);}/* * Search the actual catalogs, rather than the cache. * * This is kept separate from SearchCatCacheInternal() to keep the fast-path * as small as possible. To avoid that effort being undone by a helpful * compiler, try to explicitly forbid inlining. */static pg_noinline HeapTupleSearchCatCacheMiss(CatCache *cache, int nkeys, uint32 hashValue, Index hashIndex, Datum v1, Datum v2, Datum v3, Datum v4){ ScanKeyData cur_skey[CATCACHE_MAXKEYS]; Relation relation; SysScanDesc scandesc; HeapTuple ntp; CatCTup *ct; Datum arguments[CATCACHE_MAXKEYS]; /* Initialize local parameter array */ arguments[0] = v1; arguments[1] = v2; arguments[2] = v3; arguments[3] = v4; /* * Ok, need to make a lookup in the relation, copy the scankey and fill * out any per-call fields. */ memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys); cur_skey[0].sk_argument = v1; cur_skey[1].sk_argument = v2; cur_skey[2].sk_argument = v3; cur_skey[3].sk_argument = v4; /* * Tuple was not found in cache, so we have to try to retrieve it directly * from the relation. If found, we will add it to the cache; if not * found, we will add a negative cache entry instead. * * NOTE: it is possible for recursive cache lookups to occur while reading * the relation --- for example, due to shared-cache-inval messages being * processed during table_open(). This is OK. It's even possible for one * of those lookups to find and enter the very same tuple we are trying to * fetch here. If that happens, we will enter a second copy of the tuple * into the cache. The first copy will never be referenced again, and * will eventually age out of the cache, so there's no functional problem. * This case is rare enough that it's not worth expending extra cycles to * detect. */ relation = table_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, cache->cc_indexoid, IndexScanOK(cache, cur_skey), NULL, nkeys, cur_skey); ct = NULL; while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { ct = CatalogCacheCreateEntry(cache, ntp, arguments, hashValue, hashIndex, false); /* immediately set the refcount to 1 */ ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); break; /* assume only one match */ } systable_endscan(scandesc); table_close(relation, AccessShareLock); /* * If tuple was not found, we need to build a negative cache entry * containing a fake tuple. The fake tuple has the correct key columns, * but nulls everywhere else. * * In bootstrap mode, we don't build negative entries, because the cache * invalidation mechanism isn't alive and can't clear them if the tuple * gets created later. (Bootstrap doesn't do UPDATEs, so it doesn't need * cache inval for that.) */ if (ct == NULL) { if (IsBootstrapProcessingMode()) return NULL; ct = CatalogCacheCreateEntry(cache, NULL, arguments, hashValue, hashIndex, true); CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup); CACHE_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d", cache->cc_relname, hashIndex); /* * We are not returning the negative entry to the caller, so leave its * refcount zero. */ return NULL; } CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup); CACHE_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d", cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS cache->cc_newloads++;#endif return &ct->tuple;}
三、跟踪分析
测试脚本
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,proname,pronargs,proargtypes,proallargtypes from pg_proc where proname = 'func_test'; oid | proname | pronargs | proargtypes | proallargtypes -------+-----------+----------+--------------+------------------------ 16387 | func_test | 3 | 23 1043 1043 | {23,1043,1043,23,1043}(1 row)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 pg_proc.c:367Breakpoint 1 at 0x5be038: file pg_proc.c, line 367.(gdb) (gdb) cContinuing.Breakpoint 1, ProcedureCreate (procedureName=0x15e3ab0 "func_test", procNamespace=2200, replace=true, returnsSet=false, returnType=2249, proowner=10, languageObjectId=13581, languageValidator=13580, prosrc=0x15e45c8 "\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=0x16ce398, allParameterTypes=23913456, parameterModes=23913544, parameterNames=23913600, parameterDefaults=0x0, trftypes=0, proconfig=0, prosupport=0, procost=100, prorows=0) at pg_proc.c:367367 tupDesc = RelationGetDescr(rel);(gdb)
进入SearchSysCache3
(gdb) n370 oldtup = SearchSysCache3(PROCNAMEARGSNSP,(gdb) stepSearchSysCache3 (cacheId=42, key1=22952624, key2=23913368, key3=2200) at syscache.c:11491149 Assert(cacheId >= 0 && cacheId < SysCacheSize &&(gdb)
输入参数:cacheId=42, key1=22952624, key2=23913368, key3=2200
其中key1->procedureName,key2->parameterTypes,key3->procNamespace
输入参数类型有3个,分别是23/1043/1043(inout参数)
(gdb) p (char *)22952624$3 = 0x15e3ab0 "func_test"(gdb) p ((oidvector *)23913368)[0]$4 = {vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0, values = 0x16ce3b0}(gdb) p ((oidvector *)23913368)[0]->values$7 = 0x16ce3b0(gdb) p *((oidvector *)23913368)[0]->values$8 = 23(gdb) p ((oidvector *)23913368)[0]->values[0]$9 = 23(gdb) p ((oidvector *)23913368)[0]->values[1]$10 = 1043(gdb) p ((oidvector *)23913368)[0]->values[2]$11 = 1043(gdb)
进入SearchCatCache3
(gdb) n1151 Assert(SysCache[cacheId]->cc_nkeys == 3);(gdb) n1153 return SearchCatCache3(SysCache[cacheId], key1, key2, key3);(gdb) stepSearchCatCache3 (cache=0x1639c80, v1=22952624, v2=23913368, v3=2200) at catcache.c:11831183 return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);(gdb)
进入SearchCatCacheInternal
(gdb) stepSearchCatCacheInternal (cache=0x1639c80, nkeys=3, v1=22952624, v2=23913368, v3=2200, v4=0) at catcache.c:12131213 Assert(IsTransactionState());(gdb)
cache信息
(gdb) n1215 Assert(cache->cc_nkeys == nkeys);(gdb) p *cache$13 = {id = 42, cc_nbuckets = 128, cc_tupdesc = 0x7f8159216ef8, cc_bucket = 0x163a160, cc_hashfunc = {0xa48129, 0xa48257 , 0xa481b0 , 0x0}, cc_fastequal = {0xa480ea , 0xa48222 , 0xa48193 , 0x0}, cc_keyno = {2, 20, 3, 0}, cc_lists = {head = {prev = 0x7f81591cad60, next = 0x7f81591aab18}}, cc_ntup = 29, cc_nkeys = 3, cc_relname = 0x7f8159217f10 "pg_proc", cc_reloid = 1255, cc_indexoid = 2691, cc_relisshared = false, cc_next = {next = 0x1639698}, cc_skey = {{sk_flags = 0, sk_attno = 2, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = { fn_addr = 0x9a1cf3 , fn_oid = 62, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 0}, {sk_flags = 0, sk_attno = 20, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = {fn_addr = 0x9be7e8 , fn_oid = 679, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 0}, {sk_flags = 0, sk_attno = 3, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = { fn_addr = 0x9be650 , fn_oid = 184, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 0}, {sk_flags = 0, sk_attno = 0, sk_strategy = 0, sk_subtype = 0, sk_collation = 0, sk_func = {fn_addr = 0x0, fn_oid = 0, fn_nargs = 0, ---Type to continue, or q to quit--- fn_strict = false, fn_retset = false, fn_stats = 0 '\000', fn_extra = 0x0, fn_mcxt = 0x0, fn_expr = 0x0}, sk_argument = 0}}}(gdb)
pg_proc tuple描述符
(gdb) p *cache->cc_tupdesc$14 = {natts = 29, tdtypeid = 81, tdtypmod = -1, tdrefcount = -1, constr = 0x7f8159216b90, attrs = 0x7f8159216f10}(gdb) p *cache->cc_tupdesc->constr$15 = {defval = 0x0, check = 0x0, missing = 0x0, num_defval = 0, num_check = 0, has_not_null = true, has_generated_stored = false}(gdb) p *cache->cc_tupdesc->attrs$16 = {attrelid = 1255, attname = {data = "oid", '\000'}, atttypid = 26, attstattarget = -1, attlen = 4, attnum = 1, attndims = 0, attcacheoff = 0, atttypmod = -1, attbyval = true, attstorage = 112 'p', attalign = 105 'i', attnotnull = true, atthasdef = false, atthasmissing = false, attidentity = 0 '\000', attgenerated = 0 '\000', attisdropped = false, attislocal = true, attinhcount = 0, attcollation = 0}(gdb) p cache->cc_tupdesc->attrs[1]$17 = {attrelid = 1255, attname = {data = "proname", '\000' }, atttypid = 19, attstattarget = -1, attlen = 64, attnum = 2, attndims = 0, attcacheoff = 4, atttypmod = -1, attbyval = false, attstorage = 112 'p', attalign = 99 'c', attnotnull = true, atthasdef = false, atthasmissing = false, attidentity = 0 '\000', attgenerated = 0 '\000', attisdropped = false, attislocal = true, attinhcount = 0, attcollation = 950}(gdb)
获取hash桶
(gdb) n1220 if (unlikely(cache->cc_tupdesc == NULL))(gdb) 1228 arguments[0] = v1;(gdb) 1229 arguments[1] = v2;(gdb) 1230 arguments[2] = v3;(gdb) 1231 arguments[3] = v4;(gdb) 1236 hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);(gdb) 1237 hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);(gdb) 1245 bucket = &cache->cc_bucket[hashIndex];(gdb) p hashValue$18 = 3879045281(gdb) p hashIndex$19 = 33(gdb) (gdb) n1246 dlist_foreach(iter, bucket)(gdb) p *bucket$20 = {head = {prev = 0x7f815919d688, next = 0x7f815919d688}}(gdb)
沿着hash桶中的链表查找
(gdb) n1248 ct = dlist_container(CatCTup, cache_elem, iter.cur);(gdb) 1250 if (ct->dead)(gdb) 1253 if (ct->hash_value != hashValue)(gdb) 1254 continue; /* quickly skip entry if wrong hash val */(gdb) 1246 dlist_foreach(iter, bucket)(gdb) 1299 return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);(gdb) stepSearchCatCacheMiss (cache=0x1639c80, nkeys=3, hashValue=3879045281, hashIndex=33, v1=22952624, v2=23913368, v3=2200, v4=0) at catcache.c:13271327 arguments[0] = v1;(gdb)
如没有找到,则调用SearchCatCacheMiss,构建扫描键
(gdb) n1328 arguments[1] = v2;(gdb) 1329 arguments[2] = v3;(gdb) 1330 arguments[3] = v4;(gdb) 1336 memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);(gdb) 1337 cur_skey[0].sk_argument = v1;(gdb) 1338 cur_skey[1].sk_argument = v2;(gdb) 1339 cur_skey[2].sk_argument = v3;(gdb) 1340 cur_skey[3].sk_argument = v4;(gdb) 1357 relation = table_open(cache->cc_reloid, AccessShareLock);(gdb) p *cur_skey$21 = {sk_flags = 0, sk_attno = 2, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = {fn_addr = 0x9a1cf3, fn_oid = 62, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 22952624}(gdb) p cur_skey[1]$22 = {sk_flags = 0, sk_attno = 20, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = {fn_addr = 0x9be7e8 , fn_oid = 679, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 23913368}(gdb) p cur_skey[2]$23 = {sk_flags = 0, sk_attno = 3, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = {fn_addr = 0x9be650 , fn_oid = 184, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 2200}(gdb) p cur_skey[4]$24 = {sk_flags = 0, sk_attno = 0, sk_strategy = 0, sk_subtype = 0, sk_collation = 2691234902, sk_func = {fn_addr = 0x1639c98, fn_oid = 16681376, fn_nargs = -30559, fn_strict = 53, fn_retset = 231, fn_stats = 96 '`', fn_extra = 0xa4a825 , fn_mcxt = 0x898, fn_expr = 0x0}, sk_argument = 0}(gdb)
开始扫描
(gdb) n1361 IndexScanOK(cache, cur_skey),(gdb) 1359 scandesc = systable_beginscan(relation,(gdb) 1366 ct = NULL;(gdb) 1368 while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))(gdb) 1370 ct = CatalogCacheCreateEntry(cache, ntp, arguments,(gdb) 1374 ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);(gdb) 1375 ct->refcount++;(gdb) 1376 ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);(gdb) 1377 break; /* assume only one match */(gdb) 1380 systable_endscan(scandesc);(gdb) 1382 table_close(relation, AccessShareLock);(gdb) 1394 if (ct == NULL)(gdb)
成功,返回tuple
1425 return &ct->tuple;(gdb) (gdb) p *ct$25 = {ct_magic = 1462113538, hash_value = 3879045281, keys = {140193522567236, 140193522567344, 2200, 0}, cache_elem = {prev = 0x163a370, next = 0x7f815919d688}, refcount = 1, dead = false, negative = false, tuple = {t_len = 488, t_self = {ip_blkid = { bi_hi = 0, bi_lo = 42}, ip_posid = 20}, t_tableOid = 1255, t_data = 0x7f81591cc820}, c_list = 0x0, my_cache = 0x1639c80}(gdb) p ct->tuple$26 = {t_len = 488, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 42}, ip_posid = 20}, t_tableOid = 1255, t_data = 0x7f81591cc820}(gdb)
到此,相信大家对"分析PostgreSQL创建函数的过程"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
函数
次数
过程
分析
参数
结构
检索
对象
不同
成功
信息
内容
数据
类型
可变
学习
循环
跟踪
输入
更深
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
如何选择好的app软件开发
网络安全未来5年前景
数据库倒入数据不显示
服务器升级后宝塔不能升级
淄博工具软件开发定制
期货期货自动化交易软件开发
失落方舟欧美服怎么换服务器
吴中区口碑好的网络技术排名靠前
主机屋数据库地址怎么看
浙江应用软件开发收费报价表
网络技术开发公司实践报告
简述系统数据库
用户登录查询数据库网页
公司网络安全管理措施
阿里巴巴服务器里面的黑科技
公务员招网络安全专业的单位
中公优就业网络安全培训多久
广州集结网络技术
winscp远程服务器之间传输
计算机考试网络技术几号考
网络安全咨询规划资质
施工网络技术讲解视频
软件开发转向游戏开发的访谈
医疗软件开发框架
国美互联网科技有限公司
博山资产管理软件开发公司
服务器添加二级域名
国家网络安全宣传会议
外贸服务器选址
linux软件开发工具