ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(245)- plpgsql(CreateFunction-construct_array)

PostgreSQL 源码解读(245)- plpgsql(CreateFunction-construct_array)

原创 PostgreSQL 作者:husthxd 时间:2020-02-26 18:23:34 0 删除 编辑

本节简单介绍了PostgreSQL创建函数的过程,其实现函数是CreateFunction。这里介绍了CreateFunction中的通用函数construct_array。

一、数据结构

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;

二、源码解读


/*
*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,sizeof(Oid), true, 'i');
*parameterModes = construct_array(paramModes, parameterCount, CHAROID,1, true, 'c');
*/
/*
 * construct_array  --- simple method for constructing an array object
 * 构建数组对象的简单方法
 *
 * elems: array of Datum items to become the array contents
 *      (NULL element values are not supported).
 * nelems: number of items
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
 *
 * elems: 数组中的元素
 * nelems:元素数量
 * elmtype,elmlen,elmbyval,elmalign:元素的数据类型信息
 *
 * A palloc'd 1-D array object is constructed and returned.  Note that
 * elem values will be copied into the object even if pass-by-ref type.
 * Also note the result will be 0-D not 1-D if nelems = 0.
 * 构建一维数组对象并返回.
 * 注意元素值即使引用传递类型也会拷贝到对象中.
 * 同时要注意如元素个数为0,结果是0-D数组而不是一维数组.
 *
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
 * from the system catalogs, given the elmtype.  However, the caller is
 * in a better position to cache this info across multiple uses, or even
 * to hard-wire values if the element type is hard-wired.
 * 给定elmtype的情况下,从系统目录中获取元素elmlen/elmbval/elmalign应更加清晰.
 * 但是,调用者便于跨用户缓存这些信息.
 */
ArrayType *
construct_array(Datum *elems, int nelems,
        Oid elmtype,
        int elmlen, bool elmbyval, char elmalign)
{
  int     dims[1];
  int     lbs[1];
  dims[0] = nelems;//数组元素个数
  lbs[0] = 1;
  return construct_md_array(elems, NULL, 1, dims, lbs,
                elmtype, elmlen, elmbyval, elmalign);
}
/*
 * construct_md_array --- simple method for constructing an array object
 *              with arbitrary dimensions and possible NULLs
 * construct_md_array:构建任意维度和可能存在NULLs的数据对象
 *
 * elems: array of Datum items to become the array contents
 * nulls: array of is-null flags (can be NULL if no nulls)
 * ndims: number of dimensions
 * dims: integer array with size of each dimension
 * lbs: integer array with lower bound of each dimension
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
 * elems:同construct_array
 * nulls:is-null标记数组
 * ndims:维度数
 * dims:每个维度大小的整型数组
 * lbs:每个维度的下界整型数组
 * elmtype, elmlen, elmbyval, elmalign: 元素的数据类型信息
 *
 * A palloc'd ndims-D array object is constructed and returned.  Note that
 * elem values will be copied into the object even if pass-by-ref type.
 * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
 * 构建ndims维数组并返回.
 *
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
 * from the system catalogs, given the elmtype.  However, the caller is
 * in a better position to cache this info across multiple uses, or even
 * to hard-wire values if the element type is hard-wired.
 */
