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请自行研究,在这里不再详细说明&…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...
