pg兼容mysql框架之语法解析层(openHalo开源项目解析)

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);
相关推荐
勇往直前plus2 小时前
MyBatis/MyBatis-Plus类型转换器深度解析:从基础原理到自定义实践
数据库·oracle·mybatis
cyhysr2 小时前
sql将表字段不相关的内容关联到一起
数据库·sql
九皇叔叔2 小时前
MySQL 数据库 MVCC 机制
数据库·mysql
一棵开花的树,枝芽无限靠近你2 小时前
【face-api.js】1️⃣基于Tensorflow.js的人脸识别项目开源项目
javascript·开源·tensorflow·face-api.js
此生只爱蛋2 小时前
【Redis】Set 集合
数据库·redis·缓存
bjzhang753 小时前
C#操作SQLite数据库
数据库·sqlite·c#
hans汉斯3 小时前
嵌入式操作系统技术发展趋势
大数据·数据库·物联网·rust·云计算·嵌入式实时数据库·汉斯出版社
百***07453 小时前
MiMo-V2-Flash深度拆解:国产开源大模型的技术突破与落地实践
开源
Coder_Boy_3 小时前
Spring 核心思想与企业级最佳特性(实践级)事务相关
java·数据库·spring