千家信息网

分析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创建函数的过程"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0