关于 snowflake-api
snowflake-api 是通过 Snowflake SQL API v2 提交 SQL 并处理结果的技能,介绍如何使用附带脚本或直接调用 API 执行查询、查看目录、获取分区结果和管理语句。
主要功能
- 01使用附带脚本运行 SQL
- 02轮询异步语句
- 03获取分区查询结果
- 04用 SHOW 和 DESCRIBE 浏览目录
- 05提交多条语句
- 06取消语句并进行幂等重试
适用场景
- 01在 Snowflake 中运行 SQL 查询
- 02查看数据库、架构、表和列
- 03从语句句柄恢复查询结果
- 04提交可安全重试或多语句请求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ anthropics/ claude-tag-plugins --skill snowflake-api安装后在 Claude Code 中输入/snowflake-api确认已加载。
附带文件references/api.mdscripts/snow_query.sh
详细介绍
Snowflake 的 SQL API v2(/api/v2/statements)是一个用于执行 SQL 和获取结果的轻量 REST 层。
发送 SQL 字符串后,会返回一个语句句柄,以及查询结果(适用于快速查询)或
202 Accepted 响应,供你轮询。大型结果会拆分为多个分区,需要逐个获取。没有单独的“列出表”端点——浏览目录只需执行 SQL(SHOW TABLES、
INFORMATION_SCHEMA)。
请求设置
身份验证由运行时处理——凭据会注入发送到此 API 的出站请求,因此无需进行任何设置。请勿尝试创建、生成、刷新或验证令牌或密钥。凭据变量仅用于确保请求格式正确;如果某个变量未设置,请将其设为任意占位值。持续出现 401 表示此工作区未配置凭据——请报告此情况,不要调试身份验证。
账户标识符必须真实有效——它就是主机名。它是 Snowflake URL 中
.snowflakecomputing.com 之前的部分,例如 xy12345.us-east-1 或组织格式的 myorg-myaccount。
export SNOWFLAKE_ACCOUNT="xy12345.us-east-1" # your account identifier — must be real
export SNOWFLAKE_BASE="https://${SNOWFLAKE_ACCOUNT}.snowflakecomputing.com"
export SNOWFLAKE_TOKEN="placeholder" # injected by the runtime; any value works
export SNOWFLAKE_TOKEN_TYPE="${SNOWFLAKE_TOKEN_TYPE:-OAUTH}" # leave as provided by the runtime
辅助函数和健全性检查
每个请求都携带相同的标头。定义一次辅助函数:
snowapi() {
curl -sS --compressed "$@" \
-H "Authorization: Bearer ${SNOWFLAKE_TOKEN}" \
-H "X-Snowflake-Authorization-Token-Type: ${SNOWFLAKE_TOKEN_TYPE}" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-H "User-Agent: curl-sql-api/1.0"
}
(User-Agent 是必需的——Snowflake 会拒绝没有该标头的请求。--compressed 对结果分区很重要,因为返回内容带有 Content-Encoding: gzip。)
健全性检查——确认账户标识符正确,并且工作区已连通:
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements" \
-d '{"statement":"SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_WAREHOUSE()","timeout":10}' | jq .
# Returns the configured user/role/warehouse on success.
核心操作
1. 运行语句(scripts/snow_query.sh)
通过随附脚本运行 SQL(路径相对于此技能目录):脚本会提交语句、轮询句柄直至执行完成、遍历每个结果分区(自动处理 gzip),并输出解码后的行。任何单条语句查询、SHOW 或 DESCRIBE 都可使用此脚本。
scripts/snow_query.sh --warehouse MY_WH --bind TEXT:1996-01-01 \
'SELECT o_orderdate, COUNT(*) n FROM snowflake_sample_data.tpch_sf1.orders
WHERE o_orderdate >= ? GROUP BY 1 ORDER BY 1 LIMIT 20'
- SQL 可以作为一个带引号的参数传入,也可以通过 stdin 传入。实例的具体配置来自上文的
SNOWFLAKE_ACCOUNT/SNOWFLAKE_TOKEN/SNOWFLAKE_TOKEN_TYPE(SNOWFLAKE_BASE_URL会覆盖主机)。 --warehouse/--database/--schema/--role用于设置会话上下文(省略时各项均回退到用户的 Snowflake 默认值);--tag用于设置审计所需的QUERY_TAG。--bind TYPE:VALUE(可重复使用)按顺序绑定位置参数占位符?——建议使用它,而不是将值拼接到 SQL 字符串中。类型为references/api.md中的绑定类型。--timeout(默认 300 s)是服务器端执行时限——超出时限的语句会被取消;--max-wait(默认 600 s)是脚本轮询并等待的最长时间,超时后脚本会放弃等待。--max-rows N限制获取的行数(默认 10000,0= 全部);--json会为每行输出一个 JSON 对象,而不是带表头的 TSV。句柄、行数和错误会输出到 stderr。- 单元格按 API 的原始字符串编码输出(因此日期是 epoch 天数,时间戳是 epoch 秒——请参阅“错误处理”中的注意事项)。
- 退出代码:
0表示成功,1表示请求或 SQL 执行失败(Snowflake 的code/sqlState/message会输出到 stderr),2表示在语句仍在运行时因超过--max-wait而放弃等待——句柄会输出到 stderr;请使用下方的端点获取结果或取消语句。
如果脚本出错,请阅读脚本——它只是纯 curl + jq——并根据 references/api.md 进行调试。
对于多语句请求、幂等的 requestId 重试、数组绑定、取消操作,或从已有句柄获取结果,请直接使用下方的端点。
2. 获取现有句柄的结果
GET /api/v2/statements/{handle}——脚本因超时以退出码 2 结束后的恢复路径,或用于你从其他地方获得的任何句柄。202 = 仍在运行(稍等后再次轮询),200 = 已完成,结果的第 0 个分区随响应内联返回,422 = 语句执行失败。
snowapi "${SNOWFLAKE_BASE}/api/v2/statements/${HANDLE}" -o /tmp/sf.json -w '%{http_code}\n'
jq '{cols: [.resultSetMetaData.rowType[]?.name], parts: .resultSetMetaData.partitionInfo, rows: .data[:5]}' /tmp/sf.json
- 列信息位于
resultSetMetaData.rowType[];行位于data[]中,以字符串数组形式表示(编码表见references/api.md的“Result metadata & partitions”一节)。 resultSetMetaData.partitionInfo[]列出所有分区及其rowCount。使用?partition=N获取其余分区,其中N从 1 到partitionInfo|length - 1。分区是稳定的(可并行或乱序获取),除第一个分区外均采用 gzip 压缩(辅助工具的--compressed参数会处理),且仅包含data——不包含元数据。- 结果会在语句完成后保留 24 小时;超过此时限后,该句柄会返回
404。
3. 取消正在运行的语句
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements/${HANDLE}/cancel" | jq .
取消操作尽力而为。如果语句已经完成,则不会执行任何操作。
4. 使用 requestId 实现幂等提交
如果 POST 请求可能会重试(网络不稳定、超时),请传入客户端生成的 UUID 作为 requestId。使用相同 requestId 并设置 retry=true 重试时,将返回原语句的句柄,而不会再次运行。
RID=$(uuidgen)
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements?requestId=${RID}" -d '{"statement":"INSERT INTO t VALUES (1)","warehouse":"MY_WH"}'
# On timeout, retry:
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements?requestId=${RID}&retry=true" -d '{"statement":"INSERT INTO t VALUES (1)","warehouse":"MY_WH"}'
5. 在一个请求中运行多条语句
用 ; 分隔,并将 MULTI_STATEMENT_COUNT 设为准确的语句数量(或设为 0 表示“任意数量”)。响应会返回一个父句柄和各子语句的句柄。
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements" -d '{
"statement": "CREATE TEMP TABLE tmp AS SELECT 1 AS x; SELECT * FROM tmp;",
"warehouse": "MY_WH",
"parameters": {"MULTI_STATEMENT_COUNT": "2"}
}' | jq '{parent: .statementHandle, children: .statementHandles, msg: .message}'
通过各子语句自己的句柄获取结果。BEGIN/COMMIT/ROLLBACK、USE、ALTER SESSION 以及临时表或临时阶段的创建,仅支持在多语句请求内部执行——单独作为一条语句时会以 422 失败。多语句请求不支持绑定参数。
6. 浏览目录(数据库、架构、表、列)
没有 REST 目录端点——请使用 SHOW/DESCRIBE,它们会作为普通语句运行。失败时(对象不存在、无权限)会返回 422,包含 {code, message, sqlState},且不含 data 键——处理一次这种情况后,后续调用即可假定 .data 存在:
snowapi -X POST "${SNOWFLAKE_BASE}/api/v2/statements" -d '{"statement":"SHOW DATABASES","timeout":20}' \
| jq -r 'if .data then (.data[] | @tsv) else "error \(.code // "?"): \(.message // .)" end'
将 statement 替换为以下任意语句:SHOW SCHEMAS IN DATABASE MY_DB、SHOW TABLES IN SCHEMA MY_DB.PUBLIC、DESCRIBE TABLE MY_DB.PUBLIC.EVENTS、SHOW WAREHOUSES LIKE 'MY_WH'(第 2 列 = 状态:STARTED/SUSPENDED/RESUMING/SUSPENDING)。
若要确保不同版本的列集合一致,请改为查询 INFORMATION_SCHEMA——例如:
SELECT table_name, row_count, bytes FROM my_db.information_schema.tables WHERE table_schema='PUBLIC'
或 SELECT column_name, data_type, is_nullable FROM my_db.information_schema.columns WHERE table_schema='PUBLIC' AND table_name='EVENTS' ORDER BY ordinal_position。
若要按列名标记行,可将 resultSetMetaData.rowType[].name 与每个 data[] 行配对:
jq '[.resultSetMetaData.rowType[]?.name] as $c | .data[] | [$c,.] | transpose | map({key:.[0],value:.[1]}) | from_entries。
分页
分页采用分区,而非游标。resultSetMetaData.partitionInfo 是由 {"rowCount", "uncompressedSize} 条目组成的数组;遍历索引,无需 next-token。每个分区的大小都有限制,以控制响应体积(大约几千行或未压缩约 10 MB)。
速率限制
Snowflake 没有公布 SQL API 固定的每秒请求数上限。你实际遇到的限制包括:
- 每个仓库的并发语句数(
MAX_CONCURRENCY_LEVEL,默认值为 8)——超出的查询会在 Snowflake 内部排队,而不是在 HTTP 层排队,因此不会收到429;它们只会一直处于QUEUED状态。 429 Too Many Requests确实可能在频繁请求轮询端点时出现。请退避重试,并遵守响应中提供的Retry-After。- 凭据有效期——运行时会提供并续期凭据。持续出现
401表示配置的凭据需要修复——请报告此问题,而不要尝试重新进行身份验证。
轮询时请使用 sleep,不要紧密循环;并在 POST 请求中设置较高的 timeout,这样大多数查询无需轮询即可直接返回结果。
错误处理
Snowflake 会返回 HTTP 状态码,以及包含 code、sqlState、message 和 statementHandle 的 JSON 正文。code 是 Snowflake 专用的数字字符串(例如 "390302")。
200— 已完成,结果直接返回。读取data[]。若需获取更多页面,请检查resultSetMetaData.partitionInfo。202— 仍在运行(或已异步提交)。轮询GET .../statements/{handle}。正文包含statementStatusUrl和message。400— 请求格式错误。读取message。常见原因:缺少Content-Type、JSON 格式错误、绑定索引错误(绑定使用从 1 开始的字符串键:"1"、"2",每个?对应一个)。401— 缺少凭据或凭据被拒绝。检查SNOWFLAKE_ACCOUNT是否指向正确的账户,以及是否提供了令牌和类型标头。如果问题持续存在,说明此工作区未配置凭据——请报告此问题。404— 未找到句柄。结果保留 24 h;超过此时间后,句柄将失效。也可能是句柄错误。408— 语句超出请求的timeout,因此已取消。请提高(或省略)timeout后重新提交。422— 语句执行失败(SQL 错误、已取消)。message和sqlState包含 SQL 错误信息。不要盲目重试。429— 受到速率限制。请根据Retry-After进行退避重试。503/504— 暂时性错误。请退避重试。对于 POST 请求,请使用相同的requestId并设置retry=true重试,以保持幂等性。
需要特别注意的事项:
- 默认情况下,所有内容都会转换为大写。 未加引号的标识符会解析为大写。
SHOW TABLES IN schema my_db.public和MY_DB.PUBLIC是相同的;"my_db"."public"则不是。 - SQL 中的绑定占位符是
?,而绑定使用键为字符串且索引从 1 开始的对象,而不是数组:{"1": {...}, "2": {...}}。无论type是什么,值始终都是字符串。类型列表和数组绑定格式请参阅references/api.md(Bindings)。 data[]中的值全都是字符串,日期和时间戳也不是 ISO 格式——date是自纪元以来的天数("18262"),timestamp_*是带 9 位小数的纪元秒数。请根据resultSetMetaData.rowType[].type解析;每种类型的完整编码表请参阅references/api.md(Result metadata & partitions)。- 任何会扫描数据的操作都需要指定仓库。 仅涉及元数据的语句(大多数
SHOW命令、DESCRIBE)不需要仓库,但SELECT需要。未指定仓库时会返回明确的错误消息。对已暂停仓库的查询会自动恢复该仓库(会有启动延迟),且每次恢复都会产生 60 s 的最低计费时长(按每次仓库启动计费,而非每个查询)——请尽可能将小型查询批量处理。
深入了解
references/api.md 提供了更完整的参考资料——包括完整的请求正文字段、resultSetMetaData 结构、所有 parameters 会话选项、多语句和子句柄机制、监控端点(/api/v2/statements/{handle} 状态值),以及绑定类型和 VARIANT/ARRAY 处理的说明。如果你需要了解以上内容未涵盖事项的确切结构,请阅读该文档。