相关 《Postgresql游标使用介绍(cursor)》 《Postgresql随手记(10)游标编译过程&结果记录》
随手记录游标编译后的状态,供日后查询。
这里只记录了编译过程,缺失执行过程,执行过程放在后面两大章节介绍:SPI系统 和 Portal系统。
案例
《Postgresql游标使用介绍(cursor)》中的案例:
代码语言:javascript复制drop table tf1;
create table tf1(c1 int, c2 int, c3 varchar(32), c4 varchar(32), c5 int);
insert into tf1 values(1,1000, 'China','Dalian', 23000);
insert into tf1 values(2,4000, 'Janpan', 'Tokio', 45000);
insert into tf1 values(3,1500, 'China', 'Xian', 25000);
insert into tf1 values(4,300, 'China', 'Changsha', 24000);
insert into tf1 values(5,400,'USA','New York', 35000);
insert into tf1 values(6,5000, 'USA', 'Bostom', 15000);
CREATE OR REPLACE FUNCTION tfun1() RETURNS int AS $$
DECLARE
curs1 refcursor;
curs2 CURSOR FOR SELECT c1 FROM tf1;
curs3 CURSOR (key integer) FOR SELECT * FROM tf1 WHERE c1 > key;
x int;
y tf1%ROWTYPE;
BEGIN
open curs1 FOR SELECT * FROM tf1 WHERE c1 > 3;
fetch curs1 into y; RAISE NOTICE 'curs1 : %', y.c3;
fetch curs1 into y; RAISE NOTICE 'curs1 : %', y.c3;
open curs2;
fetch curs2 into x; RAISE NOTICE 'curs2 : %', x;
fetch curs2 into x; RAISE NOTICE 'curs2 : %', x;
OPEN curs3(4); -- OPEN curs3(key := 4);
fetch curs3 into y; RAISE NOTICE 'curs3 : %', y.c4;
fetch curs3 into y; RAISE NOTICE 'curs3 : %', y.c4;
return 0;
END;
$$ LANGUAGE plpgsql;
select tfun1();
语法编译位置
pl_gram.y
代码语言:javascript复制decl_statement : decl_varname decl_const decl_datatype decl_collate decl_notnull decl_defval
{
PLpgSQL_variable *var;
/*
* If a collation is supplied, insert it into the
* datatype. We assume decl_datatype always returns
* a freshly built struct not shared with other
* variables.
*/
if (OidIsValid($4))
{
if (!OidIsValid($3->collation))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("collations are not supported by type %s",
format_type_be($3->typoid)),
parser_errposition(@4)));
$3->collation = $4;
}
var = plpgsql_build_variable($1.name, $1.lineno,
$3, true);
var->isconst = $2;
var->notnull = $5;
var->default_val = $6;
/*
* The combination of NOT NULL without an initializer
* can't work, so let's reject it at compile time.
*/
if (var->notnull && var->default_val == NULL)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("variable "%s" must have a default value, since it's declared NOT NULL",
var->refname),
parser_errposition(@5)));
}
| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
{
plpgsql_ns_additem($4->itemtype,
$4->itemno, $1.name);
}
| decl_varname opt_scrollable K_CURSOR
{ plpgsql_ns_push($1.name, PLPGSQL_LABEL_OTHER); }
decl_cursor_args decl_is_for decl_cursor_query
{
PLpgSQL_var *new;
PLpgSQL_expr *curname_def;
char buf[NAMEDATALEN * 2 64];
char *cp1;
char *cp2;
/* pop local namespace for cursor args */
plpgsql_ns_pop();
new = (PLpgSQL_var *)
plpgsql_build_variable($1.name, $1.lineno,
plpgsql_build_datatype(REFCURSOROID,
-1,
InvalidOid,
NULL),
true);
curname_def = palloc0(sizeof(PLpgSQL_expr));
/* Note: refname has been truncated to NAMEDATALEN */
cp1 = new->refname;
cp2 = buf;
/*
* Don't trust standard_conforming_strings here;
* it might change before we use the string.
*/
if (strchr(cp1, '\') != NULL)
*cp2 = ESCAPE_STRING_SYNTAX;
*cp2 = ''';
while (*cp1)
{
if (SQL_STR_DOUBLE(*cp1, true))
*cp2 = *cp1;
*cp2 = *cp1 ;
}
strcpy(cp2, "'::pg_catalog.refcursor");
curname_def->query = pstrdup(buf);
curname_def->parseMode = RAW_PARSE_PLPGSQL_EXPR;
new->default_val = curname_def;
new->cursor_explicit_expr = $7;
if ($5 == NULL)
new->cursor_explicit_argrow = -1;
else
new->cursor_explicit_argrow = $5->dno;
new->cursor_options = CURSOR_OPT_FAST_PLAN | $2;
}
;
编译结果
整理plpgsql_Datums(解析后的变量)("无"表示该类型没有这个配置项)
type | dno | refname | lineno | datatype | fieldnames | cursor_explicit_expr | cursor_explicit_argrow | cursor_options | recparentno |
---|---|---|---|---|---|---|---|---|---|
var | 0 | found | 0 | bool | 无 | 0 | 0 | 0 | 无 |
var | 1 | curs1 | 3 | refcursor | 无 | 0 | 0 | 0 | 无 |
var | 2 | curs2 | 4 | refcursor | 无 | SELECT c1 FROM tf1 | -1 | 256 | 无 |
var | 3 | key | 5 | int4 | 无 | 0 | 0 | 0 | 无 |
row | 4 | (unnamed row) | 5 | 无 | key | x | 无 | 无 | 无 |
var | 5 | curs3 | 5 | refcursor | 无 | SELECT * FROM tf1 WHERE c1 > key | 4 | 256 | 无 |
var | 6 | x | 7 | int4 | 无 | 0 | 0 | 0 | 无 |
rec | 7 | y | 8 | tf1 | 无 | 无 | 无 | 无 | 无 |
field | 8 | c3 | 7 | 无 | 无 | 无 | 无 | 无 | 7 |
row | 9 | (unnamed row) | 15 | 无 | x | 无 | 无 | 无 | 无 |
row | 10 | (unnamed row) | 16 | 无 | x | 无 | 无 | 无 | 无 |
field | 11 | c4 | 7 | 无 | 无 | 无 | 无 | 无 | 7 |
- PLpgSQL_row
- 用于存放多个参数的场景,例如上面dno=4的key变量,有可能会使用多个游标变量,这里需要统一保存到一个row中
- 需要注意的是row只是一个总集,具体的变量还会单独记录在plpgsql_Datums中
- PLpgSQL_var游标
- 游标类型变量也是使用PLpgSQL_var来保存,特别的是会启动cursor_xxx的几个变量保存信息
- 游标类型的cursor_options用于记录游标的行为模式:使用下面的标志位
#define CURSOR_OPT_BINARY 0x0001 /* BINARY */
#define CURSOR_OPT_SCROLL 0x0002 /* SCROLL explicitly given */
#define CURSOR_OPT_NO_SCROLL 0x0004 /* NO SCROLL explicitly given */
#define CURSOR_OPT_INSENSITIVE 0x0008 /* INSENSITIVE */
#define CURSOR_OPT_ASENSITIVE 0x0010 /* ASENSITIVE */
#define CURSOR_OPT_HOLD 0x0020 /* WITH HOLD */
/* these planner-control flags do not correspond to any SQL grammar: */
#define CURSOR_OPT_FAST_PLAN 0x0100 /* prefer fast-start plan */
#define CURSOR_OPT_GENERIC_PLAN 0x0200 /* force use of generic plan */
#define CURSOR_OPT_CUSTOM_PLAN 0x0400 /* force use of custom plan */
#define CURSOR_OPT_PARALLEL_OK 0x0800 /* parallel mode OK */
- PLpgSQL_rec
- PLpgSQL_rec和PLpgSQL_recfield组合使用,用于记录形如
y tf1%ROWTYPE;
的变量类型 - PLpgSQL_rec对应一个ROWTYPE变量,PLpgSQL_recfield每一个都对应行类型的其中一个列,例如:
y.c4
- PLpgSQL_rec和PLpgSQL_recfield组合使用,用于记录形如
gdb命令
p *((PLpgSQL_var*)plpgsql_Datums[0])
p *((PLpgSQL_var*)plpgsql_Datums[1])
p *((PLpgSQL_var*)plpgsql_Datums[2])
p *((PLpgSQL_var*)plpgsql_Datums[3])
p *((PLpgSQL_row*)plpgsql_Datums[4])
p *((PLpgSQL_var*)plpgsql_Datums[5])
p *((PLpgSQL_var*)plpgsql_Datums[6])
p *((PLpgSQL_rec*)plpgsql_Datums[7])
p *((PLpgSQL_recfield*)plpgsql_Datums[8])
p *((PLpgSQL_row*)plpgsql_Datums[9])
p *((PLpgSQL_row*)plpgsql_Datums[10])
p *((PLpgSQL_recfield*)plpgsql_Datums[11])