news 2026/9/25 2:32:17

Hyrise C++ SQL Parser 实战指南:在 C++ 项目中集成 SQL 解析能力的完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hyrise C++ SQL Parser 实战指南:在 C++ 项目中集成 SQL 解析能力的完整方案
  • 数据库
  • 向量数据库
  • 数据湖
  • 人工智能
  • RAG

【免费下载链接】deeplake

Deeplake is AI Data Runtime for Agents. It provides serverless postgres with a multimodal datalake, enabling scalable retrieval and training.

项目地址:https://gitcode.com/gh_mirrors/de/deeplake
点击查看免费下载

本篇技术指南以开源仓库中内置的第三方库HYRISE C++ SQL Parser(位于 cpp/3rd_party/sql-parser)为研究对象,讲解如何在 C++ 项目中构建该解析器、通过hsql命名空间将 SQL 字符串解析为强类型的 C++ 对象,并利用SQLParserResult与各SQLStatement子类进行后续处理。读完本文,你将掌握该解析器的完整集成流程、支持的 SQL 语法范围、已知边界限制,以及它在 Deeplake 数据库内核(cpp/deeplake_pg)等场景中作为第三方依赖被引入的工程实践。

一、项目概览:专为 C++ 打造的 SQL 解析器

HYRISE SQL Parser 是一个用 C++ 编写的 SQL 解析器,其核心目标是把一段 SQL 查询字符串解析为等价的 C++ 对象。它最初是为 Hyrise 内存数据库的集成而开发的,但设计上完全独立,可以很方便地嵌入其他 C++ 工程("can be used perfectly well in other environments as well")。

从仓库结构看,该库包含三大组成部分:

  • 词法/语法分析器(src/parser):基于 flex(flex_lexer.l)与 bison(bison_parser.y)实现,关键字表由 sql_keywords.txt 与 keywordlist_generator.py 生成维护;
  • 语句对象模型(src/sql):每种 SQL 语句对应一个结构体(如SelectStatement、CreateStatement),本质上是"持有查询数据的 struct";
  • 对外 API(src/SQLParser.h 与 src/SQLParserResult.h):提供静态解析入口与结果容器。

编译环境要求:官方 README 明确要求 gcc 5+(或 clang 5+),同时说明 gcc 4.8+ 亦可工作;docs/basic-usage.md 进一步指出已验证可工作的版本为 gcc 4.8 与 clang 3.4,更老的版本"可能可以工作或只需少量修改"(untested)。

二、构建与安装:从源码产出libsqlparser.so

该库采用 Makefile 驱动构建,流程非常简洁。进入 cpp/3rd_party/sql-parser 目录后执行:

make # 编译生成 libsqlparser.so make install # (可选,推荐)将库复制到 /usr/local/lib/ make test # 运行测试,确认一切正常

其中make test会调用 test/test.sh 驱动位于 test 下的单元测试(sql_parser.cpp、select_tests.cpp、prepare_tests.cpp、tpc_h_tests.cpp、auto_query_file_test.cpp),并复用 test/thirdparty/microtest 这一轻量测试框架。此外仓库还提供 CMake 构建支持(CMakeLists.txt),便于集成到更大的 CMake 工程中——这正是 Deeplake 的做法:在 cpp/3rd_party/CMakeLists.txt 中通过add_subdirectory(sql-parser)将其纳入整个 C++ 内核的构建体系。

安装完成后,在你的工程中只需包含一个头文件SQLParser.h(源码位于 src/SQLParser.h,安装后位于/usr/local/lib/hsql/下),并在链接阶段加上-lsqlparser。

三、基础用法:两代 API 与完整的解析流程

整个框架被包裹在命名空间hsql中。当前版本对外提供两个静态解析方法(见 src/SQLParser.h):

方法状态说明
static bool parse(const std::string& sql, SQLParserResult* result)推荐(现行 API)解析 SQL 字符串并写入调用方传入的结果对象
static bool tokenize(const std::string& sql, std::vector<int16_t>* tokens)辅助 API仅做词法切分,输出 token 序列
static bool parseSQLString(...)已废弃(Deprecated)旧版 API,已由parse()取代

