千家信息网

分析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软件开发工具
0