@malloydata/db-snowflake 0.0.219 → 0.0.220-dev241204170603
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/dist/snowflake_connection.d.ts +13 -2
- package/dist/snowflake_connection.js +197 -100
- package/dist/snowflake_connection.js.map +1 -1
- package/package.json +2 -2
- package/src/snowflake_connection.ts +219 -106
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RunSQLOptions, MalloyQueryData, QueryRunStats, Connection, PersistSQLResults, StreamingConnection, PooledConnection, SQLSourceDef, TableSourceDef, QueryDataRow, TestableConnection } from '@malloydata/malloy';
|
|
1
|
+
import { RunSQLOptions, MalloyQueryData, QueryRunStats, Connection, PersistSQLResults, StreamingConnection, PooledConnection, SQLSourceDef, TableSourceDef, QueryDataRow, TestableConnection, TinyParser } from '@malloydata/malloy';
|
|
2
2
|
import { BaseConnection } from '@malloydata/malloy/connection';
|
|
3
3
|
import { ConnectionOptions } from 'snowflake-sdk';
|
|
4
4
|
import { Options as PoolOptions } from 'generic-pool';
|
|
@@ -12,6 +12,13 @@ export interface SnowflakeConnectionOptions {
|
|
|
12
12
|
scratchSpace?: namespace;
|
|
13
13
|
queryOptions?: RunSQLOptions;
|
|
14
14
|
}
|
|
15
|
+
type PathChain = {
|
|
16
|
+
arrayRef: true;
|
|
17
|
+
next?: PathChain;
|
|
18
|
+
} | {
|
|
19
|
+
name: string;
|
|
20
|
+
next?: PathChain;
|
|
21
|
+
};
|
|
15
22
|
export declare class SnowflakeConnection extends BaseConnection implements Connection, PersistSQLResults, StreamingConnection, TestableConnection {
|
|
16
23
|
readonly name: string;
|
|
17
24
|
private readonly dialect;
|
|
@@ -30,10 +37,14 @@ export declare class SnowflakeConnection extends BaseConnection implements Conne
|
|
|
30
37
|
runSQL(sql: string, options?: RunSQLOptions): Promise<MalloyQueryData>;
|
|
31
38
|
runSQLStream(sqlCommand: string, options?: RunSQLOptions): AsyncIterableIterator<QueryDataRow>;
|
|
32
39
|
test(): Promise<void>;
|
|
33
|
-
private addFieldsToStructDef;
|
|
34
40
|
private schemaFromTablePath;
|
|
35
41
|
fetchTableSchema(tableKey: string, tablePath: string): Promise<TableSourceDef>;
|
|
36
42
|
fetchSelectSchema(sqlRef: SQLSourceDef): Promise<SQLSourceDef>;
|
|
37
43
|
manifestTemporaryTable(sqlCommand: string): Promise<string>;
|
|
38
44
|
}
|
|
45
|
+
export declare class PathParser extends TinyParser {
|
|
46
|
+
constructor(pathName: string);
|
|
47
|
+
getName(): string;
|
|
48
|
+
pathChain(): PathChain;
|
|
49
|
+
}
|
|
39
50
|
export {};
|
|
@@ -45,23 +45,137 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
45
45
|
return result;
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.SnowflakeConnection = void 0;
|
|
48
|
+
exports.PathParser = exports.SnowflakeConnection = void 0;
|
|
49
49
|
const crypto = __importStar(require("crypto"));
|
|
50
50
|
const malloy_1 = require("@malloydata/malloy");
|
|
51
51
|
const connection_1 = require("@malloydata/malloy/connection");
|
|
52
52
|
const snowflake_executor_1 = require("./snowflake_executor");
|
|
53
|
-
class
|
|
54
|
-
constructor(type,
|
|
55
|
-
this.
|
|
56
|
-
this.type = 'record';
|
|
57
|
-
this.isArray = false;
|
|
53
|
+
class SnowField {
|
|
54
|
+
constructor(name, type, dialect) {
|
|
55
|
+
this.name = name;
|
|
58
56
|
this.type = type;
|
|
59
|
-
this.
|
|
57
|
+
this.dialect = dialect;
|
|
58
|
+
}
|
|
59
|
+
fieldDef() {
|
|
60
|
+
return {
|
|
61
|
+
...this.dialect.sqlTypeToMalloyType(this.type),
|
|
62
|
+
name: this.name,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
walk(_path, _fieldType) {
|
|
66
|
+
throw new Error('SNOWWFLAKE SCHEMA PARSE ERROR: Should not walk through fields');
|
|
67
|
+
}
|
|
68
|
+
static make(name, fieldType, d) {
|
|
69
|
+
if (fieldType === 'array') {
|
|
70
|
+
return new SnowArray(name, d);
|
|
71
|
+
}
|
|
72
|
+
else if (fieldType === 'object') {
|
|
73
|
+
return new SnowObject(name, d);
|
|
74
|
+
}
|
|
75
|
+
return new SnowField(name, fieldType, d);
|
|
60
76
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
77
|
+
}
|
|
78
|
+
class SnowObject extends SnowField {
|
|
79
|
+
constructor(name, d) {
|
|
80
|
+
super(name, 'object', d);
|
|
81
|
+
this.fieldMap = new Map();
|
|
82
|
+
}
|
|
83
|
+
get fields() {
|
|
84
|
+
const fields = [];
|
|
85
|
+
for (const [_, fieldObj] of this.fieldMap) {
|
|
86
|
+
fields.push(fieldObj.fieldDef());
|
|
87
|
+
}
|
|
88
|
+
return fields;
|
|
89
|
+
}
|
|
90
|
+
fieldDef() {
|
|
91
|
+
const rec = {
|
|
92
|
+
type: 'record',
|
|
93
|
+
name: this.name,
|
|
94
|
+
fields: this.fields,
|
|
95
|
+
join: 'one',
|
|
96
|
+
dialect: this.dialect.name,
|
|
97
|
+
};
|
|
98
|
+
return rec;
|
|
99
|
+
}
|
|
100
|
+
walk(path, fieldType) {
|
|
101
|
+
if ('name' in path) {
|
|
102
|
+
const field = this.fieldMap.get(path.name);
|
|
103
|
+
if (path.next) {
|
|
104
|
+
if (field) {
|
|
105
|
+
field.walk(path.next, fieldType);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
throw new Error('SNOWFLAKE SCHEMA PARSER ERROR: Walk through undefined');
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// If we get multiple type for a field, ignore them, should
|
|
112
|
+
// which will do until we support viarant data
|
|
113
|
+
if (!field) {
|
|
114
|
+
this.fieldMap.set(path.name, SnowField.make(path.name, fieldType, this.dialect));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
throw new Error('SNOWFLAKE SCHEMA PARSER ERROR: Walk object reference through array reference');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
class SnowArray extends SnowField {
|
|
123
|
+
constructor(name, d) {
|
|
124
|
+
super(name, 'array', d);
|
|
125
|
+
this.arrayOf = 'unknown';
|
|
126
|
+
}
|
|
127
|
+
isArrayOf(type) {
|
|
128
|
+
if (this.arrayOf !== 'unknown') {
|
|
129
|
+
this.arrayOf = 'variant';
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.arrayOf = type;
|
|
133
|
+
if (type === 'object') {
|
|
134
|
+
this.objectChild = new SnowObject('', this.dialect);
|
|
135
|
+
}
|
|
136
|
+
else if (type === 'array') {
|
|
137
|
+
this.arrayChild = new SnowArray('', this.dialect);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
fieldDef() {
|
|
141
|
+
const arr = {
|
|
142
|
+
type: 'array',
|
|
143
|
+
name: this.name,
|
|
144
|
+
join: 'many',
|
|
145
|
+
dialect: this.dialect.name,
|
|
146
|
+
elementTypeDef: { type: 'string' },
|
|
147
|
+
fields: [],
|
|
148
|
+
};
|
|
149
|
+
if (this.objectChild) {
|
|
150
|
+
arr.fields = this.objectChild.fieldDef().fields;
|
|
151
|
+
arr.elementTypeDef = { type: 'record_element' };
|
|
152
|
+
}
|
|
153
|
+
else if (this.arrayChild) {
|
|
154
|
+
arr.elementTypeDef = this.arrayChild.fieldDef();
|
|
155
|
+
arr.fields = (0, malloy_1.arrayEachFields)(arr.elementTypeDef);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
arr.elementTypeDef = this.dialect.sqlTypeToMalloyType(this.arrayOf);
|
|
159
|
+
arr.fields = (0, malloy_1.arrayEachFields)(arr.elementTypeDef);
|
|
160
|
+
}
|
|
161
|
+
return arr;
|
|
162
|
+
}
|
|
163
|
+
walk(path, fieldType) {
|
|
164
|
+
if ('arrayRef' in path) {
|
|
165
|
+
if (path.next) {
|
|
166
|
+
const next = this.arrayChild || this.objectChild;
|
|
167
|
+
if (next) {
|
|
168
|
+
next.walk(path.next, fieldType);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
throw new Error('SNOWFLAKE SCHEMA PARSER ERROR: Array walk through leaf');
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this.isArrayOf(fieldType);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
throw new Error('SNOWFLAKE SCHEMA PARSER ERROR: Array walk through name');
|
|
65
179
|
}
|
|
66
180
|
}
|
|
67
181
|
class SnowflakeConnection extends connection_1.BaseConnection {
|
|
@@ -126,54 +240,6 @@ class SnowflakeConnection extends connection_1.BaseConnection {
|
|
|
126
240
|
async test() {
|
|
127
241
|
await this.executor.batch('SELECT 1 as one');
|
|
128
242
|
}
|
|
129
|
-
addFieldsToStructDef(structDef, structMap) {
|
|
130
|
-
if (structMap.fieldMap.size === 0)
|
|
131
|
-
return;
|
|
132
|
-
for (const [field, value] of structMap.fieldMap) {
|
|
133
|
-
const type = value.type;
|
|
134
|
-
const name = field;
|
|
135
|
-
// check for an array
|
|
136
|
-
if (value.isArray && type !== 'object') {
|
|
137
|
-
// Apparently there can only be arrays of integers, strings, or unknowns?
|
|
138
|
-
// TODO is this true or is this just all that got implemented?
|
|
139
|
-
const malloyType = type === 'integer'
|
|
140
|
-
? { type: 'number', numberType: 'integer' }
|
|
141
|
-
: type === 'varchar'
|
|
142
|
-
? { type: 'string' }
|
|
143
|
-
: { type: 'sql native', rawType: type };
|
|
144
|
-
const innerStructDef = {
|
|
145
|
-
type: 'array',
|
|
146
|
-
name,
|
|
147
|
-
dialect: this.dialectName,
|
|
148
|
-
join: 'many',
|
|
149
|
-
elementTypeDef: malloyType,
|
|
150
|
-
fields: (0, malloy_1.arrayEachFields)(malloyType),
|
|
151
|
-
};
|
|
152
|
-
structDef.fields.push(innerStructDef);
|
|
153
|
-
}
|
|
154
|
-
else if (type === 'object') {
|
|
155
|
-
const structParts = { name, dialect: this.dialectName, fields: [] };
|
|
156
|
-
const innerStructDef = value.isArray
|
|
157
|
-
? {
|
|
158
|
-
...structParts,
|
|
159
|
-
type: 'array',
|
|
160
|
-
elementTypeDef: { type: 'record_element' },
|
|
161
|
-
join: 'many',
|
|
162
|
-
}
|
|
163
|
-
: {
|
|
164
|
-
...structParts,
|
|
165
|
-
type: 'record',
|
|
166
|
-
join: 'one',
|
|
167
|
-
};
|
|
168
|
-
this.addFieldsToStructDef(innerStructDef, value);
|
|
169
|
-
structDef.fields.push(innerStructDef);
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
const malloyType = this.dialect.sqlTypeToMalloyType(type);
|
|
173
|
-
structDef.fields.push({ ...malloyType, name });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
243
|
async schemaFromTablePath(tablePath, structDef) {
|
|
178
244
|
var _a, _b;
|
|
179
245
|
const infoQuery = `DESCRIBE TABLE ${tablePath}`;
|
|
@@ -182,67 +248,46 @@ class SnowflakeConnection extends connection_1.BaseConnection {
|
|
|
182
248
|
const notVariant = new Map();
|
|
183
249
|
for (const row of rows) {
|
|
184
250
|
// data types look like `VARCHAR(1234)`
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const malloyType = this.dialect.sqlTypeToMalloyType(snowflakeDataType);
|
|
251
|
+
const snowflakeDataType = row['type']
|
|
252
|
+
.toLocaleLowerCase()
|
|
253
|
+
.split('(')[0];
|
|
189
254
|
const name = row['name'];
|
|
190
|
-
if (
|
|
255
|
+
if (['variant', 'array', 'object'].includes(snowflakeDataType)) {
|
|
191
256
|
variants.push(name);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
notVariant.set(name, true);
|
|
195
|
-
if (malloyType) {
|
|
196
|
-
s.fields.push({ ...malloyType, name });
|
|
197
257
|
}
|
|
198
258
|
else {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
name,
|
|
203
|
-
});
|
|
259
|
+
notVariant.set(name, true);
|
|
260
|
+
const malloyType = this.dialect.sqlTypeToMalloyType(snowflakeDataType);
|
|
261
|
+
structDef.fields.push({ ...malloyType, name });
|
|
204
262
|
}
|
|
205
263
|
}
|
|
206
|
-
//
|
|
264
|
+
// For these things, we need to sample the data to know the schema
|
|
207
265
|
if (variants.length > 0) {
|
|
208
266
|
const sampleQuery = `
|
|
209
|
-
|
|
210
|
-
|
|
267
|
+
SELECT regexp_replace(PATH, '\\[[0-9]+\\]', '[*]') as PATH, lower(TYPEOF(value)) as type
|
|
268
|
+
FROM (select object_construct(*) o from ${tablePath} limit 100)
|
|
211
269
|
,table(flatten(input => o, recursive => true)) as meta
|
|
212
|
-
|
|
213
|
-
|
|
270
|
+
GROUP BY 1,2
|
|
271
|
+
ORDER BY PATH;
|
|
214
272
|
`;
|
|
215
273
|
const fieldPathRows = await this.executor.batch(sampleQuery);
|
|
216
274
|
// take the schema in list form an convert it into a tree.
|
|
217
|
-
const
|
|
275
|
+
const rootObject = new SnowObject('__root__', this.dialect);
|
|
218
276
|
for (const f of fieldPathRows) {
|
|
219
277
|
const pathString = (_a = f['PATH']) === null || _a === void 0 ? void 0 : _a.valueOf().toString();
|
|
220
278
|
const fieldType = (_b = f['TYPE']) === null || _b === void 0 ? void 0 : _b.valueOf().toString();
|
|
221
279
|
if (pathString === undefined || fieldType === undefined)
|
|
222
280
|
continue;
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
281
|
+
const pathParser = new PathParser(pathString);
|
|
282
|
+
const path = pathParser.pathChain();
|
|
283
|
+
if ('name' in path && notVariant.get(path.name)) {
|
|
284
|
+
// Name will already be in the structdef
|
|
227
285
|
continue;
|
|
228
|
-
let index = 0;
|
|
229
|
-
for (const segment of path) {
|
|
230
|
-
let thisNode = parent.fieldMap.get(segment);
|
|
231
|
-
if (thisNode === undefined) {
|
|
232
|
-
thisNode = parent.addChild(segment, fieldType);
|
|
233
|
-
}
|
|
234
|
-
if (fieldType === 'array') {
|
|
235
|
-
thisNode.isArray = true;
|
|
236
|
-
// if this is the last
|
|
237
|
-
}
|
|
238
|
-
else if (index === path.length - 1) {
|
|
239
|
-
thisNode.type = fieldType;
|
|
240
|
-
}
|
|
241
|
-
parent = thisNode;
|
|
242
|
-
index += 1;
|
|
243
286
|
}
|
|
287
|
+
// Walk the path and mark the type at the end
|
|
288
|
+
rootObject.walk(path, fieldType);
|
|
244
289
|
}
|
|
245
|
-
|
|
290
|
+
structDef.fields.push(...rootObject.fields);
|
|
246
291
|
}
|
|
247
292
|
}
|
|
248
293
|
async fetchTableSchema(tableKey, tablePath) {
|
|
@@ -273,4 +318,56 @@ class SnowflakeConnection extends connection_1.BaseConnection {
|
|
|
273
318
|
}
|
|
274
319
|
}
|
|
275
320
|
exports.SnowflakeConnection = SnowflakeConnection;
|
|
321
|
+
class PathParser extends malloy_1.TinyParser {
|
|
322
|
+
constructor(pathName) {
|
|
323
|
+
super(pathName, {
|
|
324
|
+
quoted: /^'(\\'|[^'])*'/,
|
|
325
|
+
array_of: /^\[\*]/,
|
|
326
|
+
char: /^[[.\]]/,
|
|
327
|
+
number: /^\d+/,
|
|
328
|
+
word: /^\w+/,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
getName() {
|
|
332
|
+
const nameStart = this.next();
|
|
333
|
+
if (nameStart.type === 'word') {
|
|
334
|
+
return nameStart.text;
|
|
335
|
+
}
|
|
336
|
+
if (nameStart.type === '[') {
|
|
337
|
+
const quotedName = this.next('quoted');
|
|
338
|
+
this.next(']');
|
|
339
|
+
return quotedName.text;
|
|
340
|
+
}
|
|
341
|
+
throw this.parseError('Expected column name');
|
|
342
|
+
}
|
|
343
|
+
pathChain() {
|
|
344
|
+
const chain = { name: this.getName() };
|
|
345
|
+
let node = chain;
|
|
346
|
+
for (;;) {
|
|
347
|
+
const sep = this.next();
|
|
348
|
+
if (sep.type === 'eof') {
|
|
349
|
+
return chain;
|
|
350
|
+
}
|
|
351
|
+
if (sep.type === '.') {
|
|
352
|
+
node.next = { name: this.next('word').text };
|
|
353
|
+
node = node.next;
|
|
354
|
+
}
|
|
355
|
+
else if (sep.type === 'array_of') {
|
|
356
|
+
node.next = { arrayRef: true };
|
|
357
|
+
node = node.next;
|
|
358
|
+
}
|
|
359
|
+
else if (sep.type === '[') {
|
|
360
|
+
// Actually a dot access through a quoted name
|
|
361
|
+
const quoted = this.next('quoted');
|
|
362
|
+
node.next = { name: quoted.text };
|
|
363
|
+
node = node.next;
|
|
364
|
+
this.next(']');
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
throw this.parseError(`Unexpected ${sep.type}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
exports.PathParser = PathParser;
|
|
276
373
|
//# sourceMappingURL=snowflake_connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snowflake_connection.js","sourceRoot":"","sources":["../src/snowflake_connection.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,+CAAiC;AACjC,+
|
|
1
|
+
{"version":3,"file":"snowflake_connection.js","sourceRoot":"","sources":["../src/snowflake_connection.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,+CAAiC;AACjC,+CAoB4B;AAC5B,8DAA6D;AAE7D,6DAAuD;AAwBvD,MAAM,SAAS;IACb,YACW,IAAY,EACZ,IAAY,EACZ,OAAgB;QAFhB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,YAAO,GAAP,OAAO,CAAS;IACxB,CAAC;IACJ,QAAQ;QACN,OAAO;YACL,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9C,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAgB,EAAE,UAAkB;QACvC,MAAM,IAAI,KAAK,CACb,+DAA+D,CAChE,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,IAAY,EAAE,SAAiB,EAAE,CAAU;QACrD,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC1B,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAChC,CAAC;aAAM,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;IAC3C,CAAC;CACF;AAED,MAAM,UAAW,SAAQ,SAAS;IAEhC,YAAY,IAAY,EAAE,CAAU;QAClC,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAF3B,aAAQ,GAAG,IAAI,GAAG,EAAqB,CAAC;IAGxC,CAAC;IAED,IAAI,MAAM;QACR,MAAM,MAAM,GAAe,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,QAAQ;QACN,MAAM,GAAG,GAAc;YACrB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;SAC3B,CAAC;QACF,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,CAAC,IAAe,EAAE,SAAiB;QACrC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,IAAI,KAAK,EAAE,CAAC;oBACV,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;oBACjC,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,KAAK,CACb,uDAAuD,CACxD,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,2DAA2D;gBAC3D,8CAA8C;gBAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,IAAI,CAAC,QAAQ,CAAC,GAAG,CACf,IAAI,CAAC,IAAI,EACT,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CACnD,CAAC;oBACF,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;QACD,MAAM,IAAI,KAAK,CACb,8EAA8E,CAC/E,CAAC;IACJ,CAAC;CACF;AAED,MAAM,SAAU,SAAQ,SAAS;IAI/B,YAAY,IAAY,EAAE,CAAU;QAClC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAJ1B,YAAO,GAAG,SAAS,CAAC;IAKpB,CAAC;IAED,SAAS,CAAC,IAAY;QACpB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,IAAI,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;aAAM,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,QAAQ;QACN,MAAM,GAAG,GAAiB;YACxB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YAC1B,cAAc,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;YAChC,MAAM,EAAE,EAAE;SACX,CAAC;QACF,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC;YAChD,GAAG,CAAC,cAAc,GAAG,EAAC,IAAI,EAAE,gBAAgB,EAAC,CAAC;QAChD,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3B,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YAChD,GAAG,CAAC,MAAM,GAAG,IAAA,wBAAe,EAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpE,GAAG,CAAC,MAAM,GAAG,IAAA,wBAAe,EAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,CAAC,IAAe,EAAE,SAAiB;QACrC,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,WAAW,CAAC;gBACjD,IAAI,IAAI,EAAE,CAAC;oBACT,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;oBAChC,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,KAAK,CACb,wDAAwD,CACzD,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;gBAC1B,OAAO;YACT,CAAC;QACH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;CACF;AAED,MAAa,mBACX,SAAQ,2BAAc;IActB,YACkB,IAAY,EAC5B,OAAoC;;QAEpC,KAAK,EAAE,CAAC;QAHQ,SAAI,GAAJ,IAAI,CAAQ;QARb,YAAO,GAAG,IAAI,yBAAgB,EAAE,CAAC;QAYhD,IAAI,WAAW,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,CAAC;QACvC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,mEAAmE;YACnE,WAAW,GAAG,sCAAiB,CAAC,4BAA4B,EAAE,CAAC;QACjE,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,sCAAiB,CAAC,WAAW,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,CAAC,CAAC;QACzE,IAAI,CAAC,YAAY,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,YAAY,CAAC;QAC1C,IAAI,CAAC,YAAY,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,YAAY,mCAAI,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,WAAW;QACb,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,qCAAqC;IACrC,IAAW,eAAe;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAEM,MAAM;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,SAAS;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QAChD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAEO,eAAe,CAAC,UAAkB;QACxC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,KAAK,IAAI,EAAE,CAAC;IACrB,CAAC;IAEM,KAAK,CAAC,MAAM,CACjB,GAAW,EACX,OAAuB;;QAEvB,MAAM,QAAQ,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,mCAAI,MAAA,IAAI,CAAC,YAAY,0CAAE,QAAQ,CAAC;QAClE,IAAI,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;YACrD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC;IACxC,CAAC;IAEM,KAAK,CAAC,CAAC,YAAY,CACxB,UAAkB,EAClB,UAAyB,EAAE;QAE3B,MAAM,kBAAkB,GAAG;YACzB,GAAG,IAAI,CAAC,YAAY;YACpB,GAAG,OAAO;SACX,CAAC;QAEF,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAChD,UAAU,EACV,kBAAkB,CACnB,EAAE,CAAC;YACF,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/C,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAC/B,SAAiB,EACjB,SAAoB;;QAEpB,MAAM,SAAS,GAAG,kBAAkB,SAAS,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAmB,CAAC;QAC9C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,uCAAuC;YACvC,MAAM,iBAAiB,GAAI,GAAG,CAAC,MAAM,CAAY;iBAC9C,iBAAiB,EAAE;iBACnB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YAEnC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC/D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;gBACvE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAC,GAAG,UAAU,EAAE,IAAI,EAAC,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QACD,kEAAkE;QAClE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,WAAW,GAAG;;iDAEuB,SAAS;;;;OAInD,CAAC;YACF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAE7D,0DAA0D;YAE1D,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAE5D,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;gBAC9B,MAAM,UAAU,GAAG,MAAA,CAAC,CAAC,MAAM,CAAC,0CAAE,OAAO,GAAG,QAAQ,EAAE,CAAC;gBACnD,MAAM,SAAS,GAAG,MAAA,CAAC,CAAC,MAAM,CAAC,0CAAE,OAAO,GAAG,QAAQ,EAAE,CAAC;gBAClD,IAAI,UAAU,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS;oBAAE,SAAS;gBAClE,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;gBAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;gBACpC,IAAI,MAAM,IAAI,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAChD,wCAAwC;oBACxC,SAAS;gBACX,CAAC;gBACD,6CAA6C;gBAC7C,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACnC,CAAC;YACD,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,QAAgB,EAChB,SAAiB;QAEjB,MAAM,SAAS,GAAmB;YAChC,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,QAAQ;YACd,SAAS;YACT,UAAU,EAAE,IAAI,CAAC,IAAI;YACrB,MAAM,EAAE,EAAE;SACX,CAAC;QACF,MAAM,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACrD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,MAAoB;QAC1C,MAAM,SAAS,GAAG,EAAC,GAAG,MAAM,EAAE,MAAM,EAAE,EAAE,EAAC,CAAC;QAC1C,kDAAkD;QAClD,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,CACT,+BAA+B,aAAa,QAAQ,MAAM,CAAC,SAAS,IAAI,CACzE,CAAC;QAEF,MAAM,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QACzD,OAAO,SAAS,CAAC;IACnB,CAAC;IAEM,KAAK,CAAC,sBAAsB,CAAC,UAAkB;QACpD,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,gCAAgC,SAAS,QAAQ,UAAU,IAAI,CAAC;QAC5E,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AA1LD,kDA0LC;AAED,MAAa,UAAW,SAAQ,mBAAU;IACxC,YAAY,QAAgB;QAC1B,KAAK,CAAC,QAAQ,EAAE;YACd,MAAM,EAAE,gBAAgB;YACxB,QAAQ,EAAE,QAAQ;YAClB,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,SAAS,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC,IAAI,CAAC;QACxB,CAAC;QACD,IAAI,SAAS,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACf,OAAO,UAAU,CAAC,IAAI,CAAC;QACzB,CAAC;QACD,MAAM,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;IAChD,CAAC;IAED,SAAS;QACP,MAAM,KAAK,GAAc,EAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,EAAC,CAAC;QAChD,IAAI,IAAI,GAAc,KAAK,CAAC;QAC5B,SAAS,CAAC;YACR,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;gBACrB,IAAI,CAAC,IAAI,GAAG,EAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAC,CAAC;gBAC3C,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACnB,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACnC,IAAI,CAAC,IAAI,GAAG,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC;gBAC7B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACnB,CAAC;iBAAM,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;gBAC5B,8CAA8C;gBAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACnC,IAAI,CAAC,IAAI,GAAG,EAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAC,CAAC;gBAChC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;gBACjB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,UAAU,CAAC,cAAc,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAjDD,gCAiDC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/db-snowflake",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.220-dev241204170603",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"prepublishOnly": "npm run build"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@malloydata/malloy": "^0.0.
|
|
24
|
+
"@malloydata/malloy": "^0.0.220-dev241204170603",
|
|
25
25
|
"generic-pool": "^3.9.0",
|
|
26
26
|
"snowflake-sdk": "1.14.0",
|
|
27
27
|
"toml": "^3.0.0"
|
|
@@ -37,7 +37,11 @@ import {
|
|
|
37
37
|
SnowflakeDialect,
|
|
38
38
|
TestableConnection,
|
|
39
39
|
arrayEachFields,
|
|
40
|
-
|
|
40
|
+
TinyParser,
|
|
41
|
+
Dialect,
|
|
42
|
+
FieldDef,
|
|
43
|
+
RecordDef,
|
|
44
|
+
ArrayTypeDef,
|
|
41
45
|
} from '@malloydata/malloy';
|
|
42
46
|
import {BaseConnection} from '@malloydata/malloy/connection';
|
|
43
47
|
|
|
@@ -61,20 +65,151 @@ export interface SnowflakeConnectionOptions {
|
|
|
61
65
|
queryOptions?: RunSQLOptions;
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
isArray = false;
|
|
68
|
+
type PathChain =
|
|
69
|
+
| {arrayRef: true; next?: PathChain}
|
|
70
|
+
| {name: string; next?: PathChain};
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
class SnowField {
|
|
73
|
+
constructor(
|
|
74
|
+
readonly name: string,
|
|
75
|
+
readonly type: string,
|
|
76
|
+
readonly dialect: Dialect
|
|
77
|
+
) {}
|
|
78
|
+
fieldDef(): FieldDef {
|
|
79
|
+
return {
|
|
80
|
+
...this.dialect.sqlTypeToMalloyType(this.type),
|
|
81
|
+
name: this.name,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
walk(_path: PathChain, _fieldType: string): void {
|
|
85
|
+
throw new Error(
|
|
86
|
+
'SNOWWFLAKE SCHEMA PARSE ERROR: Should not walk through fields'
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
static make(name: string, fieldType: string, d: Dialect) {
|
|
90
|
+
if (fieldType === 'array') {
|
|
91
|
+
return new SnowArray(name, d);
|
|
92
|
+
} else if (fieldType === 'object') {
|
|
93
|
+
return new SnowObject(name, d);
|
|
94
|
+
}
|
|
95
|
+
return new SnowField(name, fieldType, d);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class SnowObject extends SnowField {
|
|
100
|
+
fieldMap = new Map<string, SnowField>();
|
|
101
|
+
constructor(name: string, d: Dialect) {
|
|
102
|
+
super(name, 'object', d);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get fields(): FieldDef[] {
|
|
106
|
+
const fields: FieldDef[] = [];
|
|
107
|
+
for (const [_, fieldObj] of this.fieldMap) {
|
|
108
|
+
fields.push(fieldObj.fieldDef());
|
|
109
|
+
}
|
|
110
|
+
return fields;
|
|
72
111
|
}
|
|
73
112
|
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
113
|
+
fieldDef() {
|
|
114
|
+
const rec: RecordDef = {
|
|
115
|
+
type: 'record',
|
|
116
|
+
name: this.name,
|
|
117
|
+
fields: this.fields,
|
|
118
|
+
join: 'one',
|
|
119
|
+
dialect: this.dialect.name,
|
|
120
|
+
};
|
|
121
|
+
return rec;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
walk(path: PathChain, fieldType: string) {
|
|
125
|
+
if ('name' in path) {
|
|
126
|
+
const field = this.fieldMap.get(path.name);
|
|
127
|
+
if (path.next) {
|
|
128
|
+
if (field) {
|
|
129
|
+
field.walk(path.next, fieldType);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw new Error(
|
|
133
|
+
'SNOWFLAKE SCHEMA PARSER ERROR: Walk through undefined'
|
|
134
|
+
);
|
|
135
|
+
} else {
|
|
136
|
+
// If we get multiple type for a field, ignore them, should
|
|
137
|
+
// which will do until we support viarant data
|
|
138
|
+
if (!field) {
|
|
139
|
+
this.fieldMap.set(
|
|
140
|
+
path.name,
|
|
141
|
+
SnowField.make(path.name, fieldType, this.dialect)
|
|
142
|
+
);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
throw new Error(
|
|
148
|
+
'SNOWFLAKE SCHEMA PARSER ERROR: Walk object reference through array reference'
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
class SnowArray extends SnowField {
|
|
154
|
+
arrayOf = 'unknown';
|
|
155
|
+
objectChild?: SnowObject;
|
|
156
|
+
arrayChild?: SnowArray;
|
|
157
|
+
constructor(name: string, d: Dialect) {
|
|
158
|
+
super(name, 'array', d);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
isArrayOf(type: string) {
|
|
162
|
+
if (this.arrayOf !== 'unknown') {
|
|
163
|
+
this.arrayOf = 'variant';
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.arrayOf = type;
|
|
167
|
+
if (type === 'object') {
|
|
168
|
+
this.objectChild = new SnowObject('', this.dialect);
|
|
169
|
+
} else if (type === 'array') {
|
|
170
|
+
this.arrayChild = new SnowArray('', this.dialect);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fieldDef(): ArrayTypeDef {
|
|
175
|
+
const arr: ArrayTypeDef = {
|
|
176
|
+
type: 'array',
|
|
177
|
+
name: this.name,
|
|
178
|
+
join: 'many',
|
|
179
|
+
dialect: this.dialect.name,
|
|
180
|
+
elementTypeDef: {type: 'string'},
|
|
181
|
+
fields: [],
|
|
182
|
+
};
|
|
183
|
+
if (this.objectChild) {
|
|
184
|
+
arr.fields = this.objectChild.fieldDef().fields;
|
|
185
|
+
arr.elementTypeDef = {type: 'record_element'};
|
|
186
|
+
} else if (this.arrayChild) {
|
|
187
|
+
arr.elementTypeDef = this.arrayChild.fieldDef();
|
|
188
|
+
arr.fields = arrayEachFields(arr.elementTypeDef);
|
|
189
|
+
} else {
|
|
190
|
+
arr.elementTypeDef = this.dialect.sqlTypeToMalloyType(this.arrayOf);
|
|
191
|
+
arr.fields = arrayEachFields(arr.elementTypeDef);
|
|
192
|
+
}
|
|
193
|
+
return arr;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
walk(path: PathChain, fieldType: string) {
|
|
197
|
+
if ('arrayRef' in path) {
|
|
198
|
+
if (path.next) {
|
|
199
|
+
const next = this.arrayChild || this.objectChild;
|
|
200
|
+
if (next) {
|
|
201
|
+
next.walk(path.next, fieldType);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
throw new Error(
|
|
205
|
+
'SNOWFLAKE SCHEMA PARSER ERROR: Array walk through leaf'
|
|
206
|
+
);
|
|
207
|
+
} else {
|
|
208
|
+
this.isArrayOf(fieldType);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
throw new Error('SNOWFLAKE SCHEMA PARSER ERROR: Array walk through name');
|
|
78
213
|
}
|
|
79
214
|
}
|
|
80
215
|
|
|
@@ -175,57 +310,6 @@ export class SnowflakeConnection
|
|
|
175
310
|
await this.executor.batch('SELECT 1 as one');
|
|
176
311
|
}
|
|
177
312
|
|
|
178
|
-
private addFieldsToStructDef(
|
|
179
|
-
structDef: StructDef,
|
|
180
|
-
structMap: StructMap
|
|
181
|
-
): void {
|
|
182
|
-
if (structMap.fieldMap.size === 0) return;
|
|
183
|
-
for (const [field, value] of structMap.fieldMap) {
|
|
184
|
-
const type = value.type;
|
|
185
|
-
const name = field;
|
|
186
|
-
|
|
187
|
-
// check for an array
|
|
188
|
-
if (value.isArray && type !== 'object') {
|
|
189
|
-
// Apparently there can only be arrays of integers, strings, or unknowns?
|
|
190
|
-
// TODO is this true or is this just all that got implemented?
|
|
191
|
-
const malloyType: LeafAtomicTypeDef =
|
|
192
|
-
type === 'integer'
|
|
193
|
-
? {type: 'number', numberType: 'integer'}
|
|
194
|
-
: type === 'varchar'
|
|
195
|
-
? {type: 'string'}
|
|
196
|
-
: {type: 'sql native', rawType: type};
|
|
197
|
-
const innerStructDef: StructDef = {
|
|
198
|
-
type: 'array',
|
|
199
|
-
name,
|
|
200
|
-
dialect: this.dialectName,
|
|
201
|
-
join: 'many',
|
|
202
|
-
elementTypeDef: malloyType,
|
|
203
|
-
fields: arrayEachFields(malloyType),
|
|
204
|
-
};
|
|
205
|
-
structDef.fields.push(innerStructDef);
|
|
206
|
-
} else if (type === 'object') {
|
|
207
|
-
const structParts = {name, dialect: this.dialectName, fields: []};
|
|
208
|
-
const innerStructDef: StructDef = value.isArray
|
|
209
|
-
? {
|
|
210
|
-
...structParts,
|
|
211
|
-
type: 'array',
|
|
212
|
-
elementTypeDef: {type: 'record_element'},
|
|
213
|
-
join: 'many',
|
|
214
|
-
}
|
|
215
|
-
: {
|
|
216
|
-
...structParts,
|
|
217
|
-
type: 'record',
|
|
218
|
-
join: 'one',
|
|
219
|
-
};
|
|
220
|
-
this.addFieldsToStructDef(innerStructDef, value);
|
|
221
|
-
structDef.fields.push(innerStructDef);
|
|
222
|
-
} else {
|
|
223
|
-
const malloyType = this.dialect.sqlTypeToMalloyType(type);
|
|
224
|
-
structDef.fields.push({...malloyType, name});
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
313
|
private async schemaFromTablePath(
|
|
230
314
|
tablePath: string,
|
|
231
315
|
structDef: StructDef
|
|
@@ -236,70 +320,48 @@ export class SnowflakeConnection
|
|
|
236
320
|
const notVariant = new Map<string, boolean>();
|
|
237
321
|
for (const row of rows) {
|
|
238
322
|
// data types look like `VARCHAR(1234)`
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const malloyType = this.dialect.sqlTypeToMalloyType(snowflakeDataType);
|
|
323
|
+
const snowflakeDataType = (row['type'] as string)
|
|
324
|
+
.toLocaleLowerCase()
|
|
325
|
+
.split('(')[0];
|
|
243
326
|
const name = row['name'] as string;
|
|
244
327
|
|
|
245
|
-
if (
|
|
328
|
+
if (['variant', 'array', 'object'].includes(snowflakeDataType)) {
|
|
246
329
|
variants.push(name);
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
notVariant.set(name, true);
|
|
251
|
-
if (malloyType) {
|
|
252
|
-
s.fields.push({...malloyType, name});
|
|
253
330
|
} else {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
name,
|
|
258
|
-
});
|
|
331
|
+
notVariant.set(name, true);
|
|
332
|
+
const malloyType = this.dialect.sqlTypeToMalloyType(snowflakeDataType);
|
|
333
|
+
structDef.fields.push({...malloyType, name});
|
|
259
334
|
}
|
|
260
335
|
}
|
|
261
|
-
//
|
|
336
|
+
// For these things, we need to sample the data to know the schema
|
|
262
337
|
if (variants.length > 0) {
|
|
263
338
|
const sampleQuery = `
|
|
264
|
-
|
|
265
|
-
|
|
339
|
+
SELECT regexp_replace(PATH, '\\[[0-9]+\\]', '[*]') as PATH, lower(TYPEOF(value)) as type
|
|
340
|
+
FROM (select object_construct(*) o from ${tablePath} limit 100)
|
|
266
341
|
,table(flatten(input => o, recursive => true)) as meta
|
|
267
|
-
|
|
268
|
-
|
|
342
|
+
GROUP BY 1,2
|
|
343
|
+
ORDER BY PATH;
|
|
269
344
|
`;
|
|
270
345
|
const fieldPathRows = await this.executor.batch(sampleQuery);
|
|
271
346
|
|
|
272
347
|
// take the schema in list form an convert it into a tree.
|
|
273
348
|
|
|
274
|
-
const
|
|
349
|
+
const rootObject = new SnowObject('__root__', this.dialect);
|
|
275
350
|
|
|
276
351
|
for (const f of fieldPathRows) {
|
|
277
352
|
const pathString = f['PATH']?.valueOf().toString();
|
|
278
353
|
const fieldType = f['TYPE']?.valueOf().toString();
|
|
279
354
|
if (pathString === undefined || fieldType === undefined) continue;
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
let index = 0;
|
|
287
|
-
for (const segment of path) {
|
|
288
|
-
let thisNode = parent.fieldMap.get(segment);
|
|
289
|
-
if (thisNode === undefined) {
|
|
290
|
-
thisNode = parent.addChild(segment, fieldType);
|
|
291
|
-
}
|
|
292
|
-
if (fieldType === 'array') {
|
|
293
|
-
thisNode.isArray = true;
|
|
294
|
-
// if this is the last
|
|
295
|
-
} else if (index === path.length - 1) {
|
|
296
|
-
thisNode.type = fieldType;
|
|
297
|
-
}
|
|
298
|
-
parent = thisNode;
|
|
299
|
-
index += 1;
|
|
355
|
+
const pathParser = new PathParser(pathString);
|
|
356
|
+
const path = pathParser.pathChain();
|
|
357
|
+
if ('name' in path && notVariant.get(path.name)) {
|
|
358
|
+
// Name will already be in the structdef
|
|
359
|
+
continue;
|
|
300
360
|
}
|
|
361
|
+
// Walk the path and mark the type at the end
|
|
362
|
+
rootObject.walk(path, fieldType);
|
|
301
363
|
}
|
|
302
|
-
|
|
364
|
+
structDef.fields.push(...rootObject.fields);
|
|
303
365
|
}
|
|
304
366
|
}
|
|
305
367
|
|
|
@@ -338,3 +400,54 @@ export class SnowflakeConnection
|
|
|
338
400
|
return tableName;
|
|
339
401
|
}
|
|
340
402
|
}
|
|
403
|
+
|
|
404
|
+
export class PathParser extends TinyParser {
|
|
405
|
+
constructor(pathName: string) {
|
|
406
|
+
super(pathName, {
|
|
407
|
+
quoted: /^'(\\'|[^'])*'/,
|
|
408
|
+
array_of: /^\[\*]/,
|
|
409
|
+
char: /^[[.\]]/,
|
|
410
|
+
number: /^\d+/,
|
|
411
|
+
word: /^\w+/,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
getName() {
|
|
416
|
+
const nameStart = this.next();
|
|
417
|
+
if (nameStart.type === 'word') {
|
|
418
|
+
return nameStart.text;
|
|
419
|
+
}
|
|
420
|
+
if (nameStart.type === '[') {
|
|
421
|
+
const quotedName = this.next('quoted');
|
|
422
|
+
this.next(']');
|
|
423
|
+
return quotedName.text;
|
|
424
|
+
}
|
|
425
|
+
throw this.parseError('Expected column name');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
pathChain(): PathChain {
|
|
429
|
+
const chain: PathChain = {name: this.getName()};
|
|
430
|
+
let node: PathChain = chain;
|
|
431
|
+
for (;;) {
|
|
432
|
+
const sep = this.next();
|
|
433
|
+
if (sep.type === 'eof') {
|
|
434
|
+
return chain;
|
|
435
|
+
}
|
|
436
|
+
if (sep.type === '.') {
|
|
437
|
+
node.next = {name: this.next('word').text};
|
|
438
|
+
node = node.next;
|
|
439
|
+
} else if (sep.type === 'array_of') {
|
|
440
|
+
node.next = {arrayRef: true};
|
|
441
|
+
node = node.next;
|
|
442
|
+
} else if (sep.type === '[') {
|
|
443
|
+
// Actually a dot access through a quoted name
|
|
444
|
+
const quoted = this.next('quoted');
|
|
445
|
+
node.next = {name: quoted.text};
|
|
446
|
+
node = node.next;
|
|
447
|
+
this.next(']');
|
|
448
|
+
} else {
|
|
449
|
+
throw this.parseError(`Unexpected ${sep.type}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|