postgresql/lightdb字段typemod属性的内部实现及物理表示pg_attribute.atttypmod

以如下DDL为示例
zjh@postgres=# create table test_for_test1(id int primary key,id1 decimal(10,2),t text,t1 varchar(100)); CREATE TABLE zjh@postgres=# select * from pg_class where relname='test_for_test1'; oid | relname | relnamespace | reltype | reloftype | relowner | relam | relfilenode | reltablespace | relpages | reltuples | relallvisible | reltoastrelid | relhasindex | relissha red | relpersistence | relkind | relnatts | relchecks | relhasrules | relhastriggers | relhassubclass | relrowsecurity | relforcerowsecurity | relispopulated | relreplident | relispartition | relrewrite | relfrozenxid | relminmxid | relacl | reloptions | relpartbound -------+----------------+--------------+---------+-----------+----------+-------+-------------+---------------+----------+-----------+---------------+---------------+-------------+--------- ----+----------------+---------+----------+-----------+-------------+----------------+----------------+----------------+---------------------+----------------+--------------+--------------- -+------------+--------------+------------+--------+------------+-------------- 41052 | test_for_test1 | 2200 | 41054 | 0 | 10 | 2 | 41052 | 0 | 0 | 0 | 0 | 41055 | t | f | p | r | 4 | 0 | f | f | f | f | f | t | d | f | 0 | 17074219 | 1 | | | (1 row) zjh@postgres=# select attname,atttypmod from pg_attribute where attrelid = 41052; attname | atttypmod ----------+----------- tableoid | -1 cmax | -1 xmax | -1 cmin | -1 xmin | -1 ctid | -1 id | -1 id1 | 655366 t | -1 t1 | 104 (10 rows)

  先上答案,655366=10 << 16 | 2 + 4,也就是10,2。104 = 100 + 4。

  typemod的计算入口函数为typenameTypeMod,如下:

>	typenameTypeMod(ParseState * pstate=0x2143220, const TypeName * typeName=0x2142cb8, Type typ=0x7fc240f8bc60) Line 407	C
 	LookupTypeNameExtended(ParseState * pstate=0x2143220, const TypeName * typeName=0x2142cb8, int32 * typmod_p=0x0, _Bool temp_ok=true, _Bool missing_ok=false) Line 211	C
 	LookupTypeName(ParseState * pstate=0x2143220, const TypeName * typeName=0x2142cb8, int32 * typmod_p=0x0, _Bool missing_ok=false) Line 41	C
 	typenameType(ParseState * pstate=0x2143220, const TypeName * typeName=0x2142cb8, int32 * typmod_p=0x0) Line 268	C
 	transformColumnType(CreateStmtContext * cxt=0x7ffd456eff70, ColumnDef * column=0x2142bb0) Line 3705	C
 	transformColumnDefinition(CreateStmtContext * cxt=0x7ffd456eff70, ColumnDef * column=0x2142bb0) Line 592	C
 	transformCreateStmt(CreateStmt * stmt=0x2155a68, const char * queryString=0x205f2f8 "create table test_for_test1(id int primary key,id1 decimal(10,2),t text,t1 varchar(100));") Line 284	C
 	ProcessUtilitySlow(ParseState * pstate=0x2155958, PlannedStmt * pstmt=0x2175d98, const char * queryString=0x205f2f8 "create table test_for_test1(id int primary key,id1 decimal(10,2),t text,t1 varchar(100));", ProcessUtilityContext context=PROCESS_UTILITY_TOPLEVEL, ParamListInfo params=0x0, QueryEnvironment * queryEnv=0x0, DestReceiver * dest=0x21c7cb8, QueryCompletion * qc=0x7ffd456f0a10) Line 1147	C

  调用pg_proc中为每种数据类型定义的typmodin函数执行计算,如下:

