ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

在 C 语言中集成 PRQL:prqlc-c 最小示例从编译到运行全解析

在 C 语言中集成 PRQL:prqlc-c 最小示例从编译到运行全解析 后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载本篇技术指南以 PRQL 仓库中 minimal-c 示例 为核心讲解如何用gcc与make将 PRQL 编译器prqlc-c C 绑定嵌入 C 程序从构建静态库、编写调用compile()的入口代码到自定义编译选项、解析错误消息与分阶段获取中间表示。读完本文你将掌握一套可直接复制的 C/FFI 集成模板并理解 prqlc-c 在仓库中的完整调用链与内存管理约定。示例概览一个最小的 prqlc-c 集成prqlc-c 绑定 将 PRQL 编译器以 C ABI 的形式暴露出来同时生成静态库.a与动态库.so因此可以被任何支持 FFIForeign Function Interface的语言嵌入例如 Go、C、C、Zig 等。minimal-c示例就是其中最精简的参考实现——它只包含三个文件README.md说明使用场景与运行方式main.c完整的演示程序覆盖默认编译、自定义选项、错误处理与中间结果四类用法Makefile负责先构建 Rust 侧的prqlc-c库再调用gcc链接出可执行文件。整个示例的运行只需要一条命令make run这条命令会先执行build目标编译静态库 编译 C 程序然后运行生成的main.out。构建链路从 Rust 库到 C 可执行文件Makefile 的构建流程Makefile 揭示了完整的构建依赖关系它由两个阶段组成PRQL_PROJECT../../../../.. run: build ./main.out build-prql: cargo build --package prqlc-c --release UNAME_S : $(shell uname -s) LD_FLAGS -L${PRQL_PROJECT}/target/release \ ${PRQL_PROJECT}/target/release/libprqlc_c.a \ -pthread -ldl -lm ifeq ($(UNAME_S),Darwin) LD_FLAGS : $(LD_FLAGS) -framework CoreFoundation endif build: main.c build-prql gcc main.c -o main.out \ -I${PRQL_PROJECT}/prqlc/bindings/prqlc-c \ $(LD_FLAGS)PRQL_PROJECT指向仓库根目录minimal-c位于prqlc/bindings/prqlc-c/examples/minimal-c/向上四级即仓库根。build-prql调用cargo build --package prqlc-c --release在仓库根目录的target/release下产出libprqlc_c.a静态库与libprqlc_c.so动态库。这一产物类型由 prqlc-c/Cargo.toml 中的crate-type [staticlib, cdylib]决定。build用gcc编译main.c头文件搜索路径-I指向prqlc/bindings/prqlc-c即 prqlc.h 所在目录并链接静态库及系统依赖-pthread -ldl -lm。macOS 下额外需要-framework CoreFoundation这与 Rust 运行时在 macOS 上的链接需求有关Makefile 通过UNAME_S判断并自动追加。该 Makefile 也被 prqlc-c README 引用为标准链接参数的权威来源若使用其他构建系统可按同样方式指向target/release下的libprqlc_c.a例如 Go 的CGO_LDFLAGS-L/path/to/target/release -lprqlc_c -pthread -ldl -lm。另外 Makefile 还提供了一个valgrind目标用于在内存检查器下运行示例配合下文讲到的result_destroy内存释放约定可以验证程序没有泄漏。C 侧 API 面核心类型与函数头文件 prqlc.h 由 cbindgen 自动生成生成命令task build-prqlc-c-header配置见 cbindgen.toml它完整定义了 FFI 接口。示例中用到的核心类型如下类型作用CompileResult编译结果包含output生成的 SQL/JSON 字符串、messages消息数组与messages_len消息条数Message单条编译消息含code机器可读错误码、reason错误文本、hint修复建议、display带注解的源码片段、span/location错误位置Options编译选项含format、target、signature_comment三个字段MessageKind消息类型枚举目前仅实现ErrorWarning与Lint已预留公开的入口函数共五个compile(prql_query, options)一键完成 PRQL → SQL 的完整编译prql_to_pl(prql_query)PRQL → PL解析树以 JSON 输出pl_to_rq(pl_json)PL → RQ关系查询中间表示以 JSON 输出rq_to_sql(rq_json, options)RQ → SQLresult_destroy(res)释放CompileResult占用的全部内存。从 src/lib.rs 的实现可以看到compile本质上是后三个函数的串联封装——它在 Rust 侧依次调用prqlc::prql_to_pl、prqlc::pl_to_rq、prqlc::rq_to_sql并且省去了中间 JSON 的序列化/反序列化开销。编写 main.c四类核心用法逐行解读示例程序 main.c 结构清晰依次演示了四种典型场景。1. 默认选项编译char *prql_query; prql_query from albums | select {album_id, title} | take 3; CompileResult res; // default compile option res compile(prql_query, NULL); print_result(res); if (res.messages_len ! 0) return 1; result_destroy(res);把NULL作为第二个参数传给compile即使用默认编译选项。程序随后检查messages_len若不为 0 则说明编译失败返回非零退出码。成功后调用result_destroy释放资源。这里体现的是 prqlc-c 最重要的两条约定任何返回CompileResult的函数都必须配套调用一次result_destroy且不能手动释放其中任何字段详见 prqlc.h 中result_destroy的 Safety 注释以及 lib.rs 中对字符串、向量、结构体逐项回收的实现。2. 自定义编译选项Options opts; opts.format false; opts.signature_comment false; opts.target sql.mssql; res compile(prql_query, opts); print_result(res);Options结构体包含三个字段默认值见 prqlc.hformat默认true是否将生成的 SQL 通过格式化器美化——拆分多行、调整缩进与空格target默认sql.any目标 SQL 方言。sql.any表示从查询头target查询头注释中提取方言显式传入如sql.mssql则强制指定signature_comment默认true是否在生成的 SQL 末尾追加编译器签名注释。target的解析逻辑在 prqlc/prqlc/src/lib.rs字符串必须以sql.为前缀其后跟方言名或any支持的全部方言在 dialect.rs 的Dialect枚举中定义包括Ansi、BigQuery、ClickHouse、DuckDb、Generic、MsSql、MySql、Postgres、SQLite、Snowflake因此target也可以取sql.duckdb、sql.postgres、sql.sqlite等。非法值会被转换为错误消息返回。底层转换见 lib.rsRust 侧将 C 的Options转为prqlc::Options其中target为NULL或空串时统一回退为sql.any。3. 错误处理// error handling res compile(from album | select {album_id} | select {title}, NULL); print_result(res); if (res.messages_len 0) return 1; result_destroy(res);这段代码故意传入语义有误的 PRQLalbum在from后被当作表使用紧接着又被当作列名select属于命名/类型冲突场景验证错误路径编译失败时CompileResult.messages_len大于 0。第二个错误样例let a (from album)同样用于触发错误分支。print_result展示了读取消息字段的正确姿势void print_result(CompileResult res) { printf(---- [ Compiled with %zu errors ]----\n, res.messages_len); for (size_t i 0; i res.messages_len; i) { Message const *e res.messages[i]; if (e-display ! NULL) { printf(%s, *e-display); } else if (e-code ! NULL) { printf([%s] Error: %s\n, *e-code, e-reason); } else { printf(Error: %s, e-reason); } } if (*res.output \0) { printf(Output: empty\n\n); } else { printf(Output:\n%s\n\n, res.output); } }优先打印display带源码注解与定位的错误展示信息最丰富其次打印code与reason否则只打印reason。同时编译失败时output为空串示例据此区分输出为空与有输出两种情况。4. 分阶段编译获取中间表示// intermediate results res prql_to_pl(prql_query); print_result(res); res2 pl_to_rq(res.output); result_destroy(res); print_result(res2); result_destroy(res2);最后一段演示了调试与集成非常有用的分阶段能力prql_to_pl返回 PRQL 的解析树PLpl_to_rq再把它转换为关系查询中间表示RQ两者都以 JSON 字符串放在CompileResult.output中。注意这里先用res2接收pl_to_rq的结果然后才result_destroy(res)释放第一个结果——在把res.output作为入参传给下一个函数之前不能提前释放否则会访问已释放内存。示例还顺带印证了每个返回CompileResult的函数都必须配套一次result_destroy的规则。运行与验证在仓库根目录执行make run或进入prqlc/bindings/prqlc-c/examples/minimal-c/后执行预期输出依次为默认编译from albums | select {album_id, title} | take 3生成的 SQL关闭格式化、去掉签名注释、强制sql.mssql方言后的 SQL可观察到与默认输出的差异例如方言特有的语法与单行输出两段错误 PRQL 的编译消息Compiled with N errors 带注解的错误展示prql_to_pl与pl_to_rq得到的 JSON 中间表示。若改用make valgrind运行Valgrind 会检查每次compile/prql_to_pl/pl_to_rq后调用的result_destroy是否完整释放了 FFI 层分配的内存。同类参考实现还包括 minimal-cpp使用 cbindgen 生成的 C 头文件prqlc.hpp调用方式几乎一致见 main.cpp与 minimal-zig通过cImport引入prqlc.h它们共享同一套 FFI 面。小结minimal-c示例为在 C乃至任何支持 FFI 的语言中嵌入 PRQL 编译器提供了最小可行模板。其要点可归纳为以cargo build --package prqlc-c --release产出静态库用gcc -Iprqlc/bindings/prqlc-c链接通过compile(prql_query, options)完成端到端编译其中options NULL即默认配置Options三字段分别控制格式化、方言与签名注释通过messages_len与Message各字段完成错误诊断通过prql_to_pl/pl_to_rq获取中间表示最后务必为每个CompileResult调用一次result_destroy。深入阅读 prqlc.h 与 lib.rs 可获得完整 FFI 契约这为扩展自己的宿主语言绑定提供了清晰的起点。赞分享后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载相关推荐使用 Rerun C SDK 编写最小 C 示例从编译链接到运行 Viewer使用 Rerun C SDK 编写最小 C 示例从编译链接到运行 Viewer 本篇技术指南以 examples/c/minimal https://link数据可视化3D渲染数据分析在 Android NDK 中集成预编译第三方 C/C 库hello-libs 示例深度解析在 Android NDK 中集成预编译第三方 C/C 库hello libs 示例深度解析 导读 本篇文章围绕 Android NDK Samples示例工程移动开发Wasmtime 多语言 API 示例指南从 Rust/C/C 示例到嵌入实战Wasmtime 多语言 API 示例指南从 Rust/C/C 示例到嵌入实战 导读 examples 目录是 Wasmtime 官方提供的、以多语言 A语言运行时JIT编译编译器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表