以如下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看到内部表示。