Build repository for the refreshed snowflake provider for StackQL, enabling SQL-based query and provisioning operations against the Snowflake REST APIs: the full control plane (databases, schemas, tables, views, warehouses, roles, users, grants, network policies, compute pools, services, stages, pipes, streams, tasks, tags, integrations, dynamic/event/Iceberg tables, notebooks, Streamlit apps, secrets, alerts), the SQL API data plane (statement submission and results), and the Cortex AI surface.
This repository is a fork of snowflakedb/snowflake-rest-api-specs - the vendor OpenAPI specs live in-repo under specifications/ and stay current via upstream fork sync (the synced upstream commit is recorded in provider-dev/config/spec_pin.json at every build). The provider build scaffolding is added on top of the fork, following the repository pattern of stackql-provider-k8s. The upstream fork's own readme is preserved as README.upstream.md.
Status: the full pipeline is built - 13 services, 75 resources, 303 operations generated from the pinned spec sync, with offline validation, meta-route, integration (mock server) and live smoke test layers, a Docusaurus docs microsite, and a stackql-deploy example stack. make all runs everything.
This rebuild is a breaking change relative to the published snowflake provider (v25.08.00322):
- Service names consolidate from 36 singular per-spec services to 13 plural service groups:
snowflake.database.databases->snowflake.databases.databases,snowflake.schema.schemas->snowflake.databases.schemas,snowflake.warehouse.*->snowflake.warehouses.*, and so on. The full mapping of vendor spec to service is recorded inprovider-dev/config/service_names.json. - Grants move to a dedicated
grantsservice with verb-shaped methods:snowflake.grant.privileges.grant_privilege->INSERT INTO snowflake.grants.grants,revoke_privilege->DELETE FROM snowflake.grants.grants,grants_to.list_grants_to->SELECT FROM snowflake.grants.grants. Bulk (ALL/FUTURE) grants and grant-option revocations are their own resources (group_grants,grant_options,group_grant_options) so every overloaded SQL verb keeps a unique required-parameter signature. - Method names are resource-scoped:
list_databases/fetch_database/create_database/delete_databasebecomelist/get/create/deleteondatabases.databases.create_or_alter_*PUT operations remain mapped toREPLACEascreate_or_alter. REPLACE(create-or-alter) statements address the target with a<singular>_nameWHERE parameter (WHERE database_name = 'X',WHERE warehouse_name = 'X') whileSET name = 'X'populates the request body - the create-or-alter body requiresnameand any-sdk routes a SQL column matching a declared path parameter to the path, so the PUT path parameter is renamed at pre-normalize (wire URL unchanged; see NOTES.md).- Request body columns move from
data__prefixed (data__name) to native wire property names (name,accounts) via the naive request body translator, consistent with thek8s,awsandazureproviders. - List responses gain object keys: bare-array list responses are wrapped at normalize time and each list method carries the matching
stackql_object_key($.databases,$.schemas,$.grants_to). - Field and parameter names present as snake_case throughout: camelCase response fields surface as snake_case column aliases via the provider-level
snake_case_aliasesconfig (statementHandle->statement_handle,resultSetMetaData->result_set_meta_data; value extraction still keys on the wire name), and camelCase path parameters are renamed at pre-process (granteeType->grantee_type,statementHandle->statement_handle- path template names never reach the wire). Query parameter names keep their wire casing (showLimit,fromName). - The
User-Agent,AcceptandX-Snowflake-Authorization-Token-Typeheader parameters no longer surface as columns (stripped at pre-normalize; in the published providerUser-Agentis a required parameter on every method). The HTTP client supplies a defaultUser-Agent, which is all the service checks for;X-Snowflake-Authorization-Token-Typereturns if key-pair JWT auth is added. - Coverage expands to the current vendor spec sync: artifact repositories, secrets, sequences, network rules, password policies, tags, Streamlit apps and the Cortex generic (Anthropic/OpenAI-compatible) endpoints are added. Spark Connect endpoints are excluded with a recorded reason.
- The
endpointserver variable (orgname-accountnameaccount identifier) andSNOWFLAKE_PATbearer authentication are unchanged.
Existing queries against the old provider will require updating. The old provider version remains available in the registry for pinning.
Authentication is a Snowflake programmatic access token (PAT) supplied as a bearer token via the SNOWFLAKE_PAT environment variable, created for a least-privileged service role. Key-pair JWT auth is a documented follow-up, not v1 scope.
export SNOWFLAKE_PAT=<your-pat>Every query addresses the account through the endpoint server variable - the orgname-accountname account identifier:
SELECT name, owner FROM snowflake.databases.databases WHERE endpoint = 'MYORG-MYACCT';| Operation pattern | StackQL verb | Resource / method |
|---|---|---|
| GET collection | SELECT |
<resource>.list |
| GET single | SELECT |
<resource>.get |
| POST create | INSERT |
<resource>.create |
| PUT create-or-alter | REPLACE |
<resource>.create_or_alter - SET name = 'X' ... WHERE <singular>_name = 'X' |
| DELETE | DELETE |
<resource>.delete |
POST actions (:resume, :suspend, :execute, :abort, :refresh, :clone, :undrop, ...) |
EXEC |
<resource>.<action> (subresource actions compose, e.g. databases.enable_replication) |
| grant privilege | INSERT |
grants.grants.grant, grants.group_grants.grant |
| revoke privilege | DELETE |
grants.grants.revoke (+ grant_options, group_grants, group_grant_options) |
| list grants | SELECT |
grants.grants.list_grants_to |
| Cortex inference (analyst message, Anthropic-compatible messages, OpenAI-compatible chat completions) | SELECT |
SELECT-over-POST: WHERE members feed the request body, the completion projects as columns (matches the anthropic/gemini provider pattern). The vendor spec declares the generic endpoints as opaque passthroughs; typed request/response schemas are injected at pre-normalize |
| SubmitStatement | INSERT + RETURNING |
sqlapi.statements.submit_statement |
| GetStatementStatus (incl. partition retrieval) | SELECT |
sqlapi.statements.get_statement_status |
| CancelStatement | DELETE |
sqlapi.statements.cancel_statement |
SSE-only responses (cortex fast-generation, inference:complete), Spark Connect |
skipped | streaming and protobuf protocols are out of scope |
Statement submission maps to INSERT ... RETURNING deliberately: it is an async-capable work-creation operation - submission runs work on a warehouse and mints a statement handle to poll (async=true returns the handle immediately; synchronous submission returns partition 0 inline with the same handle for the remaining partitions). That lifecycle - create, poll via statements.get_statement_status, cancel via DELETE - is INSERT-shaped, unlike the synchronous read-shaped Cortex inference endpoints which map to SELECT-over-POST. EXEC offers no projection advantage against the identical ResultSet body and its output is not SQL-composable. The full evaluation is in NOTES.md.
-- submit a statement against a warehouse and read the result
INSERT INTO snowflake.sqlapi.statements(statement, warehouse, database, "schema", endpoint)
SELECT 'select count(*) from lineitem', 'TESTWH', 'TESTDB', 'TPCH_SF1', 'MYORG-MYACCT'
RETURNING statement_handle, result_set_meta_data, data;- No pagination config is shipped. The vendor specs declare RFC 5988
Linkresponse headers on list endpoints, but the live control plane does not emit them (verified against a real account), and the current any-sdk release (stackql v0.10.582) hangs when a header response token is configured without a request token. List reads are single-call; the full findings and re-evaluation criteria are in NOTES.md. SELECT ... LIMIT nis pushed to the wire as theshowLimitquery parameter on the 31 list methods whose operation declares it (injected per method bypost_process.mjs; client-sideLIMITremains authoritative, so pushdown never changes results).- WHERE predicates that name a declared parameter (
like,fromName,history, path params) are pushed into the request automatically by any-sdk's name-based parameter matching; all other predicates filter client-side.WHERE fromName = '...'withLIMITgives explicit windowed reads where needed. - Known toolchain defect:
EXECof non-GET methods panics stackql v0.10.582 (see NOTES.md section 1b) - the 100 mappedEXECactions are correct in the provider docs and the test suites SKIP/XFAIL on the panic signature until a fixed binary ships.
Deterministic, re-runnable steps; manual mapping decisions are rules in scripts, never hand-edits to CSVs or specs. specifications/ is upstream fork content and is never modified. Every target is in the Makefile:
make deps # npm install
make build # pre-process -> split -> pre-normalize -> mappings -> normalize -> generate
make test # offline validation + integration (mock server) + meta-routes
make smoke # live smoke suite, locally generated provider (needs SNOWFLAKE_PAT)
make smoke-live # live smoke suite against the latest published provider
make docs # generate website docs from the generated provider
make website # build the docusaurus microsite
make all # everything above except the live smokesThe individual steps, for scoping or debugging:
npm run pre-process # inject shared components, rename reserved-word path params,
# validate with swagger-parser, record the spec pin
npm run build-inventory # endpoint inventory (provider-dev/config/endpoint_inventory.csv)
npm run split -- --provider-name snowflake --overwrite
npm run pre-normalize # snowflake-specific adjustments (operationId dedupe after the merge)
npm run generate-mappings -- --provider-name snowflake --input-dir provider-dev/source --output-dir provider-dev/config
npm run map-operations # add -- --services a,b,c to scope
npm run normalize -- --api-dir provider-dev/source
make generate # generate-provider with servers/auth/pagination config + post-processFour layers, cheapest first; run the first three after every regeneration:
node tests/offline_validation.mjs # SHOW/DESCRIBE assertions, no network
npm run test-integration # row-level + wire-level assertions against a
# mock Snowflake REST server (auth, pagination,
# lifecycle, grants round trip, statements)
make test-meta # meta-route suite against a local stackql server
python tests/smoke_test.py # live account, locally generated provider
python tests/smoke_test.py --live # live account, latest published providerThe smoke suite needs SNOWFLAKE_PAT (env var or .env) and the account identifier via --endpoint/SNOWFLAKE_ENDPOINT (orgname-accountname - find it with SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME();). All created objects use STACKQL_SMOKE_<stamp> names, the warehouse is X-Small with 60s auto-suspend, and prior-run breadcrumbs are swept first - a full run costs well under $1 in credits. Never point it at a production account.
specifications/ # vendor specs, synced from snowflakedb upstream (fork) - never hand-edited
collections/ # upstream Postman collections - retained for fork sync hygiene, unused by the build
provider-dev/
source/ # pre-processed + split per-service specs (build artifacts, regenerated)
config/ # spec_pin.json, service_names.json, endpoint_inventory.csv, all_services.csv
scripts/ # pre_process.mjs, pre_normalize.mjs, map_operations.mjs, post_process.mjs, ...
openapi/src/snowflake/ # generated provider output
docgen/provider-data/ # docs landing page content (headerContent1/2.txt)
bin/ # thin node/shell wrappers for npm scripts (mirrors stackql-provider-k8s)
tests/ # offline validation, integration (mock server), smoke (pystackql)
website/ # Docusaurus 3.10 microsite (vendored shared config)
examples/stackql-deploy/ # declarative example stack (database, warehouse, role, grants)
Makefile # make all = pipeline + tests + docs + site
NOTES.md # open questions, evidence and decisions
- The k8s repo's
split.mjsfans one spec out per API group; the Snowflake vendor layout is already one spec per resource area, sobin/split.mjsis a scripted regroup/merge into service specs instead, with deterministic conflict resolution for shared component names (details in the script header). - npm scripts invoke the
.mjsentry points throughnodedirectly (toolchain rule; the build machine is Windows) - the server lifecycle scripts remain bash. - The upstream fork's
Readme.mdis preserved asREADME.upstream.mdbecause this file replaces it at the repository root.