⚠️重要语义:parse()的返回值只表示"词法/语法分析器内部运行没有崩溃",并不代表 SQL 本身合法。判断 SQL 是否合法必须检查result->isValid()。

3.1 现行推荐写法

以仓库自带的示例程序 example/example.cpp 为蓝本:

#include "SQLParser.h" // 唯一需要包含的头文件 #include "util/sqlhelper.h" // 可选的打印工具 int main(int argc, char* argv[]) { if (argc <= 1) { fprintf(stderr, "Usage: ./example \"SELECT * FROM test;\"\n"); return -1; } std::string query = argv[1]; // 解析给定的查询 hsql::SQLParserResult result; hsql::SQLParser::parse(query, &result); if (result.isValid()) { printf("Parsed successfully!\n"); printf("Number of statements: %lu\n", result.size()); for (auto i = 0u; i < result.size(); ++i) { // 打印语句摘要 hsql::printStatementInfo(result.getStatement(i)); } } else { fprintf(stderr, "Given string is not a valid SQL query.\n"); fprintf(stderr, "%s (L%d:%d)\n", result.errorMsg(), result.errorLine(), result.errorColumn()); } return 0; }

3.2 README 中的最小示例

README.md 给出的更精简的骨架:

#include "hsql/SQLParser.h" const std::string query = "..."; hsql::SQLParserResult result; hsql::SQLParser::parse(query, &result); if (result.isValid() && result.size() > 0) { const hsql::SQLStatement* statement = result.getStatement(0); if (statement->isType(hsql::kStmtSelect)) { const auto* select = static_cast<const hsql::SelectStatement*>(statement); /* 处理 SELECT 语句 ... */ } }

3.3SQLParserResult:结果容器与错误定位

解析结果由 src/SQLParserResult.h 定义,核心接口包括:

  • bool isValid() const/void setIsValid(bool):查询是否合法;
  • size_t size() const:解析出的语句条数(一条 SQL 字符串可含多条语句,分号分隔);
  • const SQLStatement* getStatement(size_t index) const/SQLStatement* getMutableStatement(size_t index):按索引取语句,调用方不持有所有权;
  • const std::vector<SQLStatement*>& getStatements() const与std::vector<SQLStatement*> releaseStatements():批量取出或转移所有权;
  • const char* errorMsg()、int errorLine()、int errorColumn():解析失败时定位错误消息与出错的行、列号;
  • const std::vector<Expr*>& parameters():提取查询中的参数占位符(配合 PREPARE 语句使用,见下文);
  • void reset():释放内部语句与数据。

注意所有权约定:SQLParserResult拥有其内部SQLStatement*的所有权(析构时统一释放),调用方通过getStatement访问时不要自行 delete;如需接管所有权应使用releaseStatements()。

3.4 编译链接示例

仓库在 example/Makefile 中给出了编译示例程序的完整规则:

CFLAGS = -std=c++11 -lstdc++ -Wall -I../src/ -L../ all: $(CXX) $(CFLAGS) example.cpp -o example -lsqlparser

即:-I../src/指向解析器头文件目录,-L../指向libsqlparser.so所在目录,最终以-lsqlparser链接。标准要求是 C++11(-std=c++11)。

四、语句对象模型:从SQLStatement到各子类

解析成功后,SQLParserResult中存放的是SQLStatement*列表。SQLStatement是所有语句类型的基类(定义见 src/sql/SQLStatement.h),它记录:

  • StatementType type():语句类型枚举值;
  • bool isType(StatementType type)/bool is(StatementType type):类型判断快捷方法;
  • size_t stringLength:该语句在原始 SQL 字符串中的长度;
  • std::vector<Expr*>* hints:查询提示(hints)。

StatementType枚举完整取值如下(同文件第 9-25 行):kStmtError、kStmtSelect、kStmtImport、kStmtInsert、kStmtUpdate、kStmtDelete、kStmtCreate、kStmtDrop、kStmtPrepare、kStmtExecute、kStmtExport、kStmtRename、kStmtAlter、kStmtShow、kStmtTransaction。注意文档 docs/basic-usage.md 明确提示:"部分枚举值并没有对应的语句类,因为它们尚未实现"——因此枚举与类并非一一对应。

