分析PostgreSQL创建函数的过程
发表于:2024-12-04 作者:千家信息网编辑
千家信息网最后更新 2024年12月04日,本篇内容主要讲解"分析PostgreSQL创建函数的过程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"分析PostgreSQL创建函数的过程"吧!一、数据
千家信息网最后更新 2024年12月04日分析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安全错误
数据库的锁怎样保障安全
云服务器的快照回滚
当今软件开发主要面临哪些问题
eval如何传两个数据库值
斐讯 网络安全密钥
物流行业人员数据库
陕西北斗时间同步服务器
国家网络安全天网百度百科
网络安全知识班会会议记录
PSV显示服务器或设备被切断
环聊无法连接服务器
集美区民宿软件开发
office数据库怎么联机
数据库隐藏的字段重新显示
喜达屋数据库
关于互联网科技的高清图片
软件开发投标需要材料
代理服务器的类型
云服务器 系统盘
历史文献数据库建设
清远无限软件开发平均价格
国家安全观和网络安全观关系
网络安全行业创新倡导公司
服务器管理不要启动
苏州前端软件开发服务费
冒险岛同一服务器
长盛招聘网络安全
百望数据库是什么意思
服务器 最大并发连接数
查找连接不到服务器怎么回事
湖南公安厅网络安全管理局