我的编程空间,编程开发者的网络收藏夹
学习永远不晚

分析PostgreSQL创建函数的过程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

分析PostgreSQL创建函数的过程

本篇内容主要讲解“分析PostgreSQL创建函数的过程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析PostgreSQL创建函数的过程”吧!

一、数据结构

Form_pg_language
plpgsql语言定义结构体


CATALOG(pg_language,2612,LanguageRelationId)
{
    Oid         oid;            
    
    NameData    lanname;
    
    Oid         lanowner BKI_DEFAULT(PGUID);
    
    bool        lanispl BKI_DEFAULT(f);
    
    bool        lanpltrusted BKI_DEFAULT(f);
    
    Oid         lanplcallfoid BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    
    Oid         laninline BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    
    Oid         lanvalidator BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
#ifdef CATALOG_VARLEN           
    
    aclitem     lanacl[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_language;

typedef FormData_pg_language *Form_pg_language;

ArrayType


typedef struct
{
    //可变的header
    int32       vl_len_;        
    //维度
    int         ndim;           
    //指向数据的偏移量,如为0则表示没有位图
    int32       dataoffset;     
    //元素类型的OID
    Oid         elemtype;       
} ArrayType;

DefElem

typedef struct DefElem
{
  NodeTag   type;
  char     *defnamespace; 
  char     *defname;
  Node     *arg;      
  DefElemAction defaction;  
  int     location;   
} DefElem;

FunctionParameter

typedef enum FunctionParameterMode
{
  
  FUNC_PARAM_IN = 'i',    
  FUNC_PARAM_OUT = 'o',   
  FUNC_PARAM_INOUT = 'b',   
  FUNC_PARAM_VARIADIC = 'v',  
  FUNC_PARAM_TABLE = 't'    
} FunctionParameterMode;
typedef struct FunctionParameter
{
  NodeTag   type;
  char     *name;     
  TypeName   *argType;    
  FunctionParameterMode mode; 
  Node     *defexpr;    
} FunctionParameter;

CatCache


typedef uint32 (*CCHashFN) (Datum datum);

typedef bool (*CCFastEqualFN) (Datum a, Datum b);
typedef struct catcache
{
  //cache ID
  int     id;       
  //cache的hash槽
  int     cc_nbuckets;  
  //元组描述符
  TupleDesc cc_tupdesc;   
  //hash桶
  dlist_head *cc_bucket;    
  //每个key的hash函数
  CCHashFN  cc_hashfunc[CATCACHE_MAXKEYS];  
  //每个key的快速等值函数
  CCFastEqualFN cc_fastequal[CATCACHE_MAXKEYS]; 
  //每个key的属性编号
  int     cc_keyno[CATCACHE_MAXKEYS]; 
  //CatCList结构体链表
  dlist_head  cc_lists;   
  //cache中元组数
  int     cc_ntup;    
  //keys数
  int     cc_nkeys;   
  //cache元组相关的relation
  const char *cc_relname;   
  //relation OID
  Oid     cc_reloid;    
  //匹配缓存keys的索引OID
  Oid     cc_indexoid;  
  //是否可跨库共享?
  bool    cc_relisshared; 
  //链表链接
  slist_node  cc_next;    
  //用于heap扫描的预计算key信息
  ScanKeyData cc_skey[CATCACHE_MAXKEYS];  
  
#ifdef CATCACHE_STATS
  //检索次数
  long    cc_searches;  
  //匹配次数
  long    cc_hits;    
  //未命中次数
  long    cc_neg_hits;  
  //未命中成功加载次数
  long    cc_newloads;  
  
  //验证失效次数
  long    cc_invals;    
  //链表检索次数
  long    cc_lsearches; 
  //
  long    cc_lhits;   
#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);
}

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;
  
  //确保处于事务中
  Assert(IsTransactionState());
  Assert(cache->cc_nkeys == nkeys);
  
  if (unlikely(cache->cc_tupdesc == NULL))
    CatalogCacheInitializeCache(cache);
#ifdef CATCACHE_STATS
  cache->cc_searches++;
#endif
  
  //初始化本地参数数组
  arguments[0] = v1;
  arguments[1] = v2;
  arguments[2] = v3;
  arguments[3] = v4;
  
  hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
  hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
  
  bucket = &cache->cc_bucket[hashIndex];
  dlist_foreach(iter, bucket)
  {
    ct = dlist_container(CatCTup, cache_elem, iter.cur);
    if (ct->dead)//忽略已废弃的条目
      continue;     
    if (ct->hash_value != hashValue)//跳过hash不同的项
      continue;     
    //不同的元组,跳过
    if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments))
      continue;
    
    dlist_move_head(bucket, &ct->cache_elem);
    
    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);
}

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];
  
  arguments[0] = v1;
  arguments[1] = v2;
  arguments[2] = v3;
  arguments[3] = v4;
  
  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;
  
  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);
    
    ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
    ct->refcount++;
    ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
    break;          
  }
  systable_endscan(scandesc);
  table_close(relation, AccessShareLock);
  
  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);
    
    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, 
    proclass="lazy" data-src=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;     
(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;          
(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创建函数的过程”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

分析PostgreSQL创建函数的过程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

分析PostgreSQL的CreateFunction函数

本篇内容主要讲解“分析PostgreSQL的CreateFunction函数”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析PostgreSQL的CreateFunction函数”吧!一、数据
2023-05-31

php中创建函数的示例分析

这篇文章主要为大家展示了“php中创建函数的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“php中创建函数的示例分析”这篇文章吧。我们要学习的是函数,那我们怎么能不知道函数是什么呢?我们
2023-06-20

mysql函数和过程创建和调用

navicat 新建过程 调用 CALL getReId(@id); SELECT @id;   navicat 新建函数 调用 select getReNum(1);
mysql函数和过程创建和调用
2015-05-26

mysql如何创建存储过程及函数

这篇文章主要为大家展示了“mysql如何创建存储过程及函数”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“mysql如何创建存储过程及函数”这篇文章吧。1. 存储过程1.1. 基本语法create
2023-06-21

MySQL存储过程和函数怎么创建

这篇文章主要介绍“MySQL存储过程和函数怎么创建”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“MySQL存储过程和函数怎么创建”文章能帮助大家解决问题。1.0 创建存储过程和函数创建存储过程和函
2023-06-30

创建函数/类的线程

# 创建类的线程import threadingimport timeclass MyThread(threading.Thread): def run(self): for i in range(3):
2023-01-30

编程热搜

目录