目前已实现的语句子类,全部聚合在 src/sql/statements.h 中统一导出:

CreateStatement — CREATE 语句 DeleteStatement — DELETE 语句 DropStatement — DROP 语句 ExecuteStatement — EXECUTE 语句 ImportStatement — IMPORT 语句 PrepareStatement — PREPARE 语句 SelectStatement — SELECT 语句 UpdateStatement — UPDATE 语句 InsertStatement — INSERT 语句 ShowStatement — SHOW 语句 TransactionStatement — 事务语句 ExportStatement — EXPORT 语句 AlterStatement — ALTER 语句

每种语句类都是"持有查询数据的 struct":例如SelectStatement记录投影列、FROM 子句、WHERE 表达式、GROUP BY、ORDER BY 等;表达式统一用 src/sql/Expr.h 中的Expr树表示。想快速熟悉各属性的实际含义,官方推荐两条路径:直接阅读各语句类的头文件定义(如 SelectStatement.h),或查看工具代码 src/util/sqlhelper.cpp 中printStatementInfo的打印逻辑——它展示了如何遍历语句对象树。

五、支持的 SQL 语法范围

5.1 SELECT 语句(最完整的支持面)

根据 docs/syntax-support.md,SELECT是支持度最高的语句类型,覆盖投影、别名、JOIN、WHERE 条件、聚合与排序等常见元素:

SELECT name, city, * FROM students AS t1 JOIN students AS t2 ON t1.city = t2.city WHERE t1.grade < 2.0 AND t2.grade > 2.0 AND t1.city = 'Frohnau' ORDER BY t1.grade DESC; SELECT city, AVG(grade) AS average, MIN(grade) AS best, MAX(grade) AS worst FROM students GROUP BY city;

5.2 数据定义与修改

CREATE TABLE students ( name TEXT, student_number INTEGER, city TEXT, grade DOUBLE ); UPDATE students SET name='Max Mustermann' WHERE name = 'Ralf Mustermann'; DELETE FROM students WHERE name = 'Max Mustermann';

5.3 预处理语句(Prepared Statements)

定义与执行采用标准PREPARE ... FROM ...+EXECUTE ...语法,问号?作为参数占位符:

PREPARE select_test FROM 'SELECT * FROM customer WHERE c_name = ?;'; EXECUTE select_test('Max Mustermann');

解析后,PREPARE中的占位符参数会通过SQLParserResult::parameters()暴露出来;EXECUTE语句则记录实际传入的参数值。

更多可解析的查询样例,见仓库测试资产 test/queries/queries-good.sql(合法的解析输入)与 test/queries/queries-bad.sql(应被判定为非法的输入);test/queries/tpc-h-01.sql 至 tpc-h-22.sql 则提供了完整的 TPC-H 基准查询集,由 test/tpc_h_tests.cpp 驱动验证。同时 benchmark 目录(含 parser_benchmark.cpp)可用于解析性能基准测试。

六、已知限制与缺失功能

官方在 docs/known-limitations.md 中坦诚地列出了当前版本(以仓库内代码为准)的边界:

完全缺失的语句类型:

  • EXPLAIN
  • EXPORT(ExportStatement类虽已生成,但语法规则层面的支持有限)
  • RENAME
  • ALTER

此外,大量数据库厂商特有的语句类型不在开发路线图上,但项目欢迎以 Pull Request 形式贡献实现后合入。

其他 SQL 层面的限制:

  • 表名会忽略 schema 前缀(见语法规则table_name),这会影响到INSERT、IMPORT、DROP、DELETE等语句——即schema.table写法中 schema 部分目前不会被保留;
  • 列数据类型仅支持INT、DOUBLE、TEXT三种。

