对scan.l的解析(二)

源码链接

https://www.gitlink.org.cn/Eao3piq4e/openGauss-server/tree/master/src%2Fcommon%2Fbackend%2Fparser%2Fscan.l

概述

        接上一篇博客对scan.l的解析(一),这篇博客我来介绍一下C代码段中一些比较重要的函数。

函数

scanner_yyerror()

函数原型为:

//代码清单1
//src/common/backend/parser/scan.l
void scanner_yyerror(const char *message, core_yyscan_t yyscanner)

        它的作用是报告词法或语法错误。该函数的第一个字符串储存了错误信息,而第二个参数在该函数中并未显式地出现,而是以作为宏函数的参数出现的,在本篇博客后面会有介绍。该函数内会使用一个全局指针变量 yylloc ,消息的游标位置是 yylloc 最后一次设置的位置,即如果在 yylex() 中调用,则为当前 token 的开始位置;如果在 base_yyparse() 中调用,那么为最新的词法 token 。对于来自 Bison 解析器的语法错误消息,这是正常的,因为 Bison 解析器在到达第一个不可解析的令牌时立即报告错误。

scanner_init()、scanner_finish()

函数原型分别为:

//代码清单2
//src/common/backend/parser/scan.l
core_yyscan_t scanner_init(const char *str,
			     core_yy_extra_type *yyext,
			     const ScanKeyword *keywords,
			     int num_keywords)
//代码清单3
//src/common/backend/parser/scan.l
void scanner_finish(core_yyscan_t yyscanner)

        在完成任何实际解析之前调用 scanner_init() ,换句话说 scanner_init() 用来初始化词法分析器;在解析完成后调用 scanner_finish() ,以在scanner_init() 之后进行清理,也就是释放内存。其中 scanner_init() 的第一个参数是指向存储了查询语句字符串的指针;第二个参数是一个core_yy_extra_type* 类型的结构体指针,它指向的内存区域储存了词法分析器允许我们传送的数据;第三个参数是一个 ScanKeyword* 类型的常量指针,指向一个存储了所有关键字的数组,第四个参数则是记录了这个数组中有效元素的个数。而 scanner_finish() 唯一的参数 yyscanner 的类型是 core_yyscan_t ,通过搜索源文件,我发现了这个类型名是在 src/include/parser/scanner.h 文件中被定义的:

//代码清单4
//src/include/parser/scanner.h
typedef void* core_yyscan_t;

这说明 yyscanner 的类型在 scan.l 外部是不透明的。另外,之前不是说 yyscanner 并未显式出现嘛,其实它是有出现的,由于在调用 scanner_init()、scanner_finish() 时如果出错会调用 ereport() 函数,这时就将 yyscanner 作为参数参送给函数 fe_rethrow() 了。

//代码清单5
//src/include/parser/scanner.h
#define ereport(a, b) fe_rethrow(yyscanner)

addlit()、addlitchar()

函数原型分别为:

//代码清单6
//src/common/backend/parser/scan.l
static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner)
//代码清单7
//src/common/backend/parser/scan.l
static void addlitchar(unsigned char ychar, core_yyscan_t yyscanner)

        这两个函数均用来向指定的缓冲区中添加文本,不同的是,可以用 addlit() 添加字符串,而用 addlitchar() 只能添加一个字符。这两个函数中的操作大同小异,我们分析一下前一个函数的源码:

//代码清单8
static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner)
{
	if ((yyextra->literallen + yleng) >= yyextra->literalalloc)
	{
		do
		{
			yyextra->literalalloc *= 2;
		} while ((yyextra->literallen + yleng) >= yyextra->literalalloc);
		yyextra->literalbuf = (char *) repalloc_huge(yyextra->literalbuf,
												yyextra->literalalloc);
	}
	memcpy(yyextra->literalbuf + yyextra->literallen, ytext, yleng);
	yyextra->literallen += yleng;
}

可以看到,addlit() 函数的第一个参数是一个字符串指针,它指向要被添加的文本,第二个参数记录了该文本的长度,第三个参数是为了使用 yyextra 。第4~12行代码是为了预防添加文本后缓冲区出现溢出现象,所以当 yyextra 记录的文本长度 literallen 和将被添加的文本的长度超过预先设置的最大长度时,要将该预先设置的长度不断翻倍直到能够容纳 ytext 指向的文本。第13行则是将 ytext 指向的文本拷贝到 literalbuf 的原文本的末尾,第14行将文本长度 literallen 更新。

getDynaParamSeq()

函数原型为:

//代码清单9
long  getDynaParamSeq(const char *string, bool initflag, bool placeholder, core_yyscan_t yyscanner)

该函数用于获取动态 SQL 的参数序列,并返回参数的序列号。它的第一个参数是一个字符指针,指向存储了所求得参数的名称,第二个参数标记操作是否为初始操作,第三个参数标记绑定参数的标志是占位符或美元引号,第四个参数用于 yyextra 。

is_trans_stmt()

函数原型为:

//代码清单10
static bool is_trans_stmt(const char *haystack, int haystack_len)

        这个函数用于判断一段 SQL 代码是不是事务语句,如果是那么返回 true ,否则返回 false 。 这个函数的第一个参数是一个字符串指针,指向一个可能储存着多条 SQL 语句的字符串,各条语句间用 '\0' 分隔,第二个参数则是所有 SQL 语句的长度加上分隔符 '\0' 数量之和。

总结

        可以看到,这些C代码段中的函数都和 scanner 有关,不论是在报错还是初始化,这些对于 scanner 都是一些很重要的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔走的月光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值