ArrayType *
construct_md_array(Datum *elems,//元素内容
           bool *nulls,//nulls标记数组(每个元素占用一个位置)
           int ndims,//维度数
           int *dims,//每个维度的元素大小
           int *lbs,//每个维度的下界数组(low bound)
           Oid elmtype, int elmlen, bool elmbyval, char elmalign)
{
  ArrayType  *result;//结果
  bool    hasnulls;//是否存在null值
  int32   nbytes;//以字节为单位的大小
  int32   dataoffset;//数据偏移
  int     i;//临时变量
  int     nelems;//元素数目
  //校验维数
  if (ndims < 0)        /* we do allow zero-dimension arrays */
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
         errmsg("invalid number of dimensions: %d", ndims)));
  if (ndims > MAXDIM)
    ereport(ERROR,
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
            ndims, MAXDIM)));
  //元素数目:每个维数的大小相乘
  nelems = ArrayGetNItems(ndims, dims);
  /* if ndims <= 0 or any dims[i] == 0, return empty array */
  //维数≤0或者任意一维的个数等于0,返回空数组
  if (nelems <= 0)
    return construct_empty_array(elmtype);
  /* compute required space */
  //计算需要的空间大小
  nbytes = 0;
  hasnulls = false;
  for (i = 0; i < nelems; i++)
  {
    if (nulls && nulls[i])
    {
      //存在null值
      hasnulls = true;
      continue;
    }
    /* make sure data is not toasted */
    //确保数据没有toasted
    if (elmlen == -1)
      elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
    //以字节为单位的大小
    nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
    //#define att_align_nominal(cur_offset,attalign) ( ((attalign) == 'i') ? INTALIGN(cur_offset) : (((attalign) == 'c') ? (uintptr_t) (cur_offset) : (((attalign) == 'd') ? DOUBLEALIGN(cur_offset) : ( AssertMacro((attalign) == 's'), SHORTALIGN(cur_offset) ))) )
    //( ((elmalign) == 'i') ? (((uintptr_t) ((nbytes)) + ((ALIGNOF_INT) - 1)) & ~((uintptr_t) ((ALIGNOF_INT) - 1))) : (((elmalign) == 'c') ? (uintptr_t) (nbytes) : (((elmalign) == 'd') ? (((uintptr_t) ((nbytes)) + ((ALIGNOF_DOUBLE) - 1)) & ~((uintptr_t) ((ALIGNOF_DOUBLE) - 1))) : ( ((void)((bool) 1)), (((uintptr_t) ((nbytes)) + ((ALIGNOF_SHORT) - 1)) & ~((uintptr_t) ((ALIGNOF_SHORT) - 1))) ))) )
    nbytes = att_align_nominal(nbytes, elmalign);
    /* check for overflow of total request */
    //检查是否溢出
    //#define AllocSizeIsValid(size) ((Size) (size) <= MaxAllocSize)
    //((Size) (nbytes) <= ((Size) 0x3fffffff))(2的30次方)
    if (!AllocSizeIsValid(nbytes))
      ereport(ERROR,
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
           errmsg("array size exceeds the maximum allowed (%d)",
              (int) MaxAllocSize)));
  }
  /* Allocate and initialize result array */
  //分配和初始化结果数组
  if (hasnulls)
  {
    //分配null bitmap空间
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
    nbytes += dataoffset;
  }
  else
  {
    //无null bitmap
    dataoffset = 0;     /* marker for no null bitmap */
    //#define ARR_OVERHEAD_WITHNULLS(ndims,nitems) MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims) + ((nitems) + 7) / 8)
    //(((uintptr_t) ((sizeof(ArrayType) + 2 * sizeof(int) * (ndims))) + ((MAXIMUM_ALIGNOF) - 1)) & ~((uintptr_t) ((MAXIMUM_ALIGNOF) - 1)))
    //#define ARR_OVERHEAD_NONULLS(ndims) MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims))
    //(((uintptr_t) ((sizeof(ArrayType) + 2 * sizeof(int) * (ndims))) + ((MAXIMUM_ALIGNOF) - 1)) & ~((uintptr_t) ((MAXIMUM_ALIGNOF) - 1)))
    nbytes += ARR_OVERHEAD_NONULLS(ndims);
  }
  //分配空间
  result = (ArrayType *) palloc0(nbytes);
  //设置大小
  //SET_VARSIZE --> 
  //#define SET_VARSIZE(PTR,len) SET_VARSIZE_4B(PTR, len)
  //(((varattrib_4b *) (result))->va_4byte.va_header = (((uint32) (nbytes)) << 2))
  //nbytes * 4
  SET_VARSIZE(result, nbytes);
  //维数
  result->ndim = ndims;
  //数据偏移(前面是null bitmap)
  result->dataoffset = dataoffset;
  //元素类型
  result->elemtype = elmtype;
  //ARR_DIMS --> 元素个数
  //#define ARR_DIMS(a) ((int *) (((char *) (a)) + sizeof(ArrayType)))
  //((int *) (((char *) (result)) + sizeof(ArrayType))) --> ((int *) (((char *) (result)) + 16))
  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
  //ARR_LBOUND --> 下限边界
  //#define ARR_LBOUND(a) ((int *) (((char *) (a)) + sizeof(ArrayType) + sizeof(int) * ARR_NDIM(a)))
  //((int *) (((char *) (result)) + sizeof(ArrayType) + sizeof(int) * ((result)->ndim)))
  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
  //拷贝数组元素
  CopyArrayEls(result,
         elems, nulls, nelems,
         elmlen, elmbyval, elmalign,
         false);
  return result;
}
/*
 * Convert array dimensions into number of elements
 * 转换数组的维数为元素个数
 *
 * This must do overflow checking, since it is used to validate that a user
 * dimensionality request doesn't overflow what we can handle.
 * 必须执行溢出检查.
 *
 * We limit array sizes to at most about a quarter billion elements,
 * so that it's not necessary to check for overflow in quite so many
 * places --- for instance when palloc'ing Datum arrays.
 * 数组的大小限制在大概100w/4.
 *
 * The multiplication overflow check only works on machines that have int64
 * arithmetic, but that is nearly all platforms these days, and doing check
 * divides for those that don't seems way too expensive.
 */
