- 后端
【免费下载链接】prql
PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement
导读
prqlc-js是 PRQL 项目官方提供的 JavaScript/TypeScript 绑定,它将 Rust 编写的prqlc编译器编译为 WebAssembly,从而让开发者能在 Node.js 服务端、现代浏览器以及各类前端框架中直接完成「PRQL → SQL」的编译。读完本文,你将掌握prqlcnpm 包的安装、compile与管道函数(prql_to_pl、pl_to_rq、rq_to_sql)的使用、CompileOptions三个核心参数(target、format、signature_comment)的语义与优先级,以及编译错误的结构化解析方法。
什么是 prqlc-js
PRQL(Pipelined Relational Query Language)是一种面向数据转换的现代语言,定位是"简单、强大、管道化的 SQL 替代品"。而prqlc是它的 Rust 编译器,负责把 PRQL 源码解析为中间表示并最终生成 SQL。prqlc-js正是这条编译管线在 JavaScript 生态中的桥接层:它通过wasm-bindgen把prqlc编译成wasm32目标(见 Cargo.toml 中的cfg(target_family="wasm")依赖声明),再以 npm 包的形式对外分发。
从源码结构看(src/lib.rs),整个绑定层只暴露了一组薄封装函数,内部全部委托给 prqlc/prqlc/src/lib.rs 中 Rust 侧同名的编译入口,例如compile(Rust 侧实现在 lib.rs 第 189 行,内部依次执行parser::parse→semantic::resolve_and_lower→sql::compile三个阶段)。因此 JS 调用方获得的能力与 Rust 原生 API 完全一致,只是输入输出以字符串 / JSON 的形式在 WASM 边界传递。
安装
prqlc-js以prqlc为包名发布在 npm registry 上(见 package.json),安装方式与其他 npm 包无异:
npm install prqlc该包同时声明了 Node 与浏览器两套入口:
main指向dist/node/prqlc_js.js(Node.js CommonJS 入口);browser指向dist/web/prqlc_js.js(浏览器 ESM 入口);types指向dist/node/prqlc_js.d.ts(TypeScript 类型声明)。
files字段仅发布dist/**/*与package.json,因此 npm 包体积只包含编译好的 WASM 产物与绑定代码。
快速开始:在 Node.js 中编译 PRQL
compile函数接受一段 PRQL 字符串,返回对应的 SQL 字符串。最直接的用法如下:
const prqlc = require("prqlc"); const sql = prqlc.compile(`from employees | select first_name`); console.log(sql);compile的完整签名(TypeScript 视角):
function compile(prql_query: string, options?: CompileOptions): string;当查询跨多行时同样直接传入模板字符串即可,PRQL 的管道语法天然适合这种写法:
const prqlc = require("prqlc"); const sql = prqlc.compile(` from employees select first_name `); console.log(sql);对应地,Rust 侧compile的原型为fn compile(prql_query: &str, options: Option<CompileOptions>) -> Option<String>(见 src/lib.rs 第 9 行),Option参数在 JS 中表现为"可省略",省略时自动回退到CompileOptions::default()。
使用 CompileOptions 控制编译行为
通过new prqlc.CompileOptions()构造选项对象,可以控制 SQL 生成的方言、格式与注释行为:
const opts = new prqlc.CompileOptions(); opts.target = "sql.mssql"; opts.format = false; opts.signature_comment = false; const sql = prqlc.compile(`from employees | take 10`, opts); console.log(sql);三个字段的语义如下:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
target | string | ""(空字符串) | 目标 SQL 方言,如sql.mssql、sql.sqlite、sql.duckdb、sql.postgres;留空表示sql.any,此时使用查询头prql target:...指定的方言 |
format | boolean | true | 是否将生成的 SQL 交给格式化器美化(拆分成多行、整理缩进与间距) |
signature_comment | boolean | true | 是否在生成的 SQL 末尾追加编译器的签名注释(包含目标方言信息等) |
这三个字段在 Rust 侧一一对应(src/lib.rs 第 60-74 行),并通过TryFrom<CompileOptions> for prqlc::Options(第 116-137 行)转换为编译器内部使用的 Options 结构。
target 的优先级与校验规则
target的处理逻辑在绑定层是"显式优先、留空回退":
- 若
opts.target为空字符串,则视为未设置,使用Target::default()(即sql.any),最终方言从 PRQL 查询头部的prql target:sql.xxx指令解析; - 若
opts.target非空,则必须能被Target::from_str成功解析,否则直接抛出异常,而不是静默回退到通用 SQL——源码注释明确说明这是为了防止sql.postgrez之类的拼写错误悄悄编译成泛化 SQL(见 src/lib.rs 第 119-127 行)。
测试 tests/test_all.mjs 对这套规则做了完整覆盖:
CompileOptions显式设置target时,即使 PRQL 头部写了不同的方言,也以选项为准(CompileOptions should be preferred...,第 56-72 行);- 未设置
target时,回退使用查询头方言(should treat an unset target as sql.any,第 83-96 行); - 传入未知方言如
sql.postgrez会抛出包含该字符串的错误(第 74-81 行)。
Rust 侧Target枚举只支持Sql(Option<Dialect>)一种变体,Target::names()会生成sql.any加全部方言变体的完整列表(见 prqlc/prqlc/src/lib.rs 第 220-240 行)。这些方言名同时也是prqlc-js暴露的get_targets()函数的返回值来源(见 src/lib.rs 第 77-82 行),可用于在 UI 中动态渲染方言下拉框。
进阶 API:分段使用编译管线
除了端到端的compile,prqlc-js还暴露了四个中间步骤函数,便于调试或在编译链路中注入自定义逻辑:
function prql_to_pl(prql_query: string): string; function pl_to_prql(pl_json: string): string; function pl_to_rq(pl_json: string): string; function rq_to_sql(rq_json: string): string;对应 Rust 侧的调用链(src/lib.rs 第 20-55 行):
prql_to_pl(prql_query):PRQL 文本 → PL(管道语言中间表示)JSON。实现为prqlc::prql_to_pl后接json::from_pl序列化;pl_to_prql(pl_json):PL JSON → PRQL 文本(反向格式化的逆操作),先json::to_pl反序列化再调用pl_to_prql;pl_to_rq(pl_json):PL JSON → RQ(关系查询中间表示)JSON,中间经过pl_to_rq与json::from_rq;rq_to_sql(rq_json):RQ JSON → SQL,此步骤以默认Options编译,不接收用户自定义选项。
测试中prql_to_pl对合法 PRQL 返回可被JSON.parse的字符串、对非法输入抛错(tests/test_all.mjs 第 99-107 行),验证了这些 API 的输入输出约定。从仓库结构看,PL 与 RQ 的具体数据结构定义分别在 prqlc/prqlc/src/ir/pl 与 prqlc/prqlc/src/ir/rq 目录下。
在浏览器中直接使用
prqlc-js的 WASM 产物同样面向浏览器设计。最轻量的方式是作为 ES Module 引入dist/web/prqlc_js.js,并先await init()完成 WASM 实例初始化:
<html> <head> <script type="module"> import init, { compile } from "./dist/web/prqlc_js.js"; await init(); const sql = compile("from employees | select first_name"); console.log(sql); </script> </head> <body></body> </html>这种场景特别适合构建纯前端的数据工具:仓库中的 web/playground(在线 PRQL Playground)正是以file:../../prqlc/bindings/js的方式本地依赖该包(见 web/playground/package.json 第 21 行),配合 DuckDB WASM 在浏览器端完成 PRQL 编译与查询执行;项目官方文档 web/book/src/project/bindings/javascript.md 也直接通过{{#include}}引入了本 README,作为 JavaScript 绑定篇的正文。
在框架或打包器(Bundler)中使用
如果项目使用 Vite、Webpack 等打包工具,应从专门为 bundler 场景生成的入口导入,以获得正确的模块解析与 tree-shaking 支持:
import { compile } from "prqlc/dist/bundler"; const sql = compile(`from employees | select first_name`); console.log(sql);这与package.json中browser字段指向dist/web的约定相辅相成:node、web、bundler三个产物目录分别服务于三种运行时。
错误处理:结构化的编译诊断
与很多把错误编码进字符串的方案不同,prqlc-js将编译错误序列化为 JSON 数组后随异常抛出,调用方可以解析出结构化的诊断信息。错误对象的 TypeScript 接口如下:
interface ErrorMessage { /// Message kind. Currently only Error is implemented. kind: "Error" | "Warning" | "Lint"; /// Machine-readable identifier of the error code: string | null; /// Plain text of the error reason: string; /// A list of suggestions of how to fix the error hints: string[]; /// Character offset of error origin within a source file span: [number, number] | null; /// Annotated code, containing cause and hints. display: string | null; /// Line and column number of error origin within a source file location: SourceLocation | null; } /// Location within the source file. /// Tuples contain: /// - line number (0-based), /// - column number within that line (0-based), interface SourceLocation { start: [number, number]; end: [number, number]; }关键字段速查:
kind:错误类别,当前仅实现Error;code:机器可读的错误码,例如E0001(语法错误);reason:纯文本的错误原因描述;hints:修复建议列表;span:错误在源文件中的字符偏移区间;display:带注解的代码片段(含原因与提示,多行文本);location:错误起止的行号与列号(均为 0-based,即行号从 0 开始计数)。
捕获方式如下:
try { const sql = prqlc.compile(`from employees | foo first_name`); } catch (error) { const errorMessages = JSON.parse(error.message).inner; console.log(errorMessages[0].display); console.log(errorMessages[0].location); }注意异常信息中的 JSON 结构带有一个inner键,实际的ErrorMessage[]数组位于JSON.parse(error.message).inner。这一序列化行为源于绑定层return_or_throw的实现——编译失败时调用wasm_bindgen::throw_str(&e.to_json()),将prqlc::ErrorMessages(内部为inner数组)整体 JSON 化后作为异常消息抛出(见 src/lib.rs 第 139-151 行)。
测试同样验证了错误结构:display字段包含换行(多行注解)、reason为单行纯文本、code可精确匹配E0001(tests/test_all.mjs 第 134-158 行)。此外,当启用console_error_panic_hookfeature(默认开启,见 Cargo.toml)时,WASM 内部的 panic 也会以console.error的形式输出,便于开发期排查。
本地开发与构建
在仓库内对prqlc-js做二次开发时,常用的命令如下:
npm run build该命令会依次产出 Node、bundler、web 三个平台的构建产物,全部放入dist目录。实际构建脚本定义在 package.json,底层依次执行:
build:node:wasm-pack build --target nodejs --out-dir dist/node;build:web:wasm-pack build --target web --out-dir dist/web;build:bundler:wasm-pack build --target bundler --out-dir dist/bundler。
运行测试:
npm test测试框架为 mocha + chai(见devDependencies),测试文件位于 tests/test_all.mjs,直接加载dist/node/prqlc_js.js进行断言。
加速开发循环:PROFILE 环境变量
默认情况下,每次构建都会对 WASM 二进制做优化(wasm-pack默认--release级别),即使底层代码没有变化也很耗时。开发阶段可以设置PROFILE环境变量为dev,以获得更快但优化程度较低的构建:
PROFILE=dev npm run build其原理是构建脚本中--${PROFILE}的插值:PROFILE=dev时等价于wasm-pack build --dev。
构建原理与注意事项
- 绑定层基于
wasm-pack生成(README 脚注同时指出wasm-pack维护活跃度有限,团队对替代方案持开放态度,并倾向裁减其部分特性以构建三个目标产物); - 项目在
wasm-pack的常规用法之上叠加了一层 npm 封装:不采用"每个目标一个包"的推荐做法,而是把 Node、bundler、web 三个目标打包进同一个 npm 包的dist子目录,构建指令写在buildscript 而非packscript 中(见 package.json)——这正是"一个包、三入口"设计得以实现的关键; - 绑定层的 Rust 代码整体受
#![cfg(target_family = "wasm")]约束(src/lib.rs 第 1 行),意味着该 crate 只在 WASM 目标下编译,Rust 原生侧并不包含这些绑定代码; CompileOptions中format与signature_comment的默认值均为true(src/lib.rs 第 84-92 行),与 Rust 侧 Options::default() 保持一致,但绑定层强制使用DisplayOptions::Plain(无 ANSI 颜色)以适配终端之外的调用环境。
小结
prqlc-js用一个 npm 包同时覆盖了 Node.js、浏览器与打包器三种运行场景:compile负责端到端编译,四个管道函数暴露中间表示,CompileOptions提供方言/格式/注释的精细控制,而结构化错误对象则让 PRQL 的诊断信息可以被程序化消费。结合 tests/test_all.mjs 中覆盖方言优先级、未知方言报错、错误码解析等场景的用例,以及 web/playground 这一真实消费方,你可以放心地将它接入自己的数据工具链。
- 后端
【免费下载链接】prql
PRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement
相关推荐
rclone 编译为 WASM:在浏览器中以 JavaScript 库方式调用 rclone RC 接口
rclone 编译为 WASM:在浏览器中以 JavaScript 库方式调用 rclone RC 接口 rclone 的 fs/rc/js/ https://
CLI数据同步对象存储Rome JavaScript Bindings 实战指南:在 Node.js 与浏览器中通过 WASM 调用 Rome 的格式化与 Lint 能力
Rome JavaScript Bindings 实战指南:在 Node.js 与浏览器中通过 WASM 调用 Rome 的格式化与 Lint 能力 @rome
开发工具CLILint格式化静态分析代码质量构建工具Lattigo加密安全指南:理解IND-CPA与CPA-D安全机制的3个关键要点
Lattigo加密安全指南:理解IND CPA与CPA D安全机制的3个关键要点 Lattigo是一个基于格的多方同态加密Go语言库,为开发者提供了强大的加密工
密码学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考