在实际集成到 Deeplake 这样的完整数据库内核时,这些限制意味着复杂的类型系统与 DDL 语法需要由上层(如 cpp/deeplake_pg 中的 PostgreSQL 侧解析)自行补齐,第三方解析器更多承担通用 SQL 语句的快速解析职责。

七、快速上手指南:三步跑通示例

结合以上内容,把整套流程串起来只需三步:

  1. 构建:在 cpp/3rd_party/sql-parser 下执行make生成libsqlparser.so;
  2. 编译示例:进入 example 目录执行make,得到example可执行文件;
  3. 运行验证:
    ./example "SELECT * FROM test;" # 期望输出:Parsed successfully! / Number of statements: 1 ./example "this is not sql" # 期望输出:Given string is not a valid SQL query. (L1:1)

若需将该库集成进自己的 CMake 工程,参考 Deeplake 的做法——在父级 CMakeLists.txt 中add_subdirectory(sql-parser)即可纳入统一构建。

八、项目治理与许可证

  • 许可证:HYRISE sql-parser 采用MIT License开源,许可证全文见仓库内 LICENSE 文件;
  • 贡献方式:发现问题或希望增强功能,可通过提交 Issue 反馈;自行实现新特性后 Fork 并提交 Pull Request,经维护者审核合入即可;
  • 开发者文档:深入参与开发前建议先阅读 docs/dev-docs.md(涵盖词法/语法生成、代码结构等内部细节)与 docs/technical_documentation.pdf(2015 年发表的原始技术论文,讨论开发细节及其在 Hyrise 数据库中的集成);
  • 文档索引:docs/README.md 汇集了全部内部文档链接(开发者文档、支持语法、已知限制、基础用法)。

总结

HYRISE C++ SQL Parser 是一个"小而美"的解析组件:单一头文件入口、hsql命名空间封装、SQLParserResult+SQLStatement子类的对象模型,配合 flex/bison 生成的词法与语法分析器,让它既能独立服务于任意 C++ 项目,也能像在 Deeplake 中一样作为第三方依赖被add_subdirectory平滑纳入大型数据库内核。开发者应重点掌握parse()与isValid()的语义区分、结果对象的所有权约定,以及官方文档明示的语法支持面与已知限制,从而在实际集成中做出正确的技术取舍。

  • 数据库
  • 向量数据库
  • 数据湖
  • 人工智能
  • RAG

【免费下载链接】deeplake

Deeplake is AI Data Runtime for Agents. It provides serverless postgres with a multimodal datalake, enabling scalable retrieval and training.

项目地址:https://gitcode.com/gh_mirrors/de/deeplake
点击查看免费下载

相关推荐

上一篇:为什么RIFM能成为React输入格式化的首选库?深度解析
下一篇:网页转 Figma 设计稿,5 分钟上手 HTML to Figma 开源转换指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 2:32:17

Design Compiler:Topographical Workshop Lab2

相关阅读 Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 目录 实验二、运行DC-T&#xff08;实验时长&#xff1a;30分钟&#xff09; 学习目标 任务 1&#xff1a;运行参考方法生成工具 任务 2&#xff1a;将RMgen种…

作者头像 李华
网站建设 2026/9/25 2:30:58

海温海冰数据预处理实战:海洋-海冰模型驱动场构建指南

简介&#xff1a;全球海水表面温度与海冰浓度数据集&#xff08;2020a专用&#xff09;源自 Met Office Hadley Centre 观测数据集&#xff0c;包含覆盖全球海域的海表温度和海冰浓度要素&#xff0c;是海洋气候研究中常用的基础数据资源&#xff0c;适合需要处理 NetCDF 格式但…

作者头像 李华
网站建设 2026/9/25 2:30:27

如何快速上手眼动模块:从OpenBlock接线到第一次眨眼的5分钟教程

如何快速上手眼动模块&#xff1a;从OpenBlock接线到第一次眨眼的5分钟教程 【免费下载链接】eye-tracking-module 源师兄扩展项目: 眼动模块 | 由源师兄组织创建 项目地址: https://gitcode.com/yuanshixiong/eye-tracking-module 本教程帮助新手在 5 分钟内快速上手源…

作者头像 李华