int
ArrayGetNItems(int ndim, const int *dims)
{
  int32   ret;
  int     i;
#define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum)))
  if (ndim <= 0)
    return 0;
  ret = 1;
  for (i = 0; i < ndim; i++)
  {
    int64   prod;
    /* A negative dimension implies that UB-LB overflowed ... */
    if (dims[i] < 0)
      ereport(ERROR,
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
           errmsg("array size exceeds the maximum allowed (%d)",
              (int) MaxArraySize)));
    prod = (int64) ret * (int64) dims[i];
    ret = (int32) prod;
    if ((int64) ret != prod)
      ereport(ERROR,
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
           errmsg("array size exceeds the maximum allowed (%d)",
              (int) MaxArraySize)));
  }
  Assert(ret >= 0);
  if ((Size) ret > MaxArraySize)
    ereport(ERROR,
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
         errmsg("array size exceeds the maximum allowed (%d)",
            (int) MaxArraySize)));
  return (int) ret;
}
/*
 * Copy data into an array object from a temporary array of Datums.
 * 从Datums临时数组中拷贝数据到数组对象中.
 *
 * array: array object (with header fields already filled in)
 * values: array of Datums to be copied
 * nulls: array of is-null flags (can be NULL if no nulls)
 * nitems: number of Datums to be copied
 * typbyval, typlen, typalign: info about element datatype
 * freedata: if true and element type is pass-by-ref, pfree data values
 * referenced by Datums after copying them.
 * 
 * array:数组对象(头部已填充)
 * values:将要拷贝的Datums数组
 * nulls:is-null标记数组
 * nitems:元素数目
 * typbyval,typlen, typalign:元素数据类型信息
 * freedata:如为T并且元素类型是引用传递,则拷贝后释放空间
 *
 * If the input data is of varlena type, the caller must have ensured that
 * the values are not toasted.  (Doing it here doesn't work since the
 * caller has already allocated space for the array...)
 * 如果输入数据是varlena类型,调用者必须确保值没有toasted.
 */
