SQL 解析 — 如何轻松实现新增语句
KaiwuDB 支持多种不同类型的 SQL 语句,例如 create、insert 等。本文将介绍在 KaiwuDB SQL Parser(下文统称解析器)中添加新语句的过程及其实现。我们将了解如何使用 goyacc 工具更新解析器,以及执行器和查询计划器(planner)如何协同执行这条语句。
一、语法和关键字
添加新的 SQL 语句需要从向 SQL 解析器添加必要的语法开始。解析器是通过 goyacc 生成的,它是流行的 yacc 编译器的 Go 版本。语法定义位于 pkg/sql/parser/sql.y 文件中。解析器的输出是一个抽象语法树(AST),其中的节点类型(node)在 pkg/sql/sem/tree 目录下的各个文件中定义。
向 SQL 解析器添加新的语句主要由三个组成部分:添加新的关键字、向语句解析器添加语法,以及添加新的语法节点类型。
二、FROBNICATE STATEMENT
本文以在 KaiwuDB 新增一个新的语句为例:FROBNICATE。这个语句将会随机修改数据库的设置。它将有三个选项:FROBNICATE CLUSTER,用于操作集群设置;FROBNICATE SESSION,用于操作会话设置;FROBNICATE ALL,用于同时处理两者。
让我们从检查所有关键字是否已定义开始。打开 pkg/sql/parser/sql.y 文件并搜索"Ordinary key words"。你会看到一系列按字母顺序排列的标记定义。由于其他语法已经定义了 SESSION、CLUSTER 和 ALL 关键字,我们不需要添加它们,但我们需要为 FROBNICATE 创建一个关键字。它应该如下所示:
%token <str> FROBNICATE
这告诉词法分析器识别关键字,但我们仍然需要将它添加到其中一个类别列表中。如果关键字可以出现在标识符位置,它必须是保留的(reserved_keyword, 这要求对其的其他用途,例如作为列名,必须使用引号引用)。由于我们的新关键字作为一条 SQL 语句的开始,它不能被误认为是标识符,所以我们可以安全地将它添加到非保留关键字列表中。在 pkg/sql/parser/sql.y 文件中搜索 unreserved_keyword:,并按照以下方式添加 | FROBNICATE:
unreserved_keyword:
...
| FROBNICATE
...
既然词法分析器已经知道了我们所有的关键字,我们需要教会解析器如何处理我们的新语句。有三个地方我们需要添加引用:语句类型列表、语句情况列表和解析子句。
在语法文件(pkg/sql/parser/sql.y)中搜索<tree.Statement>,你将找到类型列表。添加一行关于我们新语句类型的内容,类似于:
%type <tree.Statement> frobnicate_stmt
这样我们就为新语句类型"frobnicateStmt"添加了一个类型声明。请注意,"frobnicateStmt"只是一个示例名称,你可以根据实际情况自定义。
接下来,我们需要将新语句类型添加到语句情况列表中。继续搜索语法文件,找到以"stmt"开头的规则(如 stmt_select、stmt_insert 等)。在这些规则中添加以下情况:
stmt:
...
| frobnicate_stmt // EXTEND WITH HELP: FROBNICATE
...
最后,我们需要为我们的语句添加一个产生式规则。在 pkg/sql/parser/sql.y 文件中添加以下规则:
frobnicate_stmt:FROBNICATE CLUSTER { return unimplemented(sqllex, "frobnicate cluster") }
| FROBNICATE SESSION { return unimplemented(sqllex, "frobnicate session") }
| FROBNICATE ALL { return unimplemented(sqllex, "frobnicate all") }
这里列出了我们允许的三种表达形式,用竖线字符分隔。每个产生式还有一个用大括号括起来的实现(暂时报错并显示“未实现”的错误信息)。
最后为我们的语句添加 help 文档。在我们刚刚添加的产生式规则上方,添加以下注释:
// %Help: FROBNICATE - twiddle the various settings
// %Category: Misc
// %Text: FROBNICATE { CLUSTER | SESSION | ALL }
现在我们的解析器将能够识别新的语句类型,并且生成一些新语法相关的注释以为用户提供帮助。重新编译代码后,尝试执行这条语句,得到以下结果:
$ kwbase sql --insecure -e "frobnicate cluster"
ERROR: at or near "cluster": syntax error: unimplemented: this syntax
SQLSTATE: 0A000
DETAIL: source SQL:
frobnicate cluster^HINT: You have attempted to use a feature that is not yet implemented.Please check the public issue tracker to check whether this problem is
already tracked. If you cannot find it there, please report the error
with details by creating a new issue.If you would rather not post publicly, please contact us directly
using the support form.We appreciate your feedback.
Failed running "sql"
这代表我们新加的语法成功解析了,但是由于尚未实现,无法执行任何操作。
三、添加抽象语法树
语法层添加完成后,现在我们需要为新的语句赋予适当的语义。我们需要一个 AST 来从解析器向运行时传递语句的结构。上文说过我们的语句是 %type <tree.Statement>,这意味着它需要实现 tree.Statement接口,可以在 pkg/sql/sem/tree/stmt.go 中找到。
我们需要编写四个函数:三个用于 Statement 接口本身(StatementReturnType、StatementType 和 StatementTag),一个用于 NodeFormatter(Format),还有标准的 fmt.Stringer。
请为我们的语句类型创建一个新文件:pkg/sql/sem/tree/frobnicate.go。在其中,放入我们 AST 节点的格式和定义。
package treetype Frobnicate struct {Mode FrobnicateMode
}var _ Statement = &Frobnicate{}type FrobnicateMode intconst (FrobnicateModeAll FrobnicateMode = iotaFrobnicateModeClusterFrobnicateModeSession
)func (node *Frobnicate) Format(ctx *FmtCtx) {ctx.WriteString("FROBNICATE ")switch node.Mode {case FrobnicateModeAll:ctx.WriteString("ALL")case FrobnicateModeCluster:ctx.WriteString("CLUSTER")case FrobnicateModeSession:ctx.WriteString("SESSION")}
}
要添加我们 AST 树的语句和字符串表示,打开 pkg/sql/sem/tree/stmt.go 文件并搜索 // StatementReturnType implements the Statement interface。现在你可以看到不同类型 AST 的实现列表。按照字母顺序将以下内容插入其中:
func (node *Frobnicate) StatementReturnType() StatementReturnType { return Ack }// StatementType implements the Statement interface.
func (node *Frobnicate) StatementType() StatementType { return TypeDCL }// StatementTag returns a short string identifying the type of statement.
func (node *Frobnicate) StatementTag() string { return "FROBNICATE" }
接下来,按字母顺序添加以下内容:
func (n *Frobnicate) String() string { return AsString(n) }
现在我们需要更新解析器,在遇到我们的语法时返回一个带有适当模式类型 FROBNICATE 节点(AST)。返回到 pkg/sql/parser/sql.y 文件,搜索 // %Help: FROBNICATE,并将语句替换为以下内容:
frobnicate_stmt:FROBNICATE CLUSTER { $$.val = &tree.Frobnicate{Mode: tree.FrobnicateModeCluster} }
| FROBNICATE SESSION { $$.val = &tree.Frobnicate{Mode: tree.FrobnicateModeSession} }
| FROBNICATE ALL { $$.val = &tree.Frobnicate{Mode: tree.FrobnicateModeAll} }
特殊符号 $$.val 表示此规则生成的节点值。还有一些其他的 $ 符号,可以在 yacc 中使用。其中一个更有用的形式是引用子产生式的节点值(例如,在这三个语句中,$1 将是标记 FROBNICATE)。
接下来重新编译 KaiwuDB,重新输入新语法,得到以下结果:
$ kwbase sql --insecure -e "frobnicate cluster"
Error: pq: unknown statement type: *tree.Frobnicate
Failed running "sql"
现在我们看到了一个不同于之前的错误。这个错误来自于 SQL 计划器(planner),在遇到新的语句类型时不知道该怎么处理。我们需要教给它新的语句的含义。尽管我们的语句不会在任何查询计划中起作用,但我们将通过向规划器添加一个方法来实现它。这是集中化语句派发的地方,因此在那里添加语义。
找到我们当前看到的错误的源代码,会发现它在 /pkg/sql/opaque.go 文件中一长串类型选择语句的末尾。让我们在其中添加一个 case:
case *tree.Frobnicate:return p.Frobnicate(ctx, n)
同样,在同一文件 /pkg/sql/opaque.go 的 init() 函数下面添加以下内容:
&tree.Frobnicate{},
这将调用计划器自身上的一个方法(尚未实现)。让我们在 pkg/sql/frobnicate.go 文件中实现该方法。
package sql
import ("context""github.com/kwbasedb/kwbase/pkg/sql/sem/tree""github.com/kwbasedb/errors"
)func (p *planner) Frobnicate(ctx context.Context, stmt *tree.Frobnicate) (planNode, error) {return nil, errors.AssertionFailedf("We're not quite frobnicating yet...")
}
此时重新编译 KaiwuDB,再次执行该语句:
$ kwbase sql --insecure -e "frobnicate cluster"
Error: pq: We're not quite frobnicating yet...
Failed running "sql"
至此为止,我们已经能够让错误传递到 SQL 客户端了。我们只需要上述接口添加功能能代码,让语句生效即可。
相关文章:
SQL 解析 — 如何轻松实现新增语句
KaiwuDB 支持多种不同类型的 SQL 语句,例如 create、insert 等。本文将介绍在 KaiwuDB SQL Parser(下文统称解析器)中添加新语句的过程及其实现。我们将了解如何使用 goyacc 工具更新解析器,以及执行器和查询计划器(pl…...

Android集成OpenSSL实现加解密-集成
导入so 将编译生成的 OpenSSL 动态库文件(.so 文件)复制到你的 Android 项目的 libs 目录中 导入头文件 将编译生成的include文件夹导入到项目中 build.gradle添加配置 defaultConfig {……testInstrumentationRunner "androidx.test.runner…...
代码随想录算法训练营Day18|513.找树左下角的值、112. 路径总和、113. 路径总和ii、106.从中序与后序遍历序列构造二叉树
目录 513.找树左下角的值 前言 层序遍历 递归法 112. 路径总和 前言 递归法 113. 路径总和ii 前言 递归法 106.从中序与后序遍历序列构造二叉树 前言 思路 递归法 总结 513.找树左下角的值 题目链接 文章链接 前言 本题要求得到二叉树最后一行最左边的值…...
【蓝桥备赛】技能升级——二分查找
题目链接 技能升级 个人思路 需要给n个技能添加技能点,无论技能点加成如何衰减,每次始终都是选择当前技能加点加成最高的那一项技能,所以最后一次的加点一定也是加在当时技能攻击加成最高的那个。此时,我们去寻找最后一次的加点…...
zyqn-arm软中断设置
所有SGI都是边缘触发的,sgi的灵敏度类型是固定的,不能改变。 软中断初始化流程 1、初始化异常处理 2、初始化中断控制器 3、注册异常处理回调函数到CPU 4、连接软中断信号与注册软中断回调函数 5、使能中断控制器中的软中断中断 6、使能异常处理 …...

k8s---pod基础下
k8s的pod与docker重启策略的区别 k8s的重启策略 always deployment的yaml文件只能是always,pod的yaml三种模式都可以。不论正常退出还是非正常退出都重启。OnFailure:正常退出不重启,非正常退出会重启Never:正常退出和非正常退出…...

玩转朋友圈!这样运营朋友圈吸睛又吸金!
朋友圈已成为现代社交媒体中不可或缺的平台,并且有很大的潜力用于营销和推广。那么如何才能让朋友圈在众多用户中脱颖而出,吸引眼球并提升商业效益呢?主要从以下几点出发: 首先,要想吸引关注,您需要在朋友…...

react学习
目录 一、react基础 5.loadsh使用排序8.ref获取DOM对象10.props使用*13.UseEffect 二、 react使用redux三、美团外卖项目完成页面制作使用redux渲染页面使用react-router-dom评价 一、react基础 jsx 大括号的作用 {count} {userLlist.map((item)>{return <li key{item…...

vue-cli项目中vue.config.js的配置
vue-cli项目中vue.config.js的配置 一、直接上代码 一、直接上代码 let path require(path) let glob require(glob)function resolve(dir) {return path.join(__dirname, src/${dir}) }module.exports {pages: {index: {// page 的入口entry: src/main.js,// 模板来源temp…...

Github 2024-01-04 开源项目日报 Top10
根据Github Trendings的统计,今日(2024-01-04统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目3C项目2TypeScript项目2Java项目2Jupyter Notebook项目1Go项目1 系统设计指南 创建周期ÿ…...

使用GPTs+Actions自动获取第三方数据
目录 安装插件与GPT对话联网插件首先,创建GPTs。 Voxscript 官网:https://voxscript.awt.icu/index.htmlOpenAI Schema:https://voxscript.awt.icu/swagger/v1/swagger.yamlServer URL: servers: url: https://voxscript.awt.icu安装插件 要使用这个插件&...

git提交操作(不包含初始化仓库)
1.进入到本地的git仓库 查看状态 git status 如果你之前有没有成功的提交,直接看第5步。 2.追踪文件 git add . 不要提交大于100M的文件,如果有,看第5步 3.提交评论 git commit -m "你想添加的评论" 4.push (push之前可以再…...

使用YOLOv8和Grad-CAM技术生成图像热图
目录 yolov8导航 YOLOv8(附带各种任务详细说明链接) 概述 环境准备 代码解读 导入库 定义letterbox函数 调整尺寸和比例 计算填充 应用填充 yolov8_heatmap类定义和初始化 后处理函数 绘制检测结果 类的调用函数 热图生成细节 参数解释 we…...

Vue: 多个el-select不能重复选择相同属性
一、场景 1.需求: 用户可自由选择需要修改的对象并同时修改多个属性,需要校验修改对象不能重复选择,但是可供修改属性是固定的 2.目标效果: 二、实现 1.主要代码: <template><el-selectv-model"se…...

金色麦芒的2023
2023年即将过去,回首这一年,我深感自己在技术和职业生涯中取得了巨大的进步。这一年里,我不仅在技术层面有了更深入的掌握,也在个人成长和职业规划上有了更明确的方向。 首先,在技术层面,我今年最大的收获是…...

java设计模式学习之【策略模式】
文章目录 引言策略模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用计算示例代码地址 引言 设想你正在玩一个策略游戏,每一个决策都会导致不同的游戏结局。同样地,在软件开发中,我们常常需要根据不同的场景或条件选择不同…...
Mybatis SQL构建器类 - SqlBuilder and SelectBuilder (已经废弃)
在3.2版本之前,我们采用了一种略有不同的方法,通过利用ThreadLocal变量来掩盖一些使Java DSL有点繁琐的语言限制。然而,这种方法现在已被弃用,因为现代框架已经普及了使用构建器模式和匿名内部类的概念。因此,SelectBu…...
【Linux】不常用命令记录
查看开启的网络端口 1、使用netstat命令“netstat -tuln”,该命令将显示所有当前监听的TCP和UDP端口; 2、使用ss命令“ss -tuln”,用于显示当前监听的TCP和UDP端口; 3、使用lsof命令“lsof -i”,将显示当前打开的网络…...
【docker】安装docker环境并启动容器
一、安装docker 这里以centos系统为例安装docker环境 # 删除已有安装包 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-enginesudo yum install -y yum-utils # 设置源 y…...

AIOps探索 | 基于大模型构建高效的运维知识及智能问答平台(2)案例分享
原作者:擎创科技产品专家 布博士 案例分享 所需要的软件列表 本次案例的实现,全部采用开源或SAAS的产品来提供,并不涉及到私有化部署的软件产品。软件列表如下所示,如何申请apikey请自行研究,在这里不再详细说明&…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...