@keystrokehq/snowflake 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +9 -233
- package/package.json +39 -171
- package/dist/accounts/index.d.mts +0 -40
- package/dist/accounts/index.mjs +0 -51
- package/dist/bulk/index.d.mts +0 -71
- package/dist/bulk/index.mjs +0 -65
- package/dist/catalog/index.d.mts +0 -144
- package/dist/catalog/index.mjs +0 -158
- package/dist/client/index.d.mts +0 -44
- package/dist/client/index.mjs +0 -273
- package/dist/common-DfpCwJx5.mjs +0 -92
- package/dist/credential-sets/index.d.mts +0 -3
- package/dist/credential-sets/index.mjs +0 -4
- package/dist/databases/index.d.mts +0 -2
- package/dist/databases/index.mjs +0 -3
- package/dist/errors-60AsF7Su.mjs +0 -95
- package/dist/events/index.d.mts +0 -382
- package/dist/events/index.mjs +0 -291
- package/dist/file-formats/index.d.mts +0 -39
- package/dist/file-formats/index.mjs +0 -48
- package/dist/functions/index.d.mts +0 -53
- package/dist/functions/index.mjs +0 -56
- package/dist/grants/index.d.mts +0 -93
- package/dist/grants/index.mjs +0 -102
- package/dist/index.d.mts +0 -4
- package/dist/index.mjs +0 -6
- package/dist/operations/index.d.mts +0 -1
- package/dist/operations/index.mjs +0 -1
- package/dist/org-admin/index.d.mts +0 -80
- package/dist/org-admin/index.mjs +0 -79
- package/dist/pipes/index.d.mts +0 -119
- package/dist/pipes/index.mjs +0 -106
- package/dist/procedures/index.d.mts +0 -61
- package/dist/procedures/index.mjs +0 -79
- package/dist/results/index.d.mts +0 -16
- package/dist/results/index.mjs +0 -64
- package/dist/retry-B4-9MZIp.mjs +0 -164
- package/dist/retry-w7cTp1QL.d.mts +0 -10
- package/dist/roles/index.d.mts +0 -60
- package/dist/roles/index.mjs +0 -74
- package/dist/rows/index.d.mts +0 -106
- package/dist/rows/index.mjs +0 -223
- package/dist/schemas/index.d.mts +0 -2
- package/dist/schemas/index.mjs +0 -4
- package/dist/schemas-catalog/index.d.mts +0 -2
- package/dist/schemas-catalog/index.mjs +0 -3
- package/dist/shares/index.d.mts +0 -56
- package/dist/shares/index.mjs +0 -77
- package/dist/snowflake-s3-sns-webhook.credential-set-D6bLAWy5.d.mts +0 -50
- package/dist/snowflake-s3-sns-webhook.credential-set-DzR7oWNK.mjs +0 -44
- package/dist/snowflake.credential-set-BxT7Gf3A.d.mts +0 -114
- package/dist/snowflake.credential-set-DE3gXf-a.mjs +0 -106
- package/dist/sql/index.d.mts +0 -84
- package/dist/sql/index.mjs +0 -209
- package/dist/sql-options-2k5xQ-oS.d.mts +0 -32
- package/dist/sql-options-D6GEa21F.mjs +0 -79
- package/dist/sql-safety-BZpAPsq6.mjs +0 -56
- package/dist/stages/index.d.mts +0 -64
- package/dist/stages/index.mjs +0 -81
- package/dist/statements-BumhMhN4.mjs +0 -81
- package/dist/statements-DJL0qVNA.d.mts +0 -238
- package/dist/status-page/index.d.mts +0 -510
- package/dist/status-page/index.mjs +0 -261
- package/dist/streaming/index.d.mts +0 -70
- package/dist/streaming/index.mjs +0 -56
- package/dist/streams/index.d.mts +0 -71
- package/dist/streams/index.mjs +0 -78
- package/dist/tables/index.d.mts +0 -2
- package/dist/tables/index.mjs +0 -3
- package/dist/tasks/index.d.mts +0 -79
- package/dist/tasks/index.mjs +0 -104
- package/dist/triggers/index.d.mts +0 -381
- package/dist/triggers/index.mjs +0 -1100
- package/dist/users/index.d.mts +0 -61
- package/dist/users/index.mjs +0 -67
- package/dist/verification/index.d.mts +0 -201
- package/dist/verification/index.mjs +0 -512
- package/dist/views/index.d.mts +0 -2
- package/dist/views/index.mjs +0 -3
- package/dist/warehouses/index.d.mts +0 -68
- package/dist/warehouses/index.mjs +0 -101
package/dist/sql/index.mjs
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import { a as submitStatementInputSchema, n as statementResponseSchema, o as partitionResponseSchema, r as statementStatus } from "../statements-BumhMhN4.mjs";
|
|
2
|
-
import { n as SnowflakeSqlError, t as SnowflakeApiError } from "../errors-60AsF7Su.mjs";
|
|
3
|
-
import { hydrateRows } from "../results/index.mjs";
|
|
4
|
-
|
|
5
|
-
//#region src/sql/index.ts
|
|
6
|
-
const STATEMENTS_PATH = "/api/v2/statements";
|
|
7
|
-
/**
|
|
8
|
-
* Submit a statement. When `async: true` (or the server chooses async) we
|
|
9
|
-
* return the inflated response envelope along with the `pending` status so
|
|
10
|
-
* callers can poll via {@link getStatement} / {@link waitForStatement}.
|
|
11
|
-
*
|
|
12
|
-
* The SQL API itself keys idempotency on the `requestId` query param which
|
|
13
|
-
* {@link SnowflakeClient} auto-populates — callers can override it per
|
|
14
|
-
* request when they need deterministic retries.
|
|
15
|
-
*/
|
|
16
|
-
async function submitStatement(client, input, options = {}) {
|
|
17
|
-
const parsed = submitStatementInputSchema.parse(input);
|
|
18
|
-
const query = {};
|
|
19
|
-
if (options.async) query.async = true;
|
|
20
|
-
const response = await client.requestRaw(STATEMENTS_PATH, {
|
|
21
|
-
method: "POST",
|
|
22
|
-
body: parsed,
|
|
23
|
-
query,
|
|
24
|
-
requestId: options.requestId
|
|
25
|
-
});
|
|
26
|
-
const body = await response.json();
|
|
27
|
-
const envelope = statementResponseSchema.parse(body);
|
|
28
|
-
return {
|
|
29
|
-
response: envelope,
|
|
30
|
-
httpStatus: response.status,
|
|
31
|
-
status: statementStatus({
|
|
32
|
-
httpStatus: response.status,
|
|
33
|
-
code: envelope.code
|
|
34
|
-
})
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Fetch the current status of a previously submitted async statement. Returns
|
|
39
|
-
* both the envelope and the derived `status` bucket.
|
|
40
|
-
*/
|
|
41
|
-
async function getStatement(client, handle, options = {}) {
|
|
42
|
-
const query = options.partition !== void 0 ? { partition: options.partition } : void 0;
|
|
43
|
-
const response = await client.requestRaw(`${STATEMENTS_PATH}/${encodeURIComponent(handle)}`, {
|
|
44
|
-
method: "GET",
|
|
45
|
-
query
|
|
46
|
-
});
|
|
47
|
-
const body = await response.json();
|
|
48
|
-
const envelope = statementResponseSchema.parse(body);
|
|
49
|
-
return {
|
|
50
|
-
response: envelope,
|
|
51
|
-
httpStatus: response.status,
|
|
52
|
-
status: statementStatus({
|
|
53
|
-
httpStatus: response.status,
|
|
54
|
-
code: envelope.code
|
|
55
|
-
})
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* POST `/cancel`. Snowflake responds with `code: 333335` on success — we
|
|
60
|
-
* normalise anything else into a {@link SnowflakeApiError}.
|
|
61
|
-
*/
|
|
62
|
-
async function cancelStatement(client, handle) {
|
|
63
|
-
const response = await client.requestRaw(`${STATEMENTS_PATH}/${encodeURIComponent(handle)}/cancel`, {
|
|
64
|
-
method: "POST",
|
|
65
|
-
retry: false
|
|
66
|
-
});
|
|
67
|
-
if (!response.ok) {
|
|
68
|
-
const text = await response.text().catch(() => "");
|
|
69
|
-
throw new SnowflakeApiError({
|
|
70
|
-
kind: "http",
|
|
71
|
-
status: response.status,
|
|
72
|
-
message: `Failed to cancel statement ${handle}: ${text || response.statusText}`
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Submit + wait + fetch all partitions in one go. The returned rows are
|
|
78
|
-
* hydrated into objects keyed by column name by default; pass
|
|
79
|
-
* `hydrate: false` to get the raw tuple form.
|
|
80
|
-
*/
|
|
81
|
-
async function executeSql(client, input, options = {}) {
|
|
82
|
-
const submitted = await submitStatement(client, input, options);
|
|
83
|
-
let envelope = submitted.response;
|
|
84
|
-
let status = submitted.status;
|
|
85
|
-
if (status === "pending") {
|
|
86
|
-
envelope = await waitForStatementEnvelope(client, envelope.statementHandle, options);
|
|
87
|
-
status = statementStatus({
|
|
88
|
-
httpStatus: 200,
|
|
89
|
-
code: envelope.code
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
if (status === "cancelled") throw new SnowflakeSqlError({
|
|
93
|
-
kind: "cancelled",
|
|
94
|
-
status: 200,
|
|
95
|
-
message: envelope.message ?? "Statement was cancelled",
|
|
96
|
-
code: envelope.code,
|
|
97
|
-
sqlState: envelope.sqlState,
|
|
98
|
-
statementHandle: envelope.statementHandle
|
|
99
|
-
});
|
|
100
|
-
if (status === "failed") throw new SnowflakeSqlError({
|
|
101
|
-
kind: "sql",
|
|
102
|
-
status: 422,
|
|
103
|
-
message: envelope.message ?? "Statement failed",
|
|
104
|
-
code: envelope.code,
|
|
105
|
-
sqlState: envelope.sqlState,
|
|
106
|
-
statementHandle: envelope.statementHandle
|
|
107
|
-
});
|
|
108
|
-
const columns = envelope.resultSetMetaData?.rowType ?? [];
|
|
109
|
-
const partitionCount = envelope.resultSetMetaData?.partitionInfo?.length ?? (envelope.data ? 1 : 0);
|
|
110
|
-
const firstRows = envelope.data ?? [];
|
|
111
|
-
const remainder = [];
|
|
112
|
-
for (let i = 1; i < partitionCount; i += 1) {
|
|
113
|
-
const partition = await getStatementResultPartition(client, envelope.statementHandle, i);
|
|
114
|
-
remainder.push(...partition.data ?? []);
|
|
115
|
-
}
|
|
116
|
-
const rawRows = [...firstRows, ...remainder];
|
|
117
|
-
const rows = options.hydrate !== false ? hydrateRows(rawRows, columns) : rawRows;
|
|
118
|
-
return {
|
|
119
|
-
handle: envelope.statementHandle,
|
|
120
|
-
columns,
|
|
121
|
-
rows,
|
|
122
|
-
envelope,
|
|
123
|
-
partitions: Math.max(1, partitionCount)
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Composio `SNOWFLAKE_BASIC_RUN_QUERY` parity — requires an explicit
|
|
128
|
-
* database + schema to match the published BASIC shape.
|
|
129
|
-
*/
|
|
130
|
-
async function runQuery(client, input, options = {}) {
|
|
131
|
-
return executeSql(client, {
|
|
132
|
-
statement: input.statement,
|
|
133
|
-
database: input.database,
|
|
134
|
-
schema: input.schema_name,
|
|
135
|
-
warehouse: input.warehouse,
|
|
136
|
-
role: input.role
|
|
137
|
-
}, options);
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Fetch a single partition for a known handle.
|
|
141
|
-
*/
|
|
142
|
-
async function getStatementResultPartition(client, handle, partition) {
|
|
143
|
-
const body = await client.request(`${STATEMENTS_PATH}/${encodeURIComponent(handle)}`, {
|
|
144
|
-
method: "GET",
|
|
145
|
-
query: { partition }
|
|
146
|
-
});
|
|
147
|
-
return partitionResponseSchema.parse(body);
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Async iterator over every partition for a handle. Useful for streaming
|
|
151
|
-
* large result sets into downstream pipelines.
|
|
152
|
-
*/
|
|
153
|
-
async function* iterateStatementResult(client, handle, options = {}) {
|
|
154
|
-
const first = await getStatementResultPartition(client, handle, 0);
|
|
155
|
-
yield first;
|
|
156
|
-
const total = first.resultSetMetaData?.partitionInfo?.length ?? 0;
|
|
157
|
-
const cap = Math.min(total, options.maxPartitions ?? total);
|
|
158
|
-
for (let i = 1; i < cap; i += 1) yield await getStatementResultPartition(client, handle, i);
|
|
159
|
-
}
|
|
160
|
-
async function waitForStatementEnvelope(client, handle, options) {
|
|
161
|
-
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
|
162
|
-
const maxWaitMs = options.maxWaitMs ?? 300 * 1e3;
|
|
163
|
-
const now = options.now ?? (() => Date.now());
|
|
164
|
-
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
165
|
-
const deadline = now() + maxWaitMs;
|
|
166
|
-
for (;;) {
|
|
167
|
-
const result = await getStatement(client, handle);
|
|
168
|
-
if (result.status !== "pending") return result.response;
|
|
169
|
-
if (now() >= deadline) throw new SnowflakeApiError({
|
|
170
|
-
kind: "timeout",
|
|
171
|
-
status: 0,
|
|
172
|
-
message: `Statement ${handle} did not reach a terminal status within ${maxWaitMs}ms`,
|
|
173
|
-
retryable: false
|
|
174
|
-
});
|
|
175
|
-
await sleep(pollIntervalMs);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Blocking helper: polls `getStatement` until the statement reaches a
|
|
180
|
-
* terminal status or the `maxWaitMs` budget elapses. Returns the terminal
|
|
181
|
-
* envelope (throws for `failed` / `cancelled`).
|
|
182
|
-
*/
|
|
183
|
-
async function waitForStatement(client, handle, options = {}) {
|
|
184
|
-
const envelope = await waitForStatementEnvelope(client, handle, options);
|
|
185
|
-
const status = statementStatus({
|
|
186
|
-
httpStatus: 200,
|
|
187
|
-
code: envelope.code
|
|
188
|
-
});
|
|
189
|
-
if (status === "failed") throw new SnowflakeSqlError({
|
|
190
|
-
kind: "sql",
|
|
191
|
-
status: 422,
|
|
192
|
-
message: envelope.message ?? "Statement failed",
|
|
193
|
-
code: envelope.code,
|
|
194
|
-
sqlState: envelope.sqlState,
|
|
195
|
-
statementHandle: envelope.statementHandle
|
|
196
|
-
});
|
|
197
|
-
if (status === "cancelled") throw new SnowflakeSqlError({
|
|
198
|
-
kind: "cancelled",
|
|
199
|
-
status: 200,
|
|
200
|
-
message: envelope.message ?? "Statement was cancelled",
|
|
201
|
-
code: envelope.code,
|
|
202
|
-
sqlState: envelope.sqlState,
|
|
203
|
-
statementHandle: envelope.statementHandle
|
|
204
|
-
});
|
|
205
|
-
return envelope;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
//#endregion
|
|
209
|
-
export { cancelStatement, executeSql, getStatement, getStatementResultPartition, iterateStatementResult, runQuery, submitStatement, waitForStatement };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
//#region src/sql-options.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Option-list composition helpers for Snowflake DDL/DML.
|
|
4
|
-
*
|
|
5
|
-
* Most CREATE/ALTER statements (stages, file formats, pipes, tasks, COPY
|
|
6
|
-
* options, warehouses) accept option lists like:
|
|
7
|
-
*
|
|
8
|
-
* FILE_FORMAT = (TYPE = 'CSV' FIELD_DELIMITER = '|' SKIP_HEADER = 1)
|
|
9
|
-
* CREDENTIALS = (AWS_KEY_ID = 'abc' AWS_SECRET_KEY = 'xyz')
|
|
10
|
-
* COPY_OPTIONS = (ON_ERROR = 'CONTINUE' PURGE = TRUE)
|
|
11
|
-
*
|
|
12
|
-
* These helpers compose those lists safely:
|
|
13
|
-
* - String values are quoted with {@link quoteLiteral}.
|
|
14
|
-
* - Numbers, booleans, and raw-marked values pass through verbatim.
|
|
15
|
-
* - Nested option maps render as parenthesised sub-lists.
|
|
16
|
-
* - Ordering is preserved in insertion order.
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* A scalar that can appear on the RHS of an option. Pre-built raw SQL can
|
|
20
|
-
* be passed via {@link rawOption} when callers need to inject a keyword
|
|
21
|
-
* (e.g. `AUTO_REFRESH = TRUE`, `ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE')`).
|
|
22
|
-
*/
|
|
23
|
-
type OptionScalar = string | number | bigint | boolean | null | undefined | RawSql | OptionMap | readonly OptionScalar[];
|
|
24
|
-
interface OptionMap {
|
|
25
|
-
readonly [key: string]: OptionScalar;
|
|
26
|
-
}
|
|
27
|
-
/** Marker type for a pre-formatted SQL fragment. */
|
|
28
|
-
interface RawSql {
|
|
29
|
-
readonly __rawSql: string;
|
|
30
|
-
}
|
|
31
|
-
//#endregion
|
|
32
|
-
export { OptionMap as t };
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { a as quoteLiteral } from "./sql-safety-BZpAPsq6.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/sql-options.ts
|
|
4
|
-
/**
|
|
5
|
-
* Option-list composition helpers for Snowflake DDL/DML.
|
|
6
|
-
*
|
|
7
|
-
* Most CREATE/ALTER statements (stages, file formats, pipes, tasks, COPY
|
|
8
|
-
* options, warehouses) accept option lists like:
|
|
9
|
-
*
|
|
10
|
-
* FILE_FORMAT = (TYPE = 'CSV' FIELD_DELIMITER = '|' SKIP_HEADER = 1)
|
|
11
|
-
* CREDENTIALS = (AWS_KEY_ID = 'abc' AWS_SECRET_KEY = 'xyz')
|
|
12
|
-
* COPY_OPTIONS = (ON_ERROR = 'CONTINUE' PURGE = TRUE)
|
|
13
|
-
*
|
|
14
|
-
* These helpers compose those lists safely:
|
|
15
|
-
* - String values are quoted with {@link quoteLiteral}.
|
|
16
|
-
* - Numbers, booleans, and raw-marked values pass through verbatim.
|
|
17
|
-
* - Nested option maps render as parenthesised sub-lists.
|
|
18
|
-
* - Ordering is preserved in insertion order.
|
|
19
|
-
*/
|
|
20
|
-
function isRawSql(value) {
|
|
21
|
-
return typeof value === "object" && value !== null && typeof value.__rawSql === "string";
|
|
22
|
-
}
|
|
23
|
-
function isOptionMap(value) {
|
|
24
|
-
if (value === null || typeof value !== "object") return false;
|
|
25
|
-
if (Array.isArray(value)) return false;
|
|
26
|
-
if (isRawSql(value)) return false;
|
|
27
|
-
if (value instanceof Date) return false;
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
function formatScalar(value) {
|
|
31
|
-
if (value === void 0) return void 0;
|
|
32
|
-
if (value === null) return "NULL";
|
|
33
|
-
if (isRawSql(value)) return value.__rawSql;
|
|
34
|
-
if (typeof value === "string") return quoteLiteral(value);
|
|
35
|
-
if (typeof value === "number") {
|
|
36
|
-
if (!Number.isFinite(value)) throw new TypeError("Cannot serialise NaN/Infinity as a Snowflake option value");
|
|
37
|
-
return value.toString();
|
|
38
|
-
}
|
|
39
|
-
if (typeof value === "bigint") return value.toString();
|
|
40
|
-
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
41
|
-
if (Array.isArray(value)) return `(${value.map((item) => formatScalar(item)).filter((item) => item !== void 0).join(", ")})`;
|
|
42
|
-
if (isOptionMap(value)) return formatOptionGroup(value);
|
|
43
|
-
throw new TypeError(`Unsupported Snowflake option value: ${typeof value}`);
|
|
44
|
-
}
|
|
45
|
-
/** Render a map like `(KEY = value KEY2 = 'literal')`. Empty maps render as `()`. */
|
|
46
|
-
function formatOptionGroup(map) {
|
|
47
|
-
const parts = [];
|
|
48
|
-
for (const [key, value] of Object.entries(map)) {
|
|
49
|
-
const rendered = formatScalar(value);
|
|
50
|
-
if (rendered === void 0) continue;
|
|
51
|
-
parts.push(`${key.toUpperCase()} = ${rendered}`);
|
|
52
|
-
}
|
|
53
|
-
return `(${parts.join(" ")})`;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Render a map as a space-separated option list (no wrapping parens). This
|
|
57
|
-
* is the form used directly in `CREATE STAGE ... URL='...' CREDENTIALS=(..)`
|
|
58
|
-
* where top-level options sit alongside the object name.
|
|
59
|
-
*/
|
|
60
|
-
function formatOptions(map) {
|
|
61
|
-
const parts = [];
|
|
62
|
-
for (const [key, value] of Object.entries(map)) {
|
|
63
|
-
const rendered = formatScalar(value);
|
|
64
|
-
if (rendered === void 0) continue;
|
|
65
|
-
parts.push(`${key.toUpperCase()} = ${rendered}`);
|
|
66
|
-
}
|
|
67
|
-
return parts.join(" ");
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Render a nested option group as ` KEY = (...)` — returns empty string when
|
|
71
|
-
* the map is undefined so callers can compose optional clauses inline.
|
|
72
|
-
*/
|
|
73
|
-
function renderOptionGroup(key, map) {
|
|
74
|
-
if (!map) return "";
|
|
75
|
-
return ` ${key.toUpperCase()} = ${formatOptionGroup(map)}`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
//#endregion
|
|
79
|
-
export { renderOptionGroup as n, formatOptions as t };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
//#region src/sql-safety.ts
|
|
2
|
-
/**
|
|
3
|
-
* SQL identifier + literal escaping utilities.
|
|
4
|
-
*
|
|
5
|
-
* Snowflake follows ANSI rules:
|
|
6
|
-
* - Identifiers: wrap in `"..."`; internal `"` is escaped as `""`.
|
|
7
|
-
* - String literals: wrap in `'...'`; internal `'` is escaped as `''`.
|
|
8
|
-
* - Numeric, boolean, null literals are written raw.
|
|
9
|
-
*
|
|
10
|
-
* Every domain module that composes SQL goes through these helpers — they
|
|
11
|
-
* are the package's sole defense against SQL injection from operation
|
|
12
|
-
* inputs that name identifiers or match patterns.
|
|
13
|
-
*/
|
|
14
|
-
const UNQUOTED_IDENT = /^[A-Z_][A-Z0-9_$]*$/u;
|
|
15
|
-
/**
|
|
16
|
-
* Return a double-quoted form of the identifier, escaping embedded
|
|
17
|
-
* double-quotes. When `allowUnquoted` is true and the input is already a
|
|
18
|
-
* canonical unquoted identifier, return it verbatim so the resulting SQL
|
|
19
|
-
* is more readable.
|
|
20
|
-
*/
|
|
21
|
-
function quoteIdentifier(name, options = {}) {
|
|
22
|
-
if (options.allowUnquoted && UNQUOTED_IDENT.test(name)) return name;
|
|
23
|
-
return `"${name.replaceAll("\"", "\"\"")}"`;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Build a fully-qualified name from up to three segments. Each segment is
|
|
27
|
-
* independently quoted. Missing leading segments are omitted.
|
|
28
|
-
*/
|
|
29
|
-
function buildFqn(...segments) {
|
|
30
|
-
return segments.filter((segment) => segment !== void 0 && segment.length > 0).map((segment) => quoteIdentifier(segment)).join(".");
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Quote a Snowflake string literal, doubling embedded single quotes.
|
|
34
|
-
*/
|
|
35
|
-
function quoteLiteral(value) {
|
|
36
|
-
return `'${value.replaceAll("'", "''")}'`;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Compose `LIKE '<pattern>'` when `pattern` is provided, empty string
|
|
40
|
-
* otherwise. Pattern is escaped as a SQL literal.
|
|
41
|
-
*/
|
|
42
|
-
function likeClause(pattern) {
|
|
43
|
-
return pattern ? ` LIKE ${quoteLiteral(pattern)}` : "";
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Compose `IN <scopeKeyword> <quoted identifier>` clause (e.g. "IN DATABASE
|
|
47
|
-
* MYDB"). Caller supplies a pre-joined identifier (fqn already built).
|
|
48
|
-
*/
|
|
49
|
-
function inClause(scopeKeyword, target) {
|
|
50
|
-
if (!target) return "";
|
|
51
|
-
if (scopeKeyword === "ACCOUNT") return " IN ACCOUNT";
|
|
52
|
-
return ` IN ${scopeKeyword} ${target}`;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
//#endregion
|
|
56
|
-
export { quoteLiteral as a, quoteIdentifier as i, inClause as n, likeClause as r, buildFqn as t };
|
package/dist/stages/index.d.mts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { SnowflakeClient } from "../client/index.mjs";
|
|
2
|
-
import { d as HydratedRow } from "../statements-DJL0qVNA.mjs";
|
|
3
|
-
import { ExecuteSqlResult } from "../sql/index.mjs";
|
|
4
|
-
import { t as OptionMap } from "../sql-options-2k5xQ-oS.mjs";
|
|
5
|
-
import { CatalogResult, ShowScopedOptions, showStages } from "../catalog/index.mjs";
|
|
6
|
-
|
|
7
|
-
//#region src/stages/index.d.ts
|
|
8
|
-
interface StageRef {
|
|
9
|
-
readonly database?: string;
|
|
10
|
-
readonly schema?: string;
|
|
11
|
-
readonly name: string;
|
|
12
|
-
}
|
|
13
|
-
interface StageContext {
|
|
14
|
-
readonly warehouse?: string;
|
|
15
|
-
readonly role?: string;
|
|
16
|
-
}
|
|
17
|
-
/** `@<fqn>` form used inside LIST / REMOVE / COPY INTO commands. */
|
|
18
|
-
declare function stagePath(ref: StageRef, pathSuffix?: string): string;
|
|
19
|
-
interface CreateStageOptions extends StageRef, StageContext {
|
|
20
|
-
readonly orReplace?: boolean;
|
|
21
|
-
readonly ifNotExists?: boolean;
|
|
22
|
-
readonly temporary?: boolean;
|
|
23
|
-
/** `URL='...'`, `STORAGE_INTEGRATION='...'`, `CREDENTIALS=(...)`, `ENCRYPTION=(...)`, etc. */
|
|
24
|
-
readonly options?: OptionMap;
|
|
25
|
-
readonly fileFormat?: OptionMap;
|
|
26
|
-
readonly copyOptions?: OptionMap;
|
|
27
|
-
readonly comment?: string;
|
|
28
|
-
}
|
|
29
|
-
declare function createStage(client: SnowflakeClient, options: CreateStageOptions): Promise<void>;
|
|
30
|
-
interface AlterStageOptions extends StageRef, StageContext {
|
|
31
|
-
readonly rename?: StageRef;
|
|
32
|
-
readonly set?: OptionMap;
|
|
33
|
-
readonly unset?: readonly string[];
|
|
34
|
-
readonly refreshSubpath?: string;
|
|
35
|
-
readonly comment?: string;
|
|
36
|
-
}
|
|
37
|
-
declare function alterStage(client: SnowflakeClient, options: AlterStageOptions): Promise<void>;
|
|
38
|
-
interface DropStageOptions extends StageRef, StageContext {
|
|
39
|
-
readonly ifExists?: boolean;
|
|
40
|
-
}
|
|
41
|
-
declare function dropStage(client: SnowflakeClient, options: DropStageOptions): Promise<void>;
|
|
42
|
-
interface DescribeStageOptions extends StageRef, StageContext {}
|
|
43
|
-
declare function describeStage(client: SnowflakeClient, options: DescribeStageOptions): Promise<readonly HydratedRow[]>;
|
|
44
|
-
interface ListStageFilesOptions extends StageRef, StageContext {
|
|
45
|
-
readonly pathSuffix?: string;
|
|
46
|
-
/** Regex pattern matched against file names. */
|
|
47
|
-
readonly pattern?: string;
|
|
48
|
-
}
|
|
49
|
-
declare function listStageFiles(client: SnowflakeClient, options: ListStageFilesOptions): Promise<readonly HydratedRow[]>;
|
|
50
|
-
interface RemoveStageFilesOptions extends StageRef, StageContext {
|
|
51
|
-
readonly pathSuffix?: string;
|
|
52
|
-
readonly pattern?: string;
|
|
53
|
-
}
|
|
54
|
-
declare function removeStageFiles(client: SnowflakeClient, options: RemoveStageFilesOptions): Promise<ExecuteSqlResult<HydratedRow | readonly unknown[]>>;
|
|
55
|
-
/**
|
|
56
|
-
* PUT is not supported by the Snowflake SQL API v2 — the API cannot accept
|
|
57
|
-
* raw file bytes. Callers needing a file upload should use a pre-signed S3
|
|
58
|
-
* URL + external stage, or drive `snowsql`/`snowflake-sdk` from a trusted
|
|
59
|
-
* context.
|
|
60
|
-
*/
|
|
61
|
-
declare function putFile(): never;
|
|
62
|
-
declare function getFile(): never;
|
|
63
|
-
//#endregion
|
|
64
|
-
export { AlterStageOptions, type CatalogResult, CreateStageOptions, DescribeStageOptions, DropStageOptions, ListStageFilesOptions, RemoveStageFilesOptions, type ShowScopedOptions, StageContext, StageRef, alterStage, createStage, describeStage, dropStage, getFile, listStageFiles, putFile, removeStageFiles, showStages, stagePath };
|
package/dist/stages/index.mjs
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { executeSql } from "../sql/index.mjs";
|
|
2
|
-
import { a as quoteLiteral, t as buildFqn } from "../sql-safety-BZpAPsq6.mjs";
|
|
3
|
-
import { n as renderOptionGroup, t as formatOptions } from "../sql-options-D6GEa21F.mjs";
|
|
4
|
-
import { showStages } from "../catalog/index.mjs";
|
|
5
|
-
|
|
6
|
-
//#region src/stages/index.ts
|
|
7
|
-
function stageFqn(ref) {
|
|
8
|
-
return buildFqn(ref.database, ref.schema, ref.name);
|
|
9
|
-
}
|
|
10
|
-
/** `@<fqn>` form used inside LIST / REMOVE / COPY INTO commands. */
|
|
11
|
-
function stagePath(ref, pathSuffix) {
|
|
12
|
-
const fqn = stageFqn(ref);
|
|
13
|
-
if (!pathSuffix) return `@${fqn}`;
|
|
14
|
-
return `@${fqn}${pathSuffix.startsWith("/") ? pathSuffix : `/${pathSuffix}`}`;
|
|
15
|
-
}
|
|
16
|
-
async function createStage(client, options) {
|
|
17
|
-
const prefix = `CREATE${options.orReplace ? " OR REPLACE" : ""}${options.temporary ? " TEMPORARY" : ""} STAGE${options.ifNotExists ? " IF NOT EXISTS" : ""}`;
|
|
18
|
-
const topOptions = options.options ? ` ${formatOptions(options.options)}` : "";
|
|
19
|
-
await executeSql(client, {
|
|
20
|
-
statement: `${prefix} ${stageFqn(options)}${topOptions}` + renderOptionGroup("FILE_FORMAT", options.fileFormat) + renderOptionGroup("COPY_OPTIONS", options.copyOptions) + (options.comment ? ` COMMENT = ${quoteLiteral(options.comment)}` : ""),
|
|
21
|
-
warehouse: options.warehouse,
|
|
22
|
-
role: options.role
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
async function alterStage(client, options) {
|
|
26
|
-
let tail;
|
|
27
|
-
if (options.rename) tail = `RENAME TO ${stageFqn(options.rename)}`;
|
|
28
|
-
else if (options.refreshSubpath !== void 0) tail = `REFRESH${options.refreshSubpath.length > 0 ? ` SUBPATH = ${quoteLiteral(options.refreshSubpath)}` : ""}`;
|
|
29
|
-
else if (options.set && Object.keys(options.set).length > 0) tail = `SET ${formatOptions(options.set)}`;
|
|
30
|
-
else if (options.unset && options.unset.length > 0) tail = `UNSET ${options.unset.map((k) => k.toUpperCase()).join(", ")}`;
|
|
31
|
-
else if (options.comment !== void 0) tail = `SET COMMENT = ${quoteLiteral(options.comment)}`;
|
|
32
|
-
else throw new Error("alterStage requires one of: rename / refreshSubpath / set / unset / comment");
|
|
33
|
-
await executeSql(client, {
|
|
34
|
-
statement: `ALTER STAGE ${stageFqn(options)} ${tail}`,
|
|
35
|
-
warehouse: options.warehouse,
|
|
36
|
-
role: options.role
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
async function dropStage(client, options) {
|
|
40
|
-
await executeSql(client, {
|
|
41
|
-
statement: `DROP STAGE${options.ifExists ? " IF EXISTS" : ""} ${stageFqn(options)}`,
|
|
42
|
-
warehouse: options.warehouse,
|
|
43
|
-
role: options.role
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
async function describeStage(client, options) {
|
|
47
|
-
return (await executeSql(client, {
|
|
48
|
-
statement: `DESCRIBE STAGE ${stageFqn(options)}`,
|
|
49
|
-
warehouse: options.warehouse,
|
|
50
|
-
role: options.role
|
|
51
|
-
})).rows;
|
|
52
|
-
}
|
|
53
|
-
async function listStageFiles(client, options) {
|
|
54
|
-
return (await executeSql(client, {
|
|
55
|
-
statement: `LIST ${stagePath(options, options.pathSuffix)}${options.pattern ? ` PATTERN = ${quoteLiteral(options.pattern)}` : ""}`,
|
|
56
|
-
warehouse: options.warehouse,
|
|
57
|
-
role: options.role
|
|
58
|
-
})).rows;
|
|
59
|
-
}
|
|
60
|
-
async function removeStageFiles(client, options) {
|
|
61
|
-
return executeSql(client, {
|
|
62
|
-
statement: `REMOVE ${stagePath(options, options.pathSuffix)}${options.pattern ? ` PATTERN = ${quoteLiteral(options.pattern)}` : ""}`,
|
|
63
|
-
warehouse: options.warehouse,
|
|
64
|
-
role: options.role
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* PUT is not supported by the Snowflake SQL API v2 — the API cannot accept
|
|
69
|
-
* raw file bytes. Callers needing a file upload should use a pre-signed S3
|
|
70
|
-
* URL + external stage, or drive `snowsql`/`snowflake-sdk` from a trusted
|
|
71
|
-
* context.
|
|
72
|
-
*/
|
|
73
|
-
function putFile() {
|
|
74
|
-
throw new Error("putFile is not supported by the REST integration — use a pre-signed stage URL or the official Snowflake driver to upload raw file bytes.");
|
|
75
|
-
}
|
|
76
|
-
function getFile() {
|
|
77
|
-
throw new Error("getFile is not supported by the REST integration — use a pre-signed stage URL or the official Snowflake driver to download raw file bytes.");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
//#endregion
|
|
81
|
-
export { alterStage, createStage, describeStage, dropStage, getFile, listStageFiles, putFile, removeStageFiles, showStages, stagePath };
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { c as sqlBindingsSchema, l as statementContextSchema, r as resultSetMetaDataSchema } from "./common-DfpCwJx5.mjs";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
//#region src/schemas/results.ts
|
|
5
|
-
/**
|
|
6
|
-
* Result-partition + row hydration schemas.
|
|
7
|
-
*
|
|
8
|
-
* A single partition response arrives under `GET /api/v2/statements/{handle}?partition={n}`
|
|
9
|
-
* and uses the same envelope as the submit response — just with `data`
|
|
10
|
-
* populated with the rows for that partition.
|
|
11
|
-
*/
|
|
12
|
-
const partitionResponseSchema = z.object({
|
|
13
|
-
statementHandle: z.string().optional(),
|
|
14
|
-
code: z.string().optional(),
|
|
15
|
-
message: z.string().optional(),
|
|
16
|
-
resultSetMetaData: resultSetMetaDataSchema.optional(),
|
|
17
|
-
data: z.array(z.array(z.unknown())).optional()
|
|
18
|
-
}).catchall(z.unknown());
|
|
19
|
-
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/schemas/statements.ts
|
|
22
|
-
/**
|
|
23
|
-
* Schemas for the Snowflake SQL API v2 statement endpoints.
|
|
24
|
-
*
|
|
25
|
-
* https://docs.snowflake.com/en/developer-guide/sql-api/submitting-requests
|
|
26
|
-
* https://docs.snowflake.com/en/developer-guide/sql-api/responses-errors
|
|
27
|
-
*/
|
|
28
|
-
/** Status code reported under `code` / `message` alongside the HTTP status. */
|
|
29
|
-
const statementStatusCodeSchema = z.string();
|
|
30
|
-
/**
|
|
31
|
-
* Input body for `POST /api/v2/statements`.
|
|
32
|
-
* `statement` is required. All other fields fall back to session / account
|
|
33
|
-
* defaults when omitted.
|
|
34
|
-
*/
|
|
35
|
-
const submitStatementInputSchema = z.object({
|
|
36
|
-
statement: z.string().min(1),
|
|
37
|
-
bindings: sqlBindingsSchema.optional()
|
|
38
|
-
}).merge(statementContextSchema).strict();
|
|
39
|
-
/**
|
|
40
|
-
* Response body for a submitted statement — either the inline result (200)
|
|
41
|
-
* or a pending handle (202).
|
|
42
|
-
*/
|
|
43
|
-
const statementResponseSchema = z.object({
|
|
44
|
-
code: statementStatusCodeSchema.optional(),
|
|
45
|
-
sqlState: z.string().optional(),
|
|
46
|
-
message: z.string().optional(),
|
|
47
|
-
statementHandle: z.string(),
|
|
48
|
-
statementStatusUrl: z.string().optional(),
|
|
49
|
-
createdOn: z.number().optional(),
|
|
50
|
-
requestId: z.string().optional(),
|
|
51
|
-
statementHandles: z.array(z.string()).optional(),
|
|
52
|
-
resultSetMetaData: resultSetMetaDataSchema.optional(),
|
|
53
|
-
data: z.array(z.array(z.unknown())).optional()
|
|
54
|
-
}).catchall(z.unknown());
|
|
55
|
-
/**
|
|
56
|
-
* Snowflake code ranges (SQL API v2):
|
|
57
|
-
* - 090002 — success (inline result)
|
|
58
|
-
* - 333333 — statement accepted (async)
|
|
59
|
-
* - 333334 — statement executing (still polling)
|
|
60
|
-
* - 333335 — user-triggered cancel succeeded
|
|
61
|
-
* - 390xxx / 3900xx / 0919xx / ... — SQL-level failures
|
|
62
|
-
*
|
|
63
|
-
* We map explicitly the two non-error status codes; everything else is
|
|
64
|
-
* treated as `failed` when the HTTP status is also an error, otherwise
|
|
65
|
-
* `done`.
|
|
66
|
-
*/
|
|
67
|
-
function statementStatus(input) {
|
|
68
|
-
if (input.httpStatus === 202) return "pending";
|
|
69
|
-
if (input.code === "333334" || input.code === "333333") return "pending";
|
|
70
|
-
if (input.code === "333335") return "cancelled";
|
|
71
|
-
if (input.httpStatus >= 400) return "failed";
|
|
72
|
-
return "done";
|
|
73
|
-
}
|
|
74
|
-
const cancelStatementResponseSchema = z.object({
|
|
75
|
-
code: z.string().optional(),
|
|
76
|
-
message: z.string().optional(),
|
|
77
|
-
statementHandle: z.string().optional()
|
|
78
|
-
}).catchall(z.unknown());
|
|
79
|
-
|
|
80
|
-
//#endregion
|
|
81
|
-
export { submitStatementInputSchema as a, statementStatusCodeSchema as i, statementResponseSchema as n, partitionResponseSchema as o, statementStatus as r, cancelStatementResponseSchema as t };
|