void
CopyArrayEls(ArrayType *array,
       Datum *values,
       bool *nulls,
       int nitems,
       int typlen,
       bool typbyval,
       char typalign,
       bool freedata)
{
  //#define ARR_DATA_PTR(a) (((char *) (a)) + ARR_DATA_OFFSET(a))
  //(((char *) (array)) + (((array)->dataoffset != 0) ? (array)->dataoffset : (((uintptr_t) ((sizeof(ArrayType) + 2 * sizeof(int) * (((array)->ndim)))) + ((MAXIMUM_ALIGNOF) - 1)) & ~((uintptr_t) ((MAXIMUM_ALIGNOF) - 1)))))
  char     *p = ARR_DATA_PTR(array);
  //#define ARR_NULLBITMAP(a) (ARR_HASNULL(a) ? (bits8 *) (((char *) (a)) + sizeof(ArrayType) + 2 * sizeof(int) * ARR_NDIM(a)) : (bits8 *) NULL)
  //(((array)->dataoffset != 0) ? (bits8 *) (((char *) (array)) + sizeof(ArrayType) + 2 * sizeof(int) * ((array)->ndim)) : (bits8 *) NULL)
  bits8    *bitmap = ARR_NULLBITMAP(array);
  int     bitval = 0;
  int     bitmask = 1;
  int     i;
  if (typbyval)//传值
    freedata = false;
  for (i = 0; i < nitems; i++)//遍历元素
  {
    if (nulls && nulls[i])
    {
      //如为null,则bitmap不能为空指针
      if (!bitmap)    /* shouldn't happen */
        elog(ERROR, "null array element where not supported");
      /* bitmap bit stays 0 */
    }
    else
    {
      //
      bitval |= bitmask;
      p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
      if (freedata)
        pfree(DatumGetPointer(values[i]));
    }
    if (bitmap)
    {
      bitmask <<= 1;
      if (bitmask == 0x100)
      {
        *bitmap++ = bitval;
        bitval = 0;
        bitmask = 1;
      }
    }
  }
  if (bitmap && bitmask != 1)
    *bitmap = bitval;
}
/*
 * Copy datum to *dest and return total space used (including align padding)
 *
 * Caller must have handled case of NULL element
 */
static int
ArrayCastAndSet(Datum src,
        int typlen,
        bool typbyval,
        char typalign,
        char *dest)
{
  int     inc;
  if (typlen > 0)
  {
    if (typbyval)
      store_att_byval(dest, src, typlen);
    else
      memmove(dest, DatumGetPointer(src), typlen);
    inc = att_align_nominal(typlen, typalign);
  }
  else
  {
    Assert(!typbyval);
    inc = att_addlength_datum(0, typlen, src);
    memmove(dest, DatumGetPointer(src), inc);
    inc = att_align_nominal(inc, typalign);
  }
  return inc;
}

三、跟踪分析

测试脚本


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 construct_array
Breakpoint 1 at 0x91eaeb: file arrayfuncs.c, line 3298.
(gdb) c
Continuing.
Breakpoint 1, construct_array (elems=0x22d9da0, nelems=5, elmtype=26, elmlen=4, elmbyval=true, elmalign=105 'i') at arrayfuncs.c:3298
3298    dims[0] = nelems;
(gdb) bt
#0  construct_array (elems=0x22d9da0, nelems=5, elmtype=26, elmlen=4, elmbyval=true, elmalign=105 'i') at arrayfuncs.c:3298
#1  0x000000000066fa48 in interpret_function_parameter_list (pstate=0x22d9c88, parameters=0x22b3d30, languageOid=13581, objtype=OBJECT_FUNCTION, parameterTypes=0x7fff6bde92b8, 
    allParameterTypes=0x7fff6bde92b0, parameterModes=0x7fff6bde92a8, parameterNames=0x7fff6bde92a0, parameterDefaults=0x7fff6bde9298, variadicArgType=0x7fff6bde9294, 
    requiredResultType=0x7fff6bde9290) at functioncmds.c:434
