pg兼容mysql框架之语法解析层
1 兼容框架
基于《pg兼容mysql框架之整体架构》篇里所讲的,语法模块用标准引擎管理。用GUC参数pg_database_mode 作为区分MySQL和PG的依据,给标准引擎赋于PG/MYSQL的执行函数。
初始化代码:
src/backend/utils/init/postinit.c 1150:
/*其他代码*/
/* Initialize Parser Engine */
InitParserEngine();
上述代码在数据库初始化阶段对语法解析引擎进行初始化。
语法解析引擎的初始化函数代码:
void
InitParserEngine(void)
{
switch (unvdb_database_mode)
{
case UNVDBTX_COMPAT_MODE:
parserengine = GetStandardParserEngine();
break;
case MYSQL_COMPAT_MODE:
if ((MyProcPort != NULL) &&
(nodeTag(MyProcPort->protocol_handler) == T_MySQLProtocol))
{
parserengine = GetMysParserEngine();
}
else
{
parserengine = GetStandardParserEngine();
}
/* parserengine = GetMysParserEngine(); */
break;
default:
parserengine = GetStandardParserEngine();
break;
}
}
通过 GetMysParserEngine();获取MySQL的语法解析引擎。
MySQL引擎赋值代码:
static const ParserRoutine mys_parser_engine = {
.type = T_ParserRoutine,
.is_standard_parser = false,
.need_standard_parser = true,
.keywordlist = &MysScanKeywords,
.keyword_tokens = MysScanKeywordTokens,
.keyword_categories = MysScanKeywordCategories,
.transformStmt = mys_transformStmt,
.transformSelectStmt = mys_transformSelectStmt,
.transformInsertStmt = mys_transformInsertStmt,
.transformUpdateStmt = mys_transformUpdateStmt,
.transformDeleteStmt = mys_transformDeleteStmt,
.transformCallStmt = mys_transformCallStmt,
.transformOptionalSelectInto = mys_transformOptionalSelectInto,
.transformSetOperationStmt = mys_transformSetOperationStmt,
.transformSetOperationTree = mys_transformSetOperationTree,
.analyze_requires_snapshot = mys_analyze_requires_snapshot,
.transformOnConflictArbiter = mys_transformOnConflictArbiter,
.raw_parser = mys_raw_parser,
.transformExpr = mys_transformExpr,
.transformGroupClause = mys_transformGroupClause,
.transformDistinctClause = mys_transformDistinctClause,
.transformCreateStmt = mys_transformCreateStmt,
.transformAlterTableStmt = mys_transformAlterTableStmt,
.ParseFuncOrColumn = mys_ParseFuncOrColumn,
.func_get_detail = mys_func_get_detail,
.make_fn_arguments = mys_make_fn_arguments
};
其中每个赋值的函数,定义在独立的mys_开头文件里:
如:
src/backend/parser/mysql/mys_parser.c 286:
static List *
mys_raw_parser(const char *str, RawParseMode mode)
{
/*代码片段*/
if (mode == RAW_PARSE_DEFAULT)
yyextra.have_lookahead = false;
else
{
/* this array is indexed by RawParseMode enum */
static const int mode_token[] = {
0, /* RAW_PARSE_DEFAULT */
MODE_TYPE_NAME, /* RAW_PARSE_TYPE_NAME */
MODE_PLPGSQL_EXPR, /* RAW_PARSE_PLPGSQL_EXPR */
MODE_PLPGSQL_ASSIGN1, /* RAW_PARSE_PLPGSQL_ASSIGN1 */
MODE_PLPGSQL_ASSIGN2, /* RAW_PARSE_PLPGSQL_ASSIGN2 */
MODE_PLPGSQL_ASSIGN3 /* RAW_PARSE_PLPGSQL_ASSIGN3 */
};
yyextra.have_lookahead = true;
yyextra.lookahead_token = mode_token[mode];
yyextra.lookahead_yylloc = 0;
yyextra.lookahead_end = NULL;
}
/*代码片段*/
return yyextra.parsetree;
}
原pg的执行函数改成标准执行函数,在标准执行函数中根据不同的模式(PG/MySQL)再选择不同的执行函数。
代码段:
Query *
transformOptionalSelectInto(ParseState *pstate, Node *parseTree)
{
Query *query;
Assert(parserengine != NULL);
if (parserengine->transformOptionalSelectInto)
query = parserengine->transformOptionalSelectInto(pstate, parseTree);
else
query = standard_transformOptionalSelectInto(pstate, parseTree);
return query;
}
通过语法解析引擎给予不同的节点两个模式的跳转处理函数。
而对于这种两种情况的代码而言,pg自身引擎只需要赋值一些关键节点,函数是可以默认的:代码如下
/*
* 定义Standard Parser Engine
*/
static const ParserRoutine standard_parser_engine = {
.type = T_ParserRoutine,
.is_standard_parser = true,
.need_standard_parser = false,
.keywordlist = &ScanKeywords,
.keyword_tokens = ScanKeywordTokens,
.keyword_categories = ScanKeywordCategories,
.raw_parser = standard_raw_parser,
.transformStmt = NULL,
.transformSelectStmt = NULL,
.transformInsertStmt = NULL,
.transformDeleteStmt = NULL,
.transformUpdateStmt = NULL,
.transformSetOperationStmt = NULL,
.transformCallStmt = NULL,
.transformOptionalSelectInto = NULL,
.transformSetOperationTree = NULL,
.transformGroupClause = NULL,
.transformDistinctClause = NULL,
.transformOnConflictArbiter = NULL,
.transformExpr = NULL,
.transformCreateStmt = NULL,
.transformAlterTableStmt = NULL
};
2 语法层解析
语法层代码:
/*
* raw_parser
* Do lexical and grammatical analysis.
*
* Returns a list of raw (un-analyzed) parse trees.
*/
List *
raw_parser(const char *str, RawParseMode mode)
{
List *raw_parsetree = NIL;
MemoryContext oldctx = CurrentMemoryContext;
if (parserengine == NULL)
parserengine = GetStandardParserEngine();
Assert(parserengine != NULL);
Assert(parserengine->raw_parser != NULL);
PG_TRY();
{
raw_parsetree = parserengine->raw_parser(str, mode);
}
PG_CATCH();
{
if (raw_parsetree == NIL && parserengine->is_standard_parser == false
&& (standard_parserengine_auxiliary == STANDARDARD_PARSERENGINE_AUXILIARY_ON
&& parserengine->need_standard_parser == true))
{
MemoryContextSwitchTo(oldctx);
FlushErrorState();
MemoryContextSwitchTo(oldctx);
raw_parsetree = GetStandardParserEngine()->raw_parser(str, mode);
}
else
PG_RE_THROW();
}
PG_END_TRY();
return raw_parsetree;
}
执行语法解析时,根据不同的模式,获取到不同的parserengine,上面已经说过了parserengine对raw_parser分别赋值。所以上面代码中有:
if (parserengine == NULL)
parserengine = GetStandardParserEngine();
必须获取一个语法解析引擎。根据不同的raw_parser赋值情况,如果是mysql模式,那么进入mys_raw_parser执行函数。代码如下:
src/backend/parser/mysql/mys_parser.c 300行:
static List *
mys_raw_parser(const char *str, RawParseMode mode)
{
core_yyscan_t yyscanner;
mys_yy_extra_type yyextra;
int yyresult;
/* initialize the flex scanner */
yyscanner = mys_scanner_init(str, &yyextra.core_yy_extra,
&MysScanKeywords, MysScanKeywordTokens);
if (mode == RAW_PARSE_DEFAULT)
yyextra.have_lookahead = false;
else
{
/* this array is indexed by RawParseMode enum */
static const int mode_token[] = {
0, /* RAW_PARSE_DEFAULT */
MODE_TYPE_NAME, /* RAW_PARSE_TYPE_NAME */
MODE_PLPGSQL_EXPR, /* RAW_PARSE_PLPGSQL_EXPR */
MODE_PLPGSQL_ASSIGN1, /* RAW_PARSE_PLPGSQL_ASSIGN1 */
MODE_PLPGSQL_ASSIGN2, /* RAW_PARSE_PLPGSQL_ASSIGN2 */
MODE_PLPGSQL_ASSIGN3 /* RAW_PARSE_PLPGSQL_ASSIGN3 */
};
yyextra.have_lookahead = true;
yyextra.lookahead_token = mode_token[mode];
yyextra.lookahead_yylloc = 0;
yyextra.lookahead_end = NULL;
}
/* initialize the bison parser */
mys_parser_init(&yyextra);
/* Parse! */
yyresult = mys_yyparse(yyscanner);
/* Clean up (release memory) */
mys_scanner_finish(yyscanner);
if (yyresult) /* error */
return NIL;
return yyextra.parsetree;
}
上述函数被赋值到parserengine中raw_parser调用中。
上面代码中:
yyscanner = mys_scanner_init(str, &yyextra.core_yy_extra,
&MysScanKeywords, MysScanKeywordTokens);
进入了mysql自己的scan词法解析层,该文件定义了一套mysql语法所需的词法解析。
入口代码如下:
/* Parse! */
yyresult = mys_yyparse(yyscanner);
上述过程结束以后mysql语法解析已经完成。并赋值到语法树(回到pg执行逻辑)。
src/backend/tcop/unvdb.c 1083行:
/*
* Do basic parsing of the query or queries (this should be safe even if
* we are in aborted transaction state!)
*/
parsetree_list = pg_parse_query(query_string);