KaiwuDB 支持多种不同类型的 SQL 语句,如 create、insert 等。 本文介绍在 KaiwuDB SQL 解析器(以下简称解析器)中添加新语句的流程及其实现。 我们将了解如何使用 goyacc 工具更新解析器,以及执行器和查询计划器如何协同工作来执行此语句。
添加新的 SQL 语句首先要向 SQL 解析器添加必要的语法。 解析器由 goyacc 生成,goyacc 是流行的 yacc 编译器的 go 版本。 语法定义位于 pkg sql parser sql 中y 文件。 解析器的输出是一个抽象语法树 (AST),其中节点类型在 pkg SQL SEM 树目录中的各种文件中定义。
向 SQL 解析器添加新语句有三个主要组件:添加新关键字、向解析器添加语法以及添加新的语法节点类型。
在此示例中,我们将使用 KaiwuDB 中的一个新语句:frobnicate。 此语句将随机修改数据库的设置。 它将有三个选项:frobnicate cluster,用于操作集群设置;frobnicate session,用于操作会话设置;frobnicate all,用于同时处理两者。
让我们首先检查是否定义了所有关键字。 打开 pkg sql 解析器 sqlY 文件和搜索"ordinary key words"。您将看到一系列按字母顺序排列的标记定义。 由于其他语法已经定义了会话、集群和所有关键字,因此我们不需要添加它们,但我们确实需要为 frobnicate 创建一个关键字。 它应如下所示:
%token frobnicate这告诉词法分析器识别关键字,但我们仍然需要将其添加到其中一个类别列表中。 如果关键字可以出现在标识符位置,则必须保留该关键字(这要求必须将其引在引号中才能用于其他用途,例如作为列名)。 由于我们的新关键字用作 SQL 语句的开头,因此它不会被误认为是标识符,因此我们可以安全地将其添加到非保留关键字列表中。 在 pkg sql 解析器 sql 中yfile,搜索 unreserved 关键字: 并添加 |,如下所示 frobnicate:
unreserved_keyword:
frobnicate
现在词法分析器知道了我们所有的关键字,我们需要教解析器如何处理我们的新语句。 我们需要在三个地方添加引用:语句类型列表、语句大小写列表和解析子句。
在语法文件 (pkg sql parser sqly),您将找到类型列表。添加一行关于我们的新语句类型,如下所示:
%type frobnicate_stmt因此,我们将创建一个新的语句类型"frobnicatestmt"添加了类型声明。 请注意"frobnicatestmt"只是一个示例名称,您可以根据自己的实际情况进行自定义。
接下来,我们需要将新的语句类型添加到语句大小写的列表中。 继续搜索语法文件并找到它"stmt"(例如 stmt select、stmt insert 等)。 将以下情况添加到这些规则中:
stmt:
frobnicate_stmt // extend with help: frobnicate
最后,我们需要在语句中添加一个生成规则。 在 pkg sql 解析器 sql 中Y 文件:
frobnicate_stmt:
frobnicate cluster
frobnicate session
frobnicate all
以下是我们允许的三个表达式的列表,用垂直字符分隔。 每个生成器还有一个用大括号括起来的实现(它暂时报告错误并显示“未实现”错误消息)。
最后,将帮助文档添加到我们的声明中。 在我们刚刚添加的生成规则上方,添加以下注释:
// %help: frobnicate - twiddle the various settings
category: misc
text: frobnicate
现在,我们的解析器将能够识别新的句子类型,并生成一些关于新语法的注释,以帮助用户。 重新编译后,尝试执行此语句,得到如下结果:
$ kwbase sql --insecure -e "frobnicate cluster"
error: at or near "cluster": syntax error: unimplemented: this syntax
sqlstate: 0a000
detail: source sql:
frobnicate cluster
这意味着我们的新语法已成功解析,但我们无法执行任何操作,因为它尚未实现。
现在添加了语法层,我们需要为新语句提供适当的语义。 我们需要一个 ast 来将语句的结构从解析器传递到运行时。 如上所述,我们的语句是 %type,这意味着它需要实现 tree语句接口,可以在 PKG SQL SEM 树 stmt 中找到去。
我们需要编写四个函数:三个用于 Statement 接口本身(StatementReturnType、StatementType 和 StatementTag),一个用于 NodeFormatter(Format) 和标准 FMTstringer。
请为我们的语句类型创建一个新文件:pkg sql sem tree frobnicatego。在其中,输入我们 AST 节点的格式和定义。
package tree
type frobnicate struct
type frobnicatemode int
const (
frobnicatemodeall frobnicatemode = iota
frobnicatemodecluster
frobnicatemodesession
func (node *frobnicate) format(ctx *fmtctx)
要向 ast 树添加语句和字符串表示形式,请打开 pkg sql sem tree stmt转到文件并搜索 StatementReturnType 实现 Statement 接口。 现在,您可以看到不同类型 AST 的实现列表。 按字母顺序插入以下内容:
func (node *frobnicate) statementreturntype() statementreturntype
statementtype implements the statement interface.
func (node *frobnicate) statementtype() statementtype
statementtag returns a short string identifying the type of statement.
func (node *frobnicate) statementtag() string
接下来,按字母顺序添加以下内容:
func (n *frobnicate) string() string现在,我们需要更新解析器,以便在遇到语法时返回具有适当模式类型的 frobnicate 节点 (AST)。 返回 pkg sql 解析器 sqly 文件,搜索 %help: frobnicate,并将语句替换为以下内容:
frobnicate_stmt:
frobnicate cluster }
frobnicate session }
frobnicate all }
特殊符号 $$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 *tree.frobnicate:
return p.frobnicate(ctx, n)
同样,在同一文件中 pkg sql 不透明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 客户端。 我们只需要添加上面的接口函数,就可以使语句生效。