#2  0x000000000067100f in CreateFunction (pstate=0x22d9c88, stmt=0x22b48c8) at functioncmds.c:1053
#3  0x00000000008f61a6 in ProcessUtilitySlow (pstate=0x22d9c88, pstmt=0x22b4c48, 
    queryString=0x22b2ed8 "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)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x22b4d40, completionTag=0x7fff6bde9a60 "") at utility.c:1478
#4  0x00000000008f5069 in standard_ProcessUtility (pstmt=0x22b4c48, 
    queryString=0x22b2ed8 "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)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x22b4d40, completionTag=0x7fff6bde9a60 "") at utility.c:927
#5  0x00000000008f418f in ProcessUtility (pstmt=0x22b4c48, 
    queryString=0x22b2ed8 "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)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x22b4d40, completionTag=0x7fff6bde9a60 "") at utility.c:360
#6  0x00000000008f3188 in PortalRunUtility (portal=0x231a248, pstmt=0x22b4c48, isTopLevel=true, setHoldSnapshot=false, dest=0x22b4d40, completionTag=0x7fff6bde9a60 "") at pquery.c:1175
#7  0x00000000008f339e in PortalRunMulti (portal=0x231a248, isTopLevel=true, setHoldSnapshot=false, dest=0x22b4d40, altdest=0x22b4d40, completionTag=0x7fff6bde9a60 "") at pquery.c:1321
#8  0x00000000008f28d3 in PortalRun (portal=0x231a248, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x22b4d40, altdest=0x22b4d40, completionTag=0x7fff6bde9a60 "")
    at pquery.c:796
#9  0x00000000008ec882 in exec_simple_query (
    query_string=0x22b2ed8 "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)\nreturns record \nas\n$$\ndeclare\nbegin\n ---Type <return> to continue, or q <return> to quit---^CQuit
(gdb)

输入参数:
5个参数的OID,分别是23/1043/1043/23/1043;有5个元素;类型为26-OID;元素长度为4(int32);以传值方式传递;对其方式为i-INT

(gdb) p elems[0]
$73 = 23
(gdb) p elems[1]
$74 = 1043
(gdb) p elems[2]
$75 = 1043
(gdb) p elems[3]
$76 = 23
(gdb) p elems[4]
$77 = 1043
(gdb)

进入construct_md_array,输入参数维数-1,5个元素,数组下界为1