foreach(l, typeName->typmods)
	{
		Node	   *tm = (Node *) lfirst(l);
		char	   *cstr = NULL;

		if (IsA(tm, A_Const))
		{
			A_Const    *ac = (A_Const *) tm;

			if (IsA(&ac->val, Integer))
			{
				cstr = psprintf("%ld", (long) ac->val.val.ival);
			}
			else if (IsA(&ac->val, Float) ||
					 IsA(&ac->val, String))
			{
				/* we can just use the str field directly. */
				cstr = ac->val.val.str;
			}
		}
		else if (IsA(tm, ColumnRef))
		{
			ColumnRef  *cr = (ColumnRef *) tm;

			if (list_length(cr->fields) == 1 &&
				IsA(linitial(cr->fields), String))
				cstr = strVal(linitial(cr->fields));
		}
		if (!cstr)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("type modifiers must be simple constants or identifiers"),
					 parser_errposition(pstate, typeName->location)));
		datums[n++] = CStringGetDatum(cstr);
	}

	/* hardwired knowledge about cstring's representation details here */
	arrtypmod = construct_array(datums, n, CSTRINGOID,
								-2, false, TYPALIGN_CHAR);

	/* arrange to report location if type's typmodin function fails */
	setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);

	result = DatumGetInt32(OidFunctionCall1(typmodin,
											PointerGetDatum(arrtypmod))); -- 通过动态函数调用实现

  其中varchar和numeric的typmodin函数分别如下:

zjh@postgres=# select * from pg_proc where oid=2915 or oid = 2917;
 oid  |     proname     | pronamespace | proowner | prolang | procost | prorows | provariadic | prosupport | prokind | prosecdef | proleakproof | proisstrict | proretset | provolatile | pro
parallel | pronargs | pronargdefaults | prorettype | proargtypes | proallargtypes | proargmodes | proargnames | proargdefaults | protrftypes |     prosrc      | probin | proconfig | proacl 
------+-----------------+--------------+----------+---------+---------+---------+-------------+------------+---------+-----------+--------------+-------------+-----------+-------------+----
---------+----------+-----------------+------------+-------------+----------------+-------------+-------------+----------------+-------------+-----------------+--------+-----------+--------
 2915 | varchartypmodin |           11 |       10 |      12 |       1 |       0 |           0 | -          | f       | f         | f            | t           | f         | i           | s  
         |        1 |               0 |         23 | 1263        |                |             |             |                |             | varchartypmodin |        |           | 
 2917 | numerictypmodin |           11 |       10 |      12 |       1 |       0 |           0 | -          | f       | f         | f            | t           | f         | i           | s  
         |        1 |               0 |         23 | 1263        |                |             |             |                |             | numerictypmodin |        |           | 
(2 rows)

  numerictypmodin和varcahrtypmodin的实现如下:

Datum
numerictypmodin(PG_FUNCTION_ARGS)
{
    ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
    int32       *tl;
    int            n;
    int32        typmod;

    tl = ArrayGetIntegerTypmods(ta, &n);

    if (n == 2)
    {
        if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("NUMERIC precision %d must be between 1 and %d",
                            tl[0], NUMERIC_MAX_PRECISION)));
        if (tl[1] < 0 || tl[1] > tl[0])
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("NUMERIC scale %d must be between 0 and precision %d",
                            tl[1], tl[0])));
        typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ;
    }
    else if (n == 1)
    {
        if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("NUMERIC precision %d must be between 1 and %d",
                            tl[0], NUMERIC_MAX_PRECISION)));
        /* scale defaults to zero */
        typmod = (tl[0] << 16) + VARHDRSZ;
    }
    else
    {
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid NUMERIC type modifier")));
        typmod = 0;                /* keep compiler quiet */
    }

    PG_RETURN_INT32(typmod);
}


Datum
varchartypmodin(PG_FUNCTION_ARGS)
{
    ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);

    PG_RETURN_INT32(anychar_typmodin(ta, "varchar"));
}

/* common code for bpchartypmodin and varchartypmodin */
static int32
anychar_typmodin(ArrayType *ta, const char *typename)
{
    int32        typmod;
    int32       *tl;
    int            n;

    tl = ArrayGetIntegerTypmods(ta, &n);

    /*
     * we're not too tense about good error message here because grammar
     * shouldn't allow wrong number of modifiers for CHAR
     */
    if (n != 1)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid type modifier")));

    if (*tl < 1)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("length for type %s must be at least 1", typename)));
    if (*tl > MaxAttrSize)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("length for type %s cannot exceed %d",
                        typename, MaxAttrSize)));

    /*
     * For largely historical reasons, the typmod is VARHDRSZ plus the number
     * of characters; there is enough client-side code that knows about that
     * that we'd better not change it.
     */
    typmod = VARHDRSZ + *tl;

    return typmod;
}

  回到开始,655366=10 << 16 | 2 + 4,也就是10,2。104 = 100 + 4,其中的4是VARHDRSZ,可变数据类型头部长度。所以每种类型都可以通过xxxtypmodin和xxxtypmodout看到内部表示。

上一篇:文本样式


下一篇:python文件操作