前文 《Postgresql源码(41)plpgsql函数编译执行流程分析》 《Postgresql源码(46)plpgsql中的变量类型及对应关系》
plpgsql中的变量类型及对应关系
总结
1、PLpgSQL_datum.dtype共有5中类型,其中2中类型属于通用类型,覆盖pg_type中所有类型:由plpgsql_build_variable函数根据pg_type中查到的类型决定(对应关系见下表中的PLPGSQL_DTYPE_VAR、PLPGSQL_DTYPE_REC)
2、后四种类型单独应用于3种特殊场景(类行数据、列数据、触发器数据)
PLpgSQL_datum.dtype对应关系总结
代码语言:javascript复制PLpgSQL_datum.dtype < PLpgSQL_type.ttype < Form_pg_type.typtype(pg_type)
----------------------<-----------------------<------------------------
(plpgsql_build_variable只能构造PLPGSQL_DTYPE_VAR、PLPGSQL_DTYPE_REC两种类型)
PLPGSQL_DTYPE_VAR < PLPGSQL_TTYPE_SCALAR < TYPTYPE_BASE : b for a base type
< TYPTYPE_ENUM : e for an enum type
< TYPTYPE_RANGE : r for a range type
< TYPTYPE_MULTIRANGE : m for a multirange type
< TYPTYPE_DOMAIN : d for a domain
PLPGSQL_DTYPE_REC < PLPGSQL_TTYPE_REC < TYPTYPE_COMPOSITE : c for a composite type
< TYPTYPE_DOMAIN
< TYPTYPE_PSEUDO : p for a pseudo-type
< PLPGSQL_TTYPE_PSEUDO < TYPTYPE_PSEUDO
(下面三种类型不在plpgsql_build_variable中构造)
PLPGSQL_DTYPE_ROW
(PLpgSQL_row)
场景一:游标的arguments
例如:curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
场景二:超过一个out参数时,把所有的out参数拼成一个row加入datum中
场景三:for loop的targetlist
场景四:into多个变量
PLPGSQL_DTYPE_RECFIELD
场景一:用于record类型中的某一列
PLPGSQL_DTYPE_PROMISE
场景一:trigger会默认创建一些变量用于记录触发中间态,例如tg_name、tg_when、tg_level、tg_op等,类型都是PLPGSQL_DTYPE_PROMISE
梳理过程
例如这样的几个变量
代码语言:javascript复制DECLARE
row1 tf1%ROWTYPE;
row2 tf1%ROWTYPE;
txt1 text;
x1 int[];
n1 int := 1;
我们看到的变量类型是tf1%ROWTYPE
、text
等,那么在内部编译执行中,类型会被归为几类PLpgSQL_datum_type
(编译过程参考这篇:https://blog.csdn.net/jackgo73/article/details/123923201)
编译过程会把所有的变量存入plpgsql_Datums数组,例如当前例子中dtype就是PLpgSQL_datum_type类型
代码语言:javascript复制 新增的plpgsql_Datums、ns_top
p *((PLpgSQL_var*)plpgsql_Datums[3])
$19 = {
dtype = PLPGSQL_DTYPE_REC, dno = 3, refname = 0x2f360e8 "row1",
lineno = 3, isconst = false, notnull = false, default_val = 0x0,
datatype = 0x2f36018, cursor_explicit_expr = 0xffffffff00004004,
cursor_explicit_argrow = 0, cursor_options = 0, value = 9187201950435737470,
isnull = 8, freeval = false, promise = PLPGSQL_PROMISE_NONE}
p *((PLpgSQL_var*)plpgsql_Datums[4])
$20 = {
dtype = PLPGSQL_DTYPE_REC, dno = 4, refname = 0x2f36320 "row2",
lineno = 4, isconst = false, notnull = false, default_val = 0x0,
datatype = 0x2f36250, cursor_explicit_expr = 0xffffffff00004004,
cursor_explicit_argrow = 0, cursor_options = 0, value = 9187201950435737470,
isnull = 8, freeval = false, promise = PLPGSQL_PROMISE_NONE}
p *((PLpgSQL_var*)plpgsql_Datums[5])
$21 = {
dtype = PLPGSQL_DTYPE_VAR, dno = 5, refname = 0x2f2d268 "txt1",
lineno = 5, isconst = false, notnull = false, default_val = 0x0,
datatype = 0x2f2d158, cursor_explicit_expr = 0x0,
cursor_explicit_argrow = 0, cursor_options = 0, value = 0, isnull = true,
freeval = false, promise = PLPGSQL_PROMISE_NONE}
p *((PLpgSQL_var*)plpgsql_Datums[6])
$22 = {
dtype = PLPGSQL_DTYPE_VAR, dno = 6, refname = 0x2f2daa0 "x1",
lineno = 6, isconst = false, notnull = false, default_val = 0x0,
datatype = 0x2f2d990, cursor_explicit_expr = 0x0,
cursor_explicit_argrow = 0, cursor_options = 0, value = 0, isnull = true,
freeval = false, promise = PLPGSQL_PROMISE_NONE}
p *((PLpgSQL_var*)plpgsql_Datums[7])
$23 = {
dtype = PLPGSQL_DTYPE_VAR, dno = 7, refname = 0x2f2e460 "n1",
lineno = 7, isconst = false, notnull = false, default_val = 0x2f2e310,
datatype = 0x2f2e298, cursor_explicit_expr = 0x0,
cursor_explicit_argrow = 0, cursor_options = 0, value = 0,
isnull = true, freeval = false, promise = PLPGSQL_PROMISE_NONE}
===
p *ns_top
$25 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 7, prev = 0x2f2dac0, name = 0x2f2e490 "n1"}
$27 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 6, prev = 0x2f366e8, name = 0x2f2dad0 "x1"}
$28 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 5, prev = 0x2f36340, name = 0x2f366f8 "txt1"}
$29 = {itemtype = PLPGSQL_NSTYPE_REC, itemno = 4, prev = 0x2f36108, name = 0x2f36350 "row2"}
$30 = {itemtype = PLPGSQL_NSTYPE_REC, itemno = 3, prev = 0x2f35ed0, name = 0x2f36118 "row1"}
$31 = {itemtype = PLPGSQL_NSTYPE_LABEL, itemno = 0, prev = 0x2f35e98, name = 0x2f35ee0 ""}
$32 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 2, prev = 0x2f35d30, name = 0x2f35ea8 "found"}
$33 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 1, prev = 0x2f35cf8, name = 0x2f35d40 "x3"}
$34 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 1, prev = 0x2f35b90, name = 0x2f35d08 "$2"}
$35 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 0, prev = 0x2f35b58, name = 0x2f35ba0 "p3"}
$36 = {itemtype = PLPGSQL_NSTYPE_VAR, itemno = 0, prev = 0x2f35a88, name = 0x2f35b68 "$1"}
$37 = {itemtype = PLPGSQL_NSTYPE_LABEL, itemno = 0, prev = 0x0, name = 0x2f35a98 "tfun1"}
在实际处理中,会把所有的变量都归为几类,就是上面看到的dtype:
代码语言:javascript复制/*
* Datum array node types
*/
typedef enum PLpgSQL_datum_type
{
PLPGSQL_DTYPE_VAR,
PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_PROMISE
} PLpgSQL_datum_type;
那么什么样的类型会分配到什么样的TYPE?
从一个datum构造plpgsql_build_variable说起:
代码语言:javascript复制PLpgSQL_variable *
plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
bool add2namespace)
{
PLpgSQL_variable *result;
switch (dtype->ttype)
{
case PLPGSQL_TTYPE_SCALAR:
{
/* Ordinary scalar datatype */
PLpgSQL_var *var;
var = palloc0(sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR;
var->refname = pstrdup(refname);
var->lineno = lineno;
var->datatype = dtype;
/* other fields are left as 0, might be changed by caller */
/* preset to NULL */
var->value = 0;
var->isnull = true;
var->freeval = false;
plpgsql_adddatum((PLpgSQL_datum *) var);
if (add2namespace)
plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR,
var->dno,
refname);
result = (PLpgSQL_variable *) var;
break;
}
case PLPGSQL_TTYPE_REC:
{
/* Composite type -- build a record variable */
PLpgSQL_rec *rec;
rec = plpgsql_build_record(refname, lineno,
dtype, dtype->typoid,
add2namespace);
result = (PLpgSQL_variable *) rec;
break;
}
case PLPGSQL_TTYPE_PSEUDO:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("variable "%s" has pseudo-type %s",
refname, format_type_be(dtype->typoid))));
result = NULL; /* keep compiler quiet */
break;
default:
elog(ERROR, "unrecognized ttype: %d", dtype->ttype);
result = NULL; /* keep compiler quiet */
break;
}
return result;
}
plpgsql_build_variable会使用当前变量类型的dtype字段决定该变量的dtype,有如下对应关系
代码语言:javascript复制PLpgSQL_datum.dtype < PLpgSQL_type.ttype
----------------------<--------------------
(plpgsql_build_variable)
PLPGSQL_DTYPE_VAR < PLPGSQL_TTYPE_SCALAR
PLPGSQL_DTYPE_REC < PLPGSQL_TTYPE_REC
< PLPGSQL_TTYPE_PSEUDO
类型的ttype在plpgsql_build_datatype函数中构造,有pg_type系统表中对应类型的typtype列的值决定
代码语言:javascript复制PLpgSQL_type *
plpgsql_build_datatype(Oid typeOid, int32 typmod,
Oid collation, TypeName *origtypname)
{
HeapTuple typeTup;
PLpgSQL_type *typ;
typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup failed for type %u", typeOid);
typ = build_datatype(typeTup, typmod, collation, origtypname);
ReleaseSysCache(typeTup);
return typ;
}
static PLpgSQL_type *
build_datatype(HeapTuple typeTup, int32 typmod,
Oid collation, TypeName *origtypname)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
PLpgSQL_type *typ;
if (!typeStruct->typisdefined)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type "%s" is only a shell",
NameStr(typeStruct->typname))));
typ = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type));
typ->typname = pstrdup(NameStr(typeStruct->typname));
typ->typoid = typeStruct->oid;
switch (typeStruct->typtype)
{
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
typ->ttype = PLPGSQL_TTYPE_REC;
break;
case TYPTYPE_DOMAIN:
if (type_is_rowtype(typeStruct->typbasetype))
typ->ttype = PLPGSQL_TTYPE_REC;
else
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_PSEUDO:
if (typ->typoid == RECORDOID)
typ->ttype = PLPGSQL_TTYPE_REC;
else
typ->ttype = PLPGSQL_TTYPE_PSEUDO;
break;
default:
elog(ERROR, "unrecognized typtype: %d",
(int) typeStruct->typtype);
break;
}
typ->typlen = typeStruct->typlen;
typ->typbyval = typeStruct->typbyval;
typ->typtype = typeStruct->typtype;
typ->collation = typeStruct->typcollation;
if (OidIsValid(collation) && OidIsValid(typ->collation))
typ->collation = collation;
/* Detect if type is true array, or domain thereof */
/* NB: this is only used to decide whether to apply expand_array */
if (typeStruct->typtype == TYPTYPE_BASE)
{
/*
* This test should include what get_element_type() checks. We also
* disallow non-toastable array types (i.e. oidvector and int2vector).
*/
typ->typisarray = (IsTrueArrayType(typeStruct) &&
typeStruct->typstorage != TYPSTORAGE_PLAIN);
}
else if (typeStruct->typtype == TYPTYPE_DOMAIN)
{
/* we can short-circuit looking up base types if it's not varlena */
typ->typisarray = (typeStruct->typlen == -1 &&
typeStruct->typstorage != TYPSTORAGE_PLAIN &&
OidIsValid(get_base_element_type(typeStruct->typbasetype)));
}
else
typ->typisarray = false;
typ->atttypmod = typmod;
/*
* If it's a named composite type (or domain over one), find the typcache
* entry and record the current tupdesc ID, so we can detect changes
* (including drops). We don't currently support on-the-fly replacement
* of non-composite types, else we might want to do this for them too.
*/
if (typ->ttype == PLPGSQL_TTYPE_REC && typ->typoid != RECORDOID)
{
TypeCacheEntry *typentry;
typentry = lookup_type_cache(typ->typoid,
TYPECACHE_TUPDESC |
TYPECACHE_DOMAIN_BASE_INFO);
if (typentry->typtype == TYPTYPE_DOMAIN)
typentry = lookup_type_cache(typentry->domainBaseType,
TYPECACHE_TUPDESC);
if (typentry->tupDesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("type %s is not composite",
format_type_be(typ->typoid))));
typ->origtypname = origtypname;
typ->tcache = typentry;
typ->tupdesc_id = typentry->tupDesc_identifier;
}
else
{
typ->origtypname = NULL;
typ->tcache = NULL;
typ->tupdesc_id = 0;
}
return typ;
}
type举例
代码语言:javascript复制-- a base type
postgres=# select oid, typname, typtype, typcategory from pg_type where typtype='b';
oid | typname | typtype | typcategory
------- ---------------------------------------- --------- -------------
16 | bool | b | B
17 | bytea | b | U
18 | char | b | S
19 | name | b | S
20 | int8 | b | N
-- e for an enum type
-- r for a range type
postgres=# select oid, typname, typtype, typcategory from pg_type where typtype='r';
oid | typname | typtype | typcategory
------ ----------- --------- -------------
3904 | int4range | r | R
3906 | numrange | r | R
3908 | tsrange | r | R
3910 | tstzrange | r | R
3912 | daterange | r | R
3926 | int8range | r | R
-- m for a multirange type
postgres=# select oid, typname, typtype, typcategory from pg_type where typtype='m';
oid | typname | typtype | typcategory
------ ---------------- --------- -------------
4451 | int4multirange | m | R
4532 | nummultirange | m | R
4533 | tsmultirange | m | R
4534 | tstzmultirange | m | R
4535 | datemultirange | m | R
4536 | int8multirange | m | R
-- d for a domain
postgres=# select oid, typname, typtype, typcategory from pg_type where typtype='d';
oid | typname | typtype | typcategory
------- ----------------- --------- -------------
13540 | cardinal_number | d | N
13543 | character_data | d | S
13545 | sql_identifier | d | S
13551 | time_stamp | d | D
13553 | yes_or_no | d | S
-- c for a composite type
postgres=# select oid, typname, typtype, typcategory from pg_type where typtype='c';
oid | typname | typtype | typcategory
------- --------------------------------------- --------- -------------
71 | pg_type | c | C
75 | pg_attribute | c | C
81 | pg_proc | c | C
83 | pg_class | c | C
12001 | pg_attrdef | c | C
12003 | pg_constraint | c | C
PLpgSQL_datum.dtype对应关系总结
代码语言:javascript复制PLpgSQL_datum.dtype < PLpgSQL_type.ttype < Form_pg_type.typtype(pg_type)
----------------------<-----------------------<------------------------
(plpgsql_build_variable只能构造PLPGSQL_DTYPE_VAR、PLPGSQL_DTYPE_REC两种类型)
PLPGSQL_DTYPE_VAR < PLPGSQL_TTYPE_SCALAR < TYPTYPE_BASE : b for a base type
< TYPTYPE_ENUM : e for an enum type
< TYPTYPE_RANGE : r for a range type
< TYPTYPE_MULTIRANGE : m for a multirange type
< TYPTYPE_DOMAIN : d for a domain
PLPGSQL_DTYPE_REC < PLPGSQL_TTYPE_REC < TYPTYPE_COMPOSITE : c for a composite type
< TYPTYPE_DOMAIN
< TYPTYPE_PSEUDO : p for a pseudo-type
< PLPGSQL_TTYPE_PSEUDO < TYPTYPE_PSEUDO
(下面三种类型不在plpgsql_build_variable中构造)
PLPGSQL_DTYPE_ROW
(PLpgSQL_row)
场景一:游标的arguments
例如:curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
场景二:超过一个out参数时,把所有的out参数拼成一个row加入datum中
场景三:for loop的targetlist
场景四:into多个变量
PLPGSQL_DTYPE_RECFIELD
场景一:用于record类型中的某一列
PLPGSQL_DTYPE_PROMISE
场景一:trigger会默认创建一些变量用于记录触发中间态,例如tg_name、tg_when、tg_level、tg_op等,类型都是PLPGSQL_DTYPE_PROMISE