分析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;

二、源码解读

HeapTuple
SearchSysCache3(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);
}
HeapTuple
SearchCatCache3(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 HeapTuple
SearchCatCacheInternal(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 HeapTuple
SearchCatCacheMiss(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
$$
declare
begin
  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;
  pio_v3 := 'pio_v3 i/o';
  po_v4 := 100;
  po_v5 := 'po_v5 out';
end;
$$ LANGUAGE plpgsql;

启动GDB跟踪

(gdb) b pg_proc.c:367
Breakpoint 1 at 0x5be038: file pg_proc.c, line 367.
(gdb) 
(gdb) c
Continuing.
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:367
367   tupDesc = RelationGetDescr(rel);
(gdb)

进入SearchSysCache3

(gdb) n
370   oldtup = SearchSysCache3(PROCNAMEARGSNSP,
(gdb) step
SearchSysCache3 (cacheId=42, key1=22952624, key2=23913368, key3=2200) at syscache.c:1149
1149    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) n
1151    Assert(SysCache[cacheId]->cc_nkeys == 3);
(gdb) n
1153    return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
(gdb) step
SearchCatCache3 (cache=0x1639c80, v1=22952624, v2=23913368, v3=2200) at catcache.c:1183
1183    return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
(gdb)

进入SearchCatCacheInternal

(gdb) step
SearchCatCacheInternal (cache=0x1639c80, nkeys=3, v1=22952624, v2=23913368, v3=2200, v4=0)
    at catcache.c:1213
1213    Assert(IsTransactionState());
(gdb)

cache信息

(gdb) n
1215    Assert(cache->cc_nkeys == nkeys);
(gdb) p *cache
$13 = {id = 42, cc_nbuckets = 128, cc_tupdesc = 0x7f8159216ef8, cc_bucket = 0x163a160, 
  cc_hashfunc = {0xa48129 <namehashfast>, 0xa48257 <oidvectorhashfast>, 
    0xa481b0 <int4hashfast>, 0x0}, cc_fastequal = {0xa480ea <nameeqfast>, 
    0xa48222 <oidvectoreqfast>, 0xa48193 <int4eqfast>, 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 <nameeq>, 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 <oidvectoreq>, 
        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 <oideq>, 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 <return> to continue, or q <return> 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' <repeats 60 times>}, 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' <repeats 56 times>}, 
  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) n
1220    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) n
1246    dlist_foreach(iter, bucket)
(gdb) p *bucket
$20 = {head = {prev = 0x7f815919d688, next = 0x7f815919d688}}
(gdb)

沿着hash桶中的链表查找

(gdb) n
1248      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) step
SearchCatCacheMiss (cache=0x1639c80, nkeys=3, hashValue=3879045281, hashIndex=33, 
    v1=22952624, v2=23913368, v3=2200, v4=0) at catcache.c:1327
1327    arguments[0] = v1;
(gdb)

如没有找到,则调用SearchCatCacheMiss,构建扫描键

(gdb) n
1328    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 <nameeq>, 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 <oidvectoreq>, 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 <oideq>, 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 <SearchCatCacheInternal+572>, fn_mcxt = 0x898, fn_expr = 0x0}, 
  sk_argument = 0}
(gdb)

开始扫描

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


【AD】美国洛杉矶/香港/日本VPS推荐,回程电信CN2 GIA线路,延迟低、稳定性高、免费备份_搬瓦工

【AD】炭云:36元/年/1GB内存/20GB SSD空间/500GB流量/5Gbps端口/KVM/香港/国际线路LUMEN