Postgresql源码(50)语法解析时关键字判定原理(函数名不能使用的关键字为例)

2022-05-26 07:57:33 浏览数 (1)

相关: 《Postgresql源码(44)server端语法解析流程分析》 《Postgresql源码(50)语法解析时关键字判定原理(函数名不能使用的关键字为例)》

关键字报错场景

关键字不出现,或出现在函数内部:创建成功

代码语言:javascript复制
CREATE OR REPLACE FUNCTION fn1(x int) RETURNS int AS $$
BEGIN
    RETURN x;
END;
$$ LANGUAGE plpgsql;
postgres=# CREATE FUNCTION

CREATE OR REPLACE FUNCTION fn2(x int) RETURNS int AS $$
DECLARE
    normalize int;
BEGIN
    RETURN x;
END;
$$ LANGUAGE plpgsql;
postgres=# CREATE FUNCTION

关键字出现在函数名、函数参数中:创建失败

代码语言:javascript复制
CREATE OR REPLACE FUNCTION fn2(normalize int) RETURNS int AS $$
BEGIN
    RETURN normalize;
END;
$$ LANGUAGE plpgsql;
postgres=# ERROR:  syntax error at or near "normalize"
postgres=# LINE 1: CREATE OR REPLACE FUNCTION fn2(normalize int) RETURNS int AS...


CREATE OR REPLACE FUNCTION normalize(x int) RETURNS int AS $$
BEGIN
    RETURN x;
END;
$$ LANGUAGE plpgsql;
postgres=# ERROR:  syntax error at or near "normalize"
postgres=# LINE 1: CREATE OR REPLACE FUNCTION fn2(normalize int) RETURNS int AS...

解析过程分析

已创建失败的函数normalize为例,分析语法解析过程

CREATE OR REPLACE FUNCTION normalize(x int) RETURNS int AS $$

调试方法参考:《Postgresql源码(44)server端语法解析流程分析》

解析过程总结:

代码语言:javascript复制
[lex]
CREATE = 352
OR = 544
REPLACE = 595
FUNCTION = 429
==========================================
[yacc]
opt_or_replace:
    OR REPLACE                { $$ = true; }
    | /*EMPTY*/               { $$ = false; }
		;
==========================================
[lex]
IDENT = 258
(
==========================================
[yacc]
type_function_name:	IDENT             { $$ = $1; }      <--------- 走这里
      | unreserved_keyword            { $$ = pstrdup($1); }
      | type_func_name_keyword        { $$ = pstrdup($1); }
      ;

func_name:	type_function_name
          { $$ = list_make1(makeString($1)); }         <--------- 走这里
      ...
		;

从下面这里开始有问题了,函数名normalize被解析成关键字了,base_yylex返回的是NORMALIZE,如果是普通函数名应该返回IDENT。

lex返回522后,yacc语法树没有匹配项了,返回错误。

代码语言:javascript复制
[lex]
NORMALIZE = 522
[yacc]
if (!yyerrstatus)
{
    yynerrs;
  yyerror (&yylloc, yyscanner, YY_("syntax error"));
  ...
}

判定原理

base_yylex调用core_yylex解析时,如果匹配到关键字,就会返回gram.c中enum yytokentype的关键字。

core_yylex需要返回它遇到的标识符类型并将其值存储在yylval中,这些标识符在gram.y中定义:

代码语言:javascript复制
gram.y

%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
	ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
  ...

这些标识符主要是给lex使用的,在lex匹配到正则规则时,返回其中一个token。

所有的关键字都在gram.y文件中使用%token表示了,这些关键字应该都不能用于 表名、列名等对象名等,可能会造成shift/reduce冲突。但其实很多也不会触发冲突,为了使用这些关键字,在gram.y文件后面专门定义了几组语法规则:

  • unreserved_keyword:可以用于任意命名场景,如果新增的关键字不会引发shift/reduce冲突,可以放在这个列表中。
  • col_name_keyword:可用于列名、表名,但不能用于函数名。
  • type_func_name_keyword:可用于函数名、类型名。
  • reserved_keyword:只能用于列别名(例如:select name as all from tbl;)
  • bare_label_keyword:只能用于列名,但可以省略as(例如:select name all from tbl;)
代码语言:javascript复制
unreserved_keyword:
			  ABORT_P
			| ABSOLUTE_P
			| ACCESS
			| ACTION
			| ADD_P
			...

col_name_keyword:
			  BETWEEN
			| BIGINT
			| BIT
			| BOOLEAN_P
			| CHAR_P
			| CHARACTER
      ...

type_func_name_keyword:
			  AUTHORIZATION
			| BINARY
			| COLLATION
			| CONCURRENTLY
			| CROSS
			...

reserved_keyword:
			  ALL
			| ANALYSE
			| ANALYZE
			| AND
			| ANY
			| ARRAY
		  ...

bare_label_keyword:
			  ABORT_P
			| ABSOLUTE_P
			| ACCESS
			| ACTION
			| ADD_P
			| ADMIN
			| AFTER
			...

kwlist.h 的增加方法

创建新关键字时需要在kwlist.h中增加PG_KEYWORD。

增加方法:先确定新增关键字会不会造成语法冲突歧义等,加到上面5个list中,然后根据能否用于表名、列名、as等场景,在kwlist中增加即可。

代码语言:javascript复制
/* name, value, category, is-bare-label */
PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD, BARE_LABEL)
...

0 人点赞