ARTICLE DETAIL

资讯详情

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

Windmill PostgreSQL 脚本开发指南:$n 参数绑定、S3Object 输入与结果流式写入 S3

Windmill PostgreSQL 脚本开发指南:$n 参数绑定、S3Object 输入与结果流式写入 S3 Windmill PostgreSQL 脚本开发指南$n 参数绑定、S3Object 输入与结果流式写入 S3【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill本文以 Windmill 仓库中为 AI Copilot 与 CLI 编写的 PostgreSQL 语言提示词 postgresql.md 为核心完整讲解 Windmill 原生 PostgreSQL 脚本的参数绑定语法$1::type 声明注释、S3Object 文件参数、-- s3结果流式导出三大能力并结合 Worker 端执行器源码pg_executor.rs、windmill-parser-sql说明这些语法在运行时如何被解析、绑定与执行。读完后你将能在 Windmill 中写出带类型、带默认值、可吃文件参数并能把大结果集直接落到对象存储的 SQL 脚本并理解其底层机制与常见报错的成因。一、这份 PostgreSQL 提示词在 Windmill 中的定位system_prompts/languages/postgresql.md 位于 system_prompts/ 目录下。该目录是 Windmill 前端 Copilot 与 CLI 引导共用的系统提示词唯一事实来源base/存放手写的基础指令模板languages/存放各语言PostgreSQL、MySQL、DuckDB、Python 等 20 种的写法约定auto-generated/由generate.py生成、禁止手改。从目录结构看languages/postgresql.md是手工维护的源头文件system_prompts/generate.py 会把它组装进最终导出并在 write-script-postgresql/SKILL.md 中生成供 CLI如wmill init的 write-script-postgresql skill使用的技能文档——生成的 SKILL 文档尾部逐字内嵌了本篇文档的全部三节内容参数绑定、S3Object 参数、S3 流式导出可以确认这份提示词就是官方对“如何写 Windmill PostgreSQL 脚本”的规范口径。该文档定义了 Windmill 原生 PostgreSQL 脚本的三件事如何用$n::type占位符与声明注释绑定参数、如何把 S3 文件作为参数传入、如何把查询结果直接流式写入 S3。下面逐一展开并用 Worker 源码印证每条规则的实现。二、参数绑定$n::type占位符与声明注释2.1 文档定义的基本语法Windmill 的原生 SQL 脚本不经过函数签名参数直接写在 SQL 语句里用 PostgreSQL 标准的$1::type、$2::type占位符取得。参数名通过脚本开头的注释声明注释中不写类型类型由语句中的::强制转换给出-- $1 name1 -- $2 name2 default_value SELECT * FROM users WHERE name $1::TEXT AND age $2::INT;三条注释规则-- $n name把位置参数$n绑定到具名参数name可选地写-- $n name default提供默认值类型在 SQL 中通过$n::TEXT、$n::INT这类 cast 表达。2.2 声明注释是如何被解析的Worker 端由 windmill-parser-sql 解析这类脚本。PostgreSQL 声明行使用的正则是static ref RE_ARG_PGSQL: Regex Regex::new(r#(?m)^-- \$(\d) (\w)(?: \(([A-Za-z0-9_\[\]])\))?(?: ?\ ?(.))? *(?:\r|\n|$)#)见 lib.rs四个捕获组依次是位置号、参数名、可选的括号内类型、可选的默认值。也就是说声明语法实际还支持在注释里显式写类型-- $1 name (integer)这与write-script-postgresql技能文档一致文档示例省略类型是推荐写法括号类型是可选增强。parse_pgsql_sig_with_typed_schema会返回一个typed_schema标志表示参数是否携带了显式类型声明供执行器选择绑定路径。2.3 执行器对参数的实际处理pg_executor.rs 中do_postgresql_innerL399 起实现了几个文档未展开、但直接影响可用性的机制稀疏占位符自动重编号。用户写SELECT $5, $50这类不连续编号时执行器先用parse_pg_statement_arg_positions做单遍 token 化跳过字符串字面量、注释与 dollar-quoted 块避免误伤SELECT price: $5这类内容再按字节位置从后往前把$5, $50重写为$1, $2L437-L458。所以脚本作者不必保证占位符从$1连续编号。类型映射决定两条执行路径。每个声明参数都会经过convert_val转换取值并用otyp_to_pg_type把声明类型映射到 tokio-postgres 的Type。该映射L363-L397覆盖bool、char、smallint及int2/serial2别名、int/int4/serial、bigint/int8/serial8、real/float4、double/float8、numeric/decimal、text、varchar、uuid、date、time、timetz、timestamp、timestamptz、json、jsonb、bytea、oid以及以[]结尾的对应数组类型。若所有参数类型都能解析走query_typed_raw未命名 prepared statement整个查询以一次 ParseBindExecuteSync 往返发出。源码注释明确指出这样做是为了兼容事务模式的连接池代理PgBouncer/Supabase pooler/RDS Proxy——命名语句的 prepare 与 execute 可能落在不同后端连接上而被报 “missing”未命名语句则不会。只要有一个参数类型无法解析如自定义 enum、geometry就回退到preparequery_raw让服务端从 SQL 上下文推断参数类型。缺省参数绑定为 NULL 并告警。若某个声明参数在 args 中找不到值且没有默认值执行器按历史行为绑定NULL但会通过warn_on_missing_argsL290-L316在任务日志里一次性列出所有缺失的参数名提示“拼写错误会让整行静默变 NULL”建议补值、加-- $1 name (type) default默认值或删除声明。这是排错时值得记住的一点参数名写错不会立即报错只会得到 NULL 行 日志告警。绑定失败的错误会被重新包装。当 JSON 值与目标类型不匹配如把数组绑定给TEXTwrap_param_encoding_error会把底层error serializing parameter N改写成包含参数名、JSON 值种类、Postgres 类型和修复建议在 SQL 中加显式 cast 或用-- $n name (type)声明类型的信息L343-L361。numeric 精度告警。结果中的numeric列经 f64 序列化可能丢失精度执行器在有限预算内检测并写出一条日志SELECT col::text后用 Decimal 库在客户端解析可保留完整精度L264-L284。2.4 指定数据库资源文档未单独成节但源码支持的约定脚本头部可以写-- database 资源路径由parse_db_resourcewindmill-parser-sql L223 的 RE_DB 正则识别。执行器会优先解析该注释从对应路径的资源中取出数据库连接配置支持datatable://前缀的 Datatable 资源否则回退到 args 里的database参数do_postgresql L667-L704。这使得脚本可以显式声明它操作哪个数据库资源而不依赖隐式默认。三、把 S3Object 作为脚本参数3.1 文档定义的用法当声明参数的类型为(s3object)时Windmill 前端会为它渲染一个 S3 文件选择器运行时 Worker 下载该文件并把它作为jsonb参数绑定给 SQL——Parquet/CSV 文件在服务端被解码成 JSON 记录数组JSON/JSONL 原样透传。SQL 端用jsonb_to_recordset或任意 jsonb API消费-- $1 file (s3object) SELECT * FROM jsonb_to_recordset($1::jsonb) AS r(id INT, name TEXT);3.2 服务端物化过程这条链路的实现在两处materialize_s3object_args遍历签名中otyp s3object的参数从 args 取出S3Object调用下载逻辑把解码后的 JSON 文本解析回Value写回 args并把该参数的otyp改写为jsonbTyp::Object。源码注释解释了为何要解析成Value而不是保留字符串convert_val的 Array/Object → JSONB 分支才能正确绑定裸字符串会与 JSONB 参数类型不匹配。若参数值缺失直接报Missing S3Object value for arg ...的 BadRequest。fetch_s3object_as_json_text通过认证客户端的 S3 端点下载文件按对象扩展名判断格式detect_format.parquet→ 解码为 JSON 数组.csv→ 解码为 JSON 数组其余.json、.jsonl、.ndjson、无扩展名按 JSON 文本处理。JSONL 的处理由normalise_json_or_jsonl完成多行 NDJSON 会被重新包装成[v1, v2, ...]数组单条记录或单个 JSON 值则原样返回保留形状空文件返回[]。该文件的单元测试sql_s3_input.rs L130-L198覆盖了扩展名大小写、JSONL 归一化、pretty-printed JSON 不被误判为 JSONL 等场景。下载阶段还会启动JobPingHeartbeat避免大文件下载期间触发僵尸任务监控。3.3 容量边界约 256 MB 的 jsonb 上限需要特别注意的文档级限制整个文件会被物化为一个 jsonb 参数而 PostgreSQL 对单个 jsonb 值有约 256 MB 的硬上限total size of jsonb {array,object} elements exceeds the maximum of 268435455 bytes。执行器专门用map_s3object_jsonb_overflowL1141-L1161把这条晦涩的服务端错误改写为可操作的指引这是数据库侧限制不是 Worker 内存限制加大 Worker 规格也不会提高原生 SQL 的(s3object)输入是一次性加载、不流式只适合较小文件大 Parquet/CSV 文件应改用 DuckDB 脚本——DuckDB 直接以read_parquet(...)/read_csv_auto(...)从 S3 流式读取源码注释也明确 DuckDB 执行器不走fetch_s3object_as_json_text路径而是绑定裸s3://URI见 sql_s3_input.rs 文件头注释。这条边界是选型要点小文件配置、字典表、增量数据用 PostgreSQL (s3object)简单直接GB 级分析负载应走 DuckDB。四、-- s3指令把查询结果流式写入 S34.1 文档定义的用法在脚本顶部添加-- s3指令查询结果集不再作为返回值缓冲而是直接流式写入 S3Windmill 写完文件后把该文件的S3Object作为脚本结果返回-- s3 prefixexports/users formatparquet SELECT id, name FROM users;三个键全部可选prefix对象键前缀storage命名存储省略时使用工作区默认存储formatjson默认、parquet或csv。文档的建议场景是大结果集——行直接流入 S3而不是作为脚本返回值整体缓冲从而避开结果尺寸上限。4.2 指令解析parse_s3_mode 用正则(?m)^-- s3( (.))? *(?:\r|\n|$)捕获首行指令然后按键值对解析format只接受json/parquet/csv其余值报Invalid S3 mode format未知键报Invalid S3 mode argument。默认format为 Jsonprefix/storage缺省。执行器在 do_postgresql 中调用parse_s3_mode并把解析结果经s3_mode_args_to_worker_data转成携带目标对象键与存储名的S3ModeWorkerData。4.3 流式写入的执行路径当s3非空时do_postgresql_inner 不走“逐行收集到 Vec”的分支而是把行流逐行转成 JSON 值后交给 s3_stream_and_upload_with_logs该函数为 PG/MSSQL/MySQL/BigQuery/Snowflake 等 SQL 执行器共用行流经convert_json_line_stream按目标格式json/parquet/csv转码成字节流期间通过 channel 上报IngestStats一个后台任务持续把进度写入任务日志PostgreSQL s3 stream progress: N rows, X MB | elapsed ...转码完成后记录 ingest 汇总日志行数、MB、耗时、首行延迟再调用S3ModeWorkerData::upload把整个字节流通过认证客户端上传到 S3最后记录uploadtranscode done日志并返回to_return_s3_obj()构造的S3Object作为脚本的单个结果值。因此脚本的“返回值”是对象存储中的文件引用{ s3, storage, ... }下游步骤flow 中的后续脚本或 CLI 消费拿到它后可以继续读取该文件。与第三节的输入路径形成对称对比输出是真流式行到行转码、分块上传输入是一次性物化——这也是为什么文档把-- s3明确定位为“大结果集”方案。4.4 与结果尺寸限制的关系未开启-- s3时执行器逐行收集结果并对总序列化字节数做预算检查max_sql_result_size超限报sql_result_too_large_error见 L582-L624。从源码结构看开启-- s3后行流不再累积到内存返回值恰好绕开这一缓冲约束与文档“rows stream directly to S3 instead of being buffered as the script return value”的表述一致。五、执行环境的其他关键机制围绕这条执行链路pg_executor.rs 还有几处值得了解的设计它们决定了脚本在真实部署中的行为连接缓存与会话清理。非云托管构建下Worker 对同一数据库 URI 维护一个CONNECTION_CACHE缓存键包含 sslmode、根证书、accept_invalid_certs与认证模式段防止弱 TLS 配置的连接被复用到更严格的请求上。复用前先执行一条“会话复位”探测语句RESET ALL; RESET SESSION AUTHORIZATION; UNLISTEN *; CLOSE ALL; SELECT pg_advisory_unlock_all();L778-L832。源码注释解释了为何不用DISCARD ALL——它会DEALLOCATE ALL掉服务端已准备语句而 tokio-postgres 按 Client 缓存的类型解析语句会因此失效导致自定义 enum/domain 查询报prepared statement sN does not exist。对脚本作者的实际影响是前一个任务遗留的search_path、SET ROLE、advisory lock 都会被清理但临时表等极端状态可能跨任务残留注释中承认这是权衡取舍。多认证模式。PgAuthMode区分 Password、AWS RDS IAM、Azure Workload Identity 三种登录方式后两者分别依赖 enterprise 编译特性任务日志会打印实际使用的登录身份L69-L166。多语句与结果收集策略。脚本可包含多条语句parse_sql_blocks按 dollar-quote 感知的方式切分SqlAnnotationsworker.rs L1074 起解析-- raw_output、-- prepare等注解决定结果按“最后一条语句”“仅首行”等策略收集。-- prepare模式下每条语句只做PREPARE不执行返回列名与类型信息供 Datatable 类型检查使用。六、实践清单综合文档与源码在 Windmill 中编写 PostgreSQL 脚本时参数语句内用$n::TYPE取值脚本首行区用-- $n name [ (type) ] [ default]声明占位符编号可以稀疏执行器会重编号参数名拼错只产生 NULL 加日志告警部署前留意任务日志。类型cast 类型尽量落在otyp_to_pg_type的映射表内int/integer/bigint/text/varchar/uuid/jsonb/timestamptz/bytea 及[]数组等这样走池代理友好的未命名语句路径自定义 enum 会走 prepare 回退路径且支持文本往返AnyTextValueL183-L231。文件输入-- $1 file (s3object)jsonb_to_recordset($1::jsonb)记住整文件入一个 jsonb 的 256 MB 硬上限大文件改 DuckDB。大结果输出-- s3 [prefix...] [storage...] [formatjson|parquet|csv]结果为S3Object配合任务日志里的 progress/ingest/upload 三段日志监控吞吐。数据库来源可用-- database 资源路径或 args 中的database指定连接。精度高精度numeric输出建议::text后客户端解析避免 f64 序列化丢精度执行器会给出同名告警。以上语法的规范表述以 postgresql.md 为准行为细节以 pg_executor.rs、windmill-parser-sql 与 sql_s3_input.rs 中的当前实现为准修改提示词文件后需运行python system_prompts/generate.py重新生成导出物。【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表