(gdb) n
3299    lbs[0] = 1;
(gdb) 
3301    return construct_md_array(elems, NULL, 1, dims, lbs,
(gdb) step
construct_md_array (elems=0x22d9dd8, nulls=0x0, ndims=1, dims=0x7fff6bde9070, lbs=0x7fff6bde9060, elmtype=26, elmlen=4, elmbyval=true, elmalign=105 'i') at arrayfuncs.c:3340
3340    if (ndims < 0)        /* we do allow zero-dimension arrays */
(gdb)

执行相关校验(最大维数为6),获取元素个数(5)

(gdb) n
3344    if (ndims > MAXDIM)
(gdb) p MAXDIM
$78 = 6
(gdb) n
3350    nelems = ArrayGetNItems(ndims, dims);
(gdb) 
3353    if (nelems <= 0)
(gdb) p nelems
$79 = 5
(gdb)

计算需占用的空间

(gdb) n
3357    nbytes = 0;
(gdb) 
3358    hasnulls = false;
(gdb) 
3359    for (i = 0; i < nelems; i++)
(gdb) 
3361      if (nulls && nulls[i])
(gdb) 
3367      if (elmlen == -1)
(gdb) 
3369      nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
(gdb) 
3370      nbytes = att_align_nominal(nbytes, elmalign);
(gdb) 
3372      if (!AllocSizeIsValid(nbytes))
(gdb) 
3359    for (i = 0; i < nelems; i++)
(gdb) 
3361      if (nulls && nulls[i])
(gdb) 
3367      if (elmlen == -1)
(gdb) 
3369      nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
(gdb) 
3370      nbytes = att_align_nominal(nbytes, elmalign);
(gdb) 
3372      if (!AllocSizeIsValid(nbytes))
(gdb) 
3359    for (i = 0; i < nelems; i++)
(gdb) 
3361      if (nulls && nulls[i])
(gdb) 
3367      if (elmlen == -1)
(gdb) 
3369      nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
(gdb) 
3370      nbytes = att_align_nominal(nbytes, elmalign);
(gdb) 
3372      if (!AllocSizeIsValid(nbytes))
(gdb) 
3359    for (i = 0; i < nelems; i++)
(gdb) 
3361      if (nulls && nulls[i])
(gdb) 
3367      if (elmlen == -1)
(gdb) 
3369      nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
(gdb) 
3370      nbytes = att_align_nominal(nbytes, elmalign);
(gdb) 
3372      if (!AllocSizeIsValid(nbytes))
(gdb) 
3359    for (i = 0; i < nelems; i++)
(gdb) 
3361      if (nulls && nulls[i])
(gdb) 
3367      if (elmlen == -1)
(gdb) 
3369      nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
(gdb) 
3370      nbytes = att_align_nominal(nbytes, elmalign);
(gdb) 
3372      if (!AllocSizeIsValid(nbytes))
(gdb) 
3359    for (i = 0; i < nelems; i++)
(gdb) 
3380    if (hasnulls)
(gdb)

5个OID,需占用20个bytes

(gdb) p nbytes
$80 = 20
(gdb)

没有null bitmap,偏移为0;添加头部空间(ArrayType的大小16+2*4*维数,这里是24)

(gdb) n
3387      dataoffset = 0;     /* marker for no null bitmap */
(gdb) 
3388      nbytes += ARR_OVERHEAD_NONULLS(ndims);
(gdb) 
3390    result = (ArrayType *) palloc0(nbytes);
(gdb) p nbyes
$81 = 44
(gdb)

分配空间,赋值结构体

(gdb) n
3391    SET_VARSIZE(result, nbytes);
(gdb) n
3392    result->ndim = ndims;
(gdb) p nbytes
$82 = 44
(gdb) p *result
$83 = {vl_len_ = 176, ndim = 0, dataoffset = 0, elemtype = 0}
(gdb) n
3393    result->dataoffset = dataoffset;
(gdb) 
3394    result->elemtype = elmtype;
(gdb) 
3395    memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));-->元素个数
(gdb) 
3396    memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));-->第一个维度的下界
(gdb) p *result
$84 = {vl_len_ = 176, ndim = 1, dataoffset = 0, elemtype = 26}
(gdb) p *(int *)((char *)result+16)
$85 = 5
(gdb) p *(int *)((char *)result+20)
$86 = 0
(gdb) p *(int *)((char *)result+24)
$87 = 0
(gdb) p *(int *)((char *)result+28)
$88 = 0
(gdb) n
3398    CopyArrayEls(result,
(gdb) p *(int *)((char *)result+20)
$89 = 1
(gdb)

拷贝OID到数组中,从24开始,每个值占用4个byte,共5个值

(gdb) n
3403    return result;
(gdb) p *(int *)((char *)result+24)
$90 = 23
(gdb) p *(int *)((char *)result+28)
$91 = 1043
(gdb) p *(int *)((char *)result+32)
$92 = 1043
(gdb) p *(int *)((char *)result+36)
$93 = 23
(gdb) p *(int *)((char *)result+40)
$94 = 1043
(gdb)

完成函数调用

(gdb) n
3404  }
(gdb) 
construct_array (elems=0x22d9dd8, nelems=5, elmtype=26, elmlen=4, elmbyval=true, elmalign=105 'i') at arrayfuncs.c:3303
3303  }
(gdb)

DONE!

四、参考资料

N/A

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2677362/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
ITPUB数据库版块资深版主,对Oracle、PostgreSQL有深入研究。

注册时间:2007-12-28

  • 博文量
    1488
  • 访问量
    3925784