前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Postgresql源码(46)plpgsql中的变量类型及对应关系

Postgresql源码(46)plpgsql中的变量类型及对应关系

作者头像
mingjie
发布2022-05-18 11:00:37
1.2K0
发布2022-05-18 11:00:37
举报
文章被收录于专栏:Postgresql源码分析

前文 《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%ROWTYPEtext等,那么在内部编译执行中,类型会被归为几类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
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结
  • 梳理过程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档