@malloydata/malloy 0.0.240-dev250311155207 → 0.0.240-dev250311213218
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/api/asynchronous.js +4 -4
- package/dist/api/asynchronous.spec.js +8 -6
- package/dist/api/connection.d.ts +1 -1
- package/dist/api/core.d.ts +2 -0
- package/dist/api/core.js +29 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +6 -1
- package/dist/api/sessioned.js +1 -2
- package/dist/api/sessioned.spec.js +1 -1
- package/dist/api/stateless.spec.js +7 -1
- package/dist/api/util.d.ts +9 -0
- package/dist/api/util.js +215 -0
- package/dist/index.d.ts +1 -1
- package/dist/lang/malloy-parse-info.d.ts +2 -10
- package/dist/lang/malloy-to-ast.d.ts +1 -0
- package/dist/lang/malloy-to-ast.js +9 -6
- package/dist/lang/malloy-to-stable-query.d.ts +76 -0
- package/dist/lang/malloy-to-stable-query.js +660 -0
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/lang/parse-malloy.d.ts +2 -9
- package/dist/lang/parse-malloy.js +7 -112
- package/dist/lang/parse-tree-walkers/model-annotation-walker.js +1 -1
- package/dist/lang/run-malloy-parser.d.ts +3 -0
- package/dist/lang/run-malloy-parser.js +53 -0
- package/dist/lang/syntax-errors/malloy-parser-error-listener.d.ts +4 -3
- package/dist/lang/syntax-errors/malloy-parser-error-listener.js +8 -4
- package/dist/lang/test/malloy-to-stable-query.spec.d.ts +1 -0
- package/dist/lang/test/malloy-to-stable-query.spec.js +365 -0
- package/dist/lang/utils.d.ts +27 -1
- package/dist/lang/utils.js +78 -1
- package/dist/malloy.d.ts +1 -1
- package/dist/malloy.js +1 -5
- package/dist/model/malloy_query.d.ts +1 -0
- package/dist/model/malloy_query.js +6 -2
- package/dist/to_stable.d.ts +2 -1
- package/dist/to_stable.js +34 -3
- package/package.json +3 -3
package/dist/api/asynchronous.js
CHANGED
|
@@ -121,7 +121,7 @@ async function compileModel(request, fetchers) {
|
|
|
121
121
|
// eslint-disable-next-line no-constant-condition
|
|
122
122
|
while (true) {
|
|
123
123
|
const result = Core.statedCompileModel(state);
|
|
124
|
-
if (result.model) {
|
|
124
|
+
if (result.model || Core.hasErrors(result.logs)) {
|
|
125
125
|
return result;
|
|
126
126
|
}
|
|
127
127
|
const needs = await fetchNeeds(result.compiler_needs, fetchers);
|
|
@@ -134,7 +134,7 @@ async function compileSource(request, fetchers) {
|
|
|
134
134
|
// eslint-disable-next-line no-constant-condition
|
|
135
135
|
while (true) {
|
|
136
136
|
const result = Core.statedCompileSource(state, request.name);
|
|
137
|
-
if (result.source) {
|
|
137
|
+
if (result.source || Core.hasErrors(result.logs)) {
|
|
138
138
|
return result;
|
|
139
139
|
}
|
|
140
140
|
const needs = await fetchNeeds(result.compiler_needs, fetchers);
|
|
@@ -147,7 +147,7 @@ async function compileQuery(request, fetchers) {
|
|
|
147
147
|
// eslint-disable-next-line no-constant-condition
|
|
148
148
|
while (true) {
|
|
149
149
|
const result = Core.statedCompileQuery(state);
|
|
150
|
-
if (result.result) {
|
|
150
|
+
if (result.result || Core.hasErrors(result.logs)) {
|
|
151
151
|
return result;
|
|
152
152
|
}
|
|
153
153
|
const needs = await fetchNeeds(result.compiler_needs, fetchers);
|
|
@@ -177,7 +177,7 @@ async function runQuery(request, fetchers) {
|
|
|
177
177
|
}
|
|
178
178
|
try {
|
|
179
179
|
const connection = await fetchers.connections.lookupConnection(compiled.result.connection_name);
|
|
180
|
-
const data = await connection.runSQL(compiled.result.sql);
|
|
180
|
+
const data = await connection.runSQL(compiled.result.sql, compiled.result.schema);
|
|
181
181
|
return {
|
|
182
182
|
...compiled,
|
|
183
183
|
result: {
|
|
@@ -105,7 +105,7 @@ describe('api', () => {
|
|
|
105
105
|
query: {
|
|
106
106
|
definition: {
|
|
107
107
|
kind: 'arrow',
|
|
108
|
-
|
|
108
|
+
source: { kind: 'source_reference', name: 'flights' },
|
|
109
109
|
view: {
|
|
110
110
|
kind: 'segment',
|
|
111
111
|
operations: [
|
|
@@ -146,13 +146,15 @@ ORDER BY 1 asc NULLS LAST
|
|
|
146
146
|
describe('run query', () => {
|
|
147
147
|
test('run query with table dependency', async () => {
|
|
148
148
|
const data = {
|
|
149
|
-
kind: '
|
|
150
|
-
|
|
149
|
+
kind: 'array_cell',
|
|
150
|
+
array_value: [
|
|
151
151
|
{
|
|
152
|
-
|
|
152
|
+
kind: 'record_cell',
|
|
153
|
+
record_value: [{ kind: 'string_cell', string_value: 'WN' }],
|
|
153
154
|
},
|
|
154
155
|
{
|
|
155
|
-
|
|
156
|
+
kind: 'record_cell',
|
|
157
|
+
record_value: [{ kind: 'string_cell', string_value: 'AA' }],
|
|
156
158
|
},
|
|
157
159
|
],
|
|
158
160
|
};
|
|
@@ -195,7 +197,7 @@ ORDER BY 1 asc NULLS LAST
|
|
|
195
197
|
query: {
|
|
196
198
|
definition: {
|
|
197
199
|
kind: 'arrow',
|
|
198
|
-
|
|
200
|
+
source: { kind: 'source_reference', name: 'flights' },
|
|
199
201
|
view: {
|
|
200
202
|
kind: 'segment',
|
|
201
203
|
operations: [
|
package/dist/api/connection.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface InfoConnection {
|
|
|
5
5
|
get dialectName(): string;
|
|
6
6
|
}
|
|
7
7
|
export interface Connection extends InfoConnection {
|
|
8
|
-
runSQL(sql: string): Promise<Malloy.Data>;
|
|
8
|
+
runSQL(sql: string, schema: Malloy.Schema): Promise<Malloy.Data>;
|
|
9
9
|
}
|
|
10
10
|
export interface LookupConnection<T extends InfoConnection> {
|
|
11
11
|
lookupConnection(connectionName?: string): Promise<T>;
|
package/dist/api/core.d.ts
CHANGED
|
@@ -31,7 +31,9 @@ export declare function statedCompileModel(state: CompileModelState): Malloy.Com
|
|
|
31
31
|
export declare function statedCompileSource(state: CompileModelState, name: string): Malloy.CompileSourceResponse;
|
|
32
32
|
export declare function _statedCompileModel(state: CompileModelState): CompileResponse;
|
|
33
33
|
export declare const DEFAULT_LOG_RANGE: Malloy.DocumentRange;
|
|
34
|
+
export declare function mapLogs(logs: LogMessage[], defaultURL: string): Malloy.LogMessage[];
|
|
34
35
|
export declare function compileModel(request: Malloy.CompileModelRequest, state?: CompileModelState): Malloy.CompileModelResponse;
|
|
35
36
|
export declare function compileSource(request: Malloy.CompileSourceRequest): Malloy.CompileSourceResponse;
|
|
37
|
+
export declare function hasErrors(log: Malloy.LogMessage[] | undefined): boolean;
|
|
36
38
|
export declare function newCompileQueryState(request: Malloy.CompileQueryRequest): CompileModelState;
|
|
37
39
|
export declare function statedCompileQuery(state: CompileModelState): Malloy.CompileQueryResponse;
|
package/dist/api/core.js
CHANGED
|
@@ -29,12 +29,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
29
29
|
return result;
|
|
30
30
|
};
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.statedCompileQuery = exports.newCompileQueryState = exports.compileSource = exports.compileModel = exports.DEFAULT_LOG_RANGE = exports._statedCompileModel = exports.statedCompileSource = exports.statedCompileModel = exports.newCompileSourceState = exports.newCompileModelState = exports.updateCompileModelState = exports.compileQuery = void 0;
|
|
32
|
+
exports.statedCompileQuery = exports.newCompileQueryState = exports.hasErrors = exports.compileSource = exports.compileModel = exports.mapLogs = exports.DEFAULT_LOG_RANGE = exports._statedCompileModel = exports.statedCompileSource = exports.statedCompileModel = exports.newCompileSourceState = exports.newCompileModelState = exports.updateCompileModelState = exports.compileQuery = void 0;
|
|
33
33
|
const Malloy = __importStar(require("@malloydata/malloy-interfaces"));
|
|
34
34
|
const lang_1 = require("../lang");
|
|
35
35
|
const model_1 = require("../model");
|
|
36
36
|
const to_stable_1 = require("../to_stable");
|
|
37
37
|
const sql_block_1 = require("../model/sql_block");
|
|
38
|
+
const annotation_1 = require("../annotation");
|
|
39
|
+
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
38
40
|
// TODO find where this should go...
|
|
39
41
|
function tableKey(connectionName, tablePath) {
|
|
40
42
|
return `${connectionName}:${tablePath}`;
|
|
@@ -324,6 +326,7 @@ function mapLogs(logs, defaultURL) {
|
|
|
324
326
|
});
|
|
325
327
|
});
|
|
326
328
|
}
|
|
329
|
+
exports.mapLogs = mapLogs;
|
|
327
330
|
function wrapResponse(response, defaultURL) {
|
|
328
331
|
const logs = response.logs ? mapLogs(response.logs, defaultURL) : undefined;
|
|
329
332
|
if (response.compilerNeeds) {
|
|
@@ -371,6 +374,11 @@ function extractSource(result, name, defaultURL) {
|
|
|
371
374
|
return { compiler_needs: result.compilerNeeds, logs };
|
|
372
375
|
}
|
|
373
376
|
}
|
|
377
|
+
function hasErrors(log) {
|
|
378
|
+
var _a;
|
|
379
|
+
return (_a = log === null || log === void 0 ? void 0 : log.some(m => m.severity === 'error')) !== null && _a !== void 0 ? _a : false;
|
|
380
|
+
}
|
|
381
|
+
exports.hasErrors = hasErrors;
|
|
374
382
|
// Given a StableQueryDef and the URL to a model, run it and return a StableResult
|
|
375
383
|
// Given a StableQueryDef and the URL to a model, compile it and return a StableResultDef
|
|
376
384
|
// Given a StableQueryDef and the URL to a model, validate it and return a list of StableErrors
|
|
@@ -393,6 +401,7 @@ function newCompileQueryState(request) {
|
|
|
393
401
|
}
|
|
394
402
|
exports.newCompileQueryState = newCompileQueryState;
|
|
395
403
|
function statedCompileQuery(state) {
|
|
404
|
+
var _a;
|
|
396
405
|
const result = _statedCompileModel(state);
|
|
397
406
|
// TODO this can expose the internal URL... is there a better way to handle URL-less errors from the compiler?
|
|
398
407
|
const defaultURL = state.translator.sourceURL;
|
|
@@ -415,14 +424,33 @@ function statedCompileQuery(state) {
|
|
|
415
424
|
const index = queries.length - 1;
|
|
416
425
|
const query = result.modelDef.queryList[index];
|
|
417
426
|
const schema = result.model.anonymous_queries[index].schema;
|
|
427
|
+
const annotations = (_a = result.model.anonymous_queries[index].annotations) !== null && _a !== void 0 ? _a : [];
|
|
418
428
|
try {
|
|
419
429
|
const queryModel = new model_1.QueryModel(result.modelDef);
|
|
420
430
|
const translatedQuery = queryModel.compileQuery(query);
|
|
431
|
+
const modelAnnotations = (0, annotation_1.annotationToTaglines)(result.modelDef.annotation).map(l => ({
|
|
432
|
+
value: l,
|
|
433
|
+
}));
|
|
434
|
+
annotations.push({
|
|
435
|
+
value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
|
|
436
|
+
.set(['source_name'], translatedQuery.sourceExplore)
|
|
437
|
+
.toString(),
|
|
438
|
+
});
|
|
439
|
+
if (translatedQuery.queryName) {
|
|
440
|
+
annotations.push({
|
|
441
|
+
value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
|
|
442
|
+
.set(['query_name'], translatedQuery.queryName)
|
|
443
|
+
.toString(),
|
|
444
|
+
});
|
|
445
|
+
}
|
|
421
446
|
return {
|
|
422
447
|
result: {
|
|
423
448
|
sql: translatedQuery.sql,
|
|
424
449
|
schema,
|
|
425
450
|
connection_name: translatedQuery.connectionName,
|
|
451
|
+
annotations: annotations.length > 0 ? annotations : undefined,
|
|
452
|
+
model_annotations: modelAnnotations.length > 0 ? modelAnnotations : undefined,
|
|
453
|
+
query_timezone: translatedQuery.queryTimezone,
|
|
426
454
|
},
|
|
427
455
|
};
|
|
428
456
|
}
|
package/dist/api/index.d.ts
CHANGED
package/dist/api/index.js
CHANGED
|
@@ -22,8 +22,11 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
26
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.asynchronous = exports.stateless = exports.sessioned = void 0;
|
|
29
|
+
exports.util = exports.asynchronous = exports.stateless = exports.sessioned = void 0;
|
|
27
30
|
/*
|
|
28
31
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
29
32
|
*
|
|
@@ -33,4 +36,6 @@ exports.asynchronous = exports.stateless = exports.sessioned = void 0;
|
|
|
33
36
|
exports.sessioned = __importStar(require("./sessioned"));
|
|
34
37
|
exports.stateless = __importStar(require("./stateless"));
|
|
35
38
|
exports.asynchronous = __importStar(require("./asynchronous"));
|
|
39
|
+
__exportStar(require("./connection"), exports);
|
|
40
|
+
exports.util = __importStar(require("./util"));
|
|
36
41
|
//# sourceMappingURL=index.js.map
|
package/dist/api/sessioned.js
CHANGED
|
@@ -131,8 +131,7 @@ class SessionManager {
|
|
|
131
131
|
this.sessions.delete(sessionId);
|
|
132
132
|
}
|
|
133
133
|
hasErrors(log) {
|
|
134
|
-
|
|
135
|
-
return (_a = log === null || log === void 0 ? void 0 : log.some(m => m.severity === 'error')) !== null && _a !== void 0 ? _a : false;
|
|
134
|
+
return Core.hasErrors(log);
|
|
136
135
|
}
|
|
137
136
|
compileModel(request, options) {
|
|
138
137
|
const sessionInfo = {
|
|
@@ -269,9 +269,10 @@ describe('api', () => {
|
|
|
269
269
|
const result = (0, stateless_1.compileQuery)({
|
|
270
270
|
model_url: 'file://test.malloy',
|
|
271
271
|
query: {
|
|
272
|
+
annotations: [{ value: '#(test) hello' }],
|
|
272
273
|
definition: {
|
|
273
274
|
kind: 'arrow',
|
|
274
|
-
|
|
275
|
+
source: { kind: 'source_reference', name: 'flights' },
|
|
275
276
|
view: {
|
|
276
277
|
kind: 'segment',
|
|
277
278
|
operations: [
|
|
@@ -313,6 +314,11 @@ describe('api', () => {
|
|
|
313
314
|
const expected = {
|
|
314
315
|
result: {
|
|
315
316
|
connection_name: 'connection',
|
|
317
|
+
annotations: [
|
|
318
|
+
{ value: '#(test) hello\n' },
|
|
319
|
+
{ value: '#(malloy) ordered_by = [{ carrier = asc }]\n' },
|
|
320
|
+
{ value: '#(malloy) source_name = flights\n' },
|
|
321
|
+
],
|
|
316
322
|
sql: `SELECT \n\
|
|
317
323
|
base."carrier" as "carrier"
|
|
318
324
|
FROM flights as base
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InfoConnection as LegacyInfoConnection, Connection as LegacyConnection } from '../connection';
|
|
2
|
+
import { Result } from '../malloy';
|
|
3
|
+
import { QueryData } from '../model';
|
|
4
|
+
import { Connection, InfoConnection } from './connection';
|
|
5
|
+
import * as Malloy from '@malloydata/malloy-interfaces';
|
|
6
|
+
export declare function wrapLegacyInfoConnection(connection: LegacyInfoConnection): InfoConnection;
|
|
7
|
+
export declare function wrapLegacyConnection(connection: LegacyConnection): Connection;
|
|
8
|
+
export declare function mapData(data: QueryData, schema: Malloy.Schema): Malloy.Data;
|
|
9
|
+
export declare function wrapResult(result: Result): Malloy.Result;
|
package/dist/api/util.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.wrapResult = exports.mapData = exports.wrapLegacyConnection = exports.wrapLegacyInfoConnection = void 0;
|
|
10
|
+
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
11
|
+
const annotation_1 = require("../annotation");
|
|
12
|
+
const to_stable_1 = require("../to_stable");
|
|
13
|
+
const luxon_1 = require("luxon");
|
|
14
|
+
function wrapLegacyInfoConnection(connection) {
|
|
15
|
+
return {
|
|
16
|
+
get dialectName() {
|
|
17
|
+
return connection.dialectName;
|
|
18
|
+
},
|
|
19
|
+
async fetchSchemaForSQLQuery(sql) {
|
|
20
|
+
const result = await connection.fetchSchemaForSQLStruct({ connection: connection.name, selectStr: sql }, {});
|
|
21
|
+
const table = result.structDef;
|
|
22
|
+
if (table === undefined) {
|
|
23
|
+
throw new Error(result.error);
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
fields: (0, to_stable_1.convertFieldInfos)(table, table.fields),
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
async fetchSchemaForTable(tableName) {
|
|
30
|
+
const key = `${connection.name}:${tableName}`;
|
|
31
|
+
const result = await connection.fetchSchemaForTables({ [key]: tableName }, {});
|
|
32
|
+
const table = result.schemas[key];
|
|
33
|
+
if (table === undefined) {
|
|
34
|
+
throw new Error(result.errors[key]);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
fields: (0, to_stable_1.convertFieldInfos)(table, table.fields),
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
exports.wrapLegacyInfoConnection = wrapLegacyInfoConnection;
|
|
43
|
+
function wrapLegacyConnection(connection) {
|
|
44
|
+
return {
|
|
45
|
+
...wrapLegacyInfoConnection(connection),
|
|
46
|
+
runSQL: async (sql, schema) => {
|
|
47
|
+
const result = await connection.runSQL(sql);
|
|
48
|
+
return mapData(result.rows, schema);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
exports.wrapLegacyConnection = wrapLegacyConnection;
|
|
53
|
+
function valueToDate(value) {
|
|
54
|
+
// TODO properly map the data from BQ/Postgres types
|
|
55
|
+
if (value instanceof Date) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
const valAsAny = value;
|
|
60
|
+
if (valAsAny.constructor.name === 'Date') {
|
|
61
|
+
// For some reason duckdb TSTZ values come back as objects which do not
|
|
62
|
+
// pass "instance of" but do seem date like.
|
|
63
|
+
return new Date(value);
|
|
64
|
+
}
|
|
65
|
+
else if (typeof value === 'number') {
|
|
66
|
+
return new Date(value);
|
|
67
|
+
}
|
|
68
|
+
else if (typeof value !== 'string') {
|
|
69
|
+
return new Date(value.value);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Postgres timestamps end up here, and ideally we would know the system
|
|
73
|
+
// timezone of the postgres instance to correctly create a Date() object
|
|
74
|
+
// which represents the same instant in time, but we don't have the data
|
|
75
|
+
// flow to implement that. This may be a problem at some future day,
|
|
76
|
+
// so here is a comment, for that day.
|
|
77
|
+
let parsed = luxon_1.DateTime.fromISO(value, { zone: 'UTC' });
|
|
78
|
+
if (!parsed.isValid) {
|
|
79
|
+
parsed = luxon_1.DateTime.fromSQL(value, { zone: 'UTC' });
|
|
80
|
+
}
|
|
81
|
+
return parsed.toJSDate();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function mapData(data, schema) {
|
|
85
|
+
function mapValue(value, field) {
|
|
86
|
+
if (value === null) {
|
|
87
|
+
return { kind: 'null_cell' };
|
|
88
|
+
}
|
|
89
|
+
else if (field.type.kind === 'date_type' ||
|
|
90
|
+
field.type.kind === 'timestamp_type') {
|
|
91
|
+
const time_value = valueToDate(value).toISOString();
|
|
92
|
+
if (field.type.kind === 'date_type') {
|
|
93
|
+
return { kind: 'date_cell', date_value: time_value };
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return { kind: 'timestamp_cell', timestamp_value: time_value };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (field.type.kind === 'boolean_type') {
|
|
100
|
+
if (typeof value === 'number') {
|
|
101
|
+
return { kind: 'boolean_cell', boolean_value: value !== 0 };
|
|
102
|
+
}
|
|
103
|
+
if (typeof value !== 'boolean') {
|
|
104
|
+
throw new Error(`Invalid boolean ${value}`);
|
|
105
|
+
}
|
|
106
|
+
return { kind: 'boolean_cell', boolean_value: value };
|
|
107
|
+
}
|
|
108
|
+
else if (field.type.kind === 'number_type') {
|
|
109
|
+
if (typeof value !== 'number') {
|
|
110
|
+
throw new Error(`Invalid number ${value}`);
|
|
111
|
+
}
|
|
112
|
+
return { kind: 'number_cell', number_value: value };
|
|
113
|
+
}
|
|
114
|
+
else if (field.type.kind === 'string_type') {
|
|
115
|
+
if (typeof value !== 'string') {
|
|
116
|
+
throw new Error(`Invalid string ${value}`);
|
|
117
|
+
}
|
|
118
|
+
return { kind: 'string_cell', string_value: value };
|
|
119
|
+
}
|
|
120
|
+
else if (field.type.kind === 'array_type') {
|
|
121
|
+
if (!Array.isArray(value)) {
|
|
122
|
+
throw new Error(`Invalid array ${value}`);
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
kind: 'array_cell',
|
|
126
|
+
array_value: value.map(value => mapValue(value, {
|
|
127
|
+
name: 'array_element',
|
|
128
|
+
type: field.type.element_type,
|
|
129
|
+
})),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
else if (field.type.kind === 'json_type') {
|
|
133
|
+
return { kind: 'json_cell', json_value: JSON.stringify(value) };
|
|
134
|
+
}
|
|
135
|
+
else if (field.type.kind === 'sql_native_type') {
|
|
136
|
+
return { kind: 'sql_native_cell', sql_native_value: JSON.stringify(value) };
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const type = field.type;
|
|
140
|
+
if (type.kind !== 'record_type') {
|
|
141
|
+
throw new Error(`Invalid record in result ${JSON.stringify(field)}, ${JSON.stringify(value)}`);
|
|
142
|
+
}
|
|
143
|
+
return mapRow(value, {
|
|
144
|
+
kind: 'join',
|
|
145
|
+
relationship: 'many',
|
|
146
|
+
name: 'array_element',
|
|
147
|
+
schema: {
|
|
148
|
+
fields: type.fields.map(f => ({ kind: 'dimension', ...f })),
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function mapRow(row, field) {
|
|
154
|
+
const cells = [];
|
|
155
|
+
for (const f of field.schema.fields) {
|
|
156
|
+
const value = row[f.name];
|
|
157
|
+
if (f.kind !== 'dimension') {
|
|
158
|
+
throw new Error('Invalid result -- expected all fields to be dimensions');
|
|
159
|
+
}
|
|
160
|
+
const cell = mapValue(value, f);
|
|
161
|
+
cells.push(cell);
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
kind: 'record_cell',
|
|
165
|
+
record_value: cells,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const rootField = {
|
|
169
|
+
kind: 'join',
|
|
170
|
+
schema,
|
|
171
|
+
name: 'root',
|
|
172
|
+
relationship: 'one',
|
|
173
|
+
};
|
|
174
|
+
return {
|
|
175
|
+
kind: 'array_cell',
|
|
176
|
+
array_value: data.map(row => mapRow(row, rootField)),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
exports.mapData = mapData;
|
|
180
|
+
function wrapResult(result) {
|
|
181
|
+
const structs = result._queryResult.structs;
|
|
182
|
+
const struct = structs[structs.length - 1];
|
|
183
|
+
const schema = { fields: (0, to_stable_1.convertFieldInfos)(struct, struct.fields) };
|
|
184
|
+
const annotations = (0, annotation_1.annotationToTaglines)(result.annotation).map(l => ({
|
|
185
|
+
value: l,
|
|
186
|
+
}));
|
|
187
|
+
const metadataAnnot = struct.resultMetadata
|
|
188
|
+
? (0, to_stable_1.getResultStructMetadataAnnotation)(struct, struct.resultMetadata)
|
|
189
|
+
: undefined;
|
|
190
|
+
if (metadataAnnot) {
|
|
191
|
+
annotations.push(metadataAnnot);
|
|
192
|
+
}
|
|
193
|
+
annotations.push(...(struct.resultMetadata ? [] : []));
|
|
194
|
+
if (result.sourceExplore) {
|
|
195
|
+
annotations.push({
|
|
196
|
+
value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
|
|
197
|
+
.set(['source_name'], result.sourceExplore.name)
|
|
198
|
+
.toString(),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
annotations.push({
|
|
202
|
+
value: malloy_tag_1.Tag.withPrefix('#(malloy) ')
|
|
203
|
+
.set(['query_name'], result.resultExplore.name)
|
|
204
|
+
.toString(),
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
schema,
|
|
208
|
+
data: mapData(result.data.toObject(), schema),
|
|
209
|
+
connection_name: result.connectionName,
|
|
210
|
+
annotations: annotations.length > 0 ? annotations : undefined,
|
|
211
|
+
query_timezone: result.data.field.queryTimezone,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
exports.wrapResult = wrapResult;
|
|
215
|
+
//# sourceMappingURL=util.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { isSourceDef, Segment, isLeafAtomic, isJoined, isJoinedSource, isSamplin
|
|
|
5
5
|
export { MalloyTranslator, } from './lang';
|
|
6
6
|
export type { LogMessage, TranslateResponse } from './lang';
|
|
7
7
|
export { Model, Malloy, Runtime, AtomicFieldType, ConnectionRuntime, SingleConnectionRuntime, EmptyURLReader, InMemoryURLReader, FixedConnectionMap, MalloyError, JoinRelationship, SourceRelationship, DateTimeframe, TimestampTimeframe, PreparedResult, Result, QueryMaterializer, CSVWriter, JSONWriter, Parse, DataWriter, Explore, InMemoryModelCache, CacheManager, } from './malloy';
|
|
8
|
-
export type { PreparedQuery, Field, AtomicField, ExploreField, QueryField, SortableField, DataArray, DataRecord, DataColumn, DataArrayOrRecord, Loggable, ModelMaterializer, DocumentTablePath, DocumentSymbol, ResultJSON, PreparedResultMaterializer, ExploreMaterializer, WriteStream, SerializedExplore, ModelCache, CachedModel, DateField, TimestampField, } from './malloy';
|
|
8
|
+
export type { PreparedQuery, Field, AtomicField, ExploreField, QueryField, SortableField, DataArray, DataRecord, DataColumn, DataArrayOrRecord, Loggable, ModelMaterializer, DocumentTablePath, DocumentSymbol, ResultJSON, PreparedResultJSON, PreparedResultMaterializer, ExploreMaterializer, WriteStream, SerializedExplore, ModelCache, CachedModel, DateField, TimestampField, } from './malloy';
|
|
9
9
|
export type { QueryOptionsReader, RunSQLOptions } from './run_sql_options';
|
|
10
10
|
export type { EventStream, ModelString, ModelURL, QueryString, QueryURL, URLReader, InvalidationKey, } from './runtime_types';
|
|
11
11
|
export type { Connection, ConnectionConfig, ConnectionFactory, ConnectionParameter, ConnectionParameterValue, ConnectionConfigSchema, FetchSchemaOptions, InfoConnection, LookupConnection, PersistSQLResults, PooledConnection, TestableConnection, StreamingConnection, } from './connection/types';
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { DocumentRange } from '../model/malloy_types';
|
|
4
|
-
export interface MalloyParseInfo {
|
|
5
|
-
root: ParseTree;
|
|
6
|
-
tokenStream: CommonTokenStream;
|
|
7
|
-
sourceStream: CodePointCharStream;
|
|
8
|
-
sourceURL: string;
|
|
1
|
+
import { ParseInfo } from './utils';
|
|
2
|
+
export interface MalloyParseInfo extends ParseInfo {
|
|
9
3
|
importBaseURL: string;
|
|
10
|
-
rangeFromContext: (pcx: ParserRuleContext) => DocumentRange;
|
|
11
|
-
malloyVersion: string;
|
|
12
4
|
}
|
|
@@ -44,6 +44,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
|
|
|
44
44
|
* Log an error message relative to an AST node
|
|
45
45
|
*/
|
|
46
46
|
protected astError<T extends MessageCode>(el: ast.MalloyElement, code: T, data: MessageParameterType<T>, options?: LogMessageOptions): void;
|
|
47
|
+
protected rangeFromContext(cx: ParserRuleContext): DocumentRange;
|
|
47
48
|
protected getLocation(cx: ParserRuleContext): DocumentLocation;
|
|
48
49
|
protected getSourceString(cx: ParserRuleContext): string;
|
|
49
50
|
/**
|
|
@@ -105,10 +105,13 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
105
105
|
astError(el, code, data, options) {
|
|
106
106
|
this.msgLog.log((0, parse_log_1.makeLogMessage)(code, data, { at: el.location, ...options }));
|
|
107
107
|
}
|
|
108
|
+
rangeFromContext(cx) {
|
|
109
|
+
return (0, utils_1.rangeFromContext)(this.parseInfo.sourceInfo, cx);
|
|
110
|
+
}
|
|
108
111
|
getLocation(cx) {
|
|
109
112
|
return {
|
|
110
113
|
url: this.parseInfo.sourceURL,
|
|
111
|
-
range: this.
|
|
114
|
+
range: this.rangeFromContext(cx),
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
getSourceString(cx) {
|
|
@@ -180,7 +183,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
180
183
|
astAt(el, cx) {
|
|
181
184
|
el.location = {
|
|
182
185
|
url: this.parseInfo.sourceURL,
|
|
183
|
-
range: this.
|
|
186
|
+
range: this.rangeFromContext(cx),
|
|
184
187
|
};
|
|
185
188
|
return el;
|
|
186
189
|
}
|
|
@@ -957,7 +960,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
957
960
|
const left = this.getFieldExpr(pcx.fieldExpr(0));
|
|
958
961
|
const right = this.getFieldExpr(pcx.fieldExpr(1));
|
|
959
962
|
if (ast.isEquality(op)) {
|
|
960
|
-
const wholeRange = this.
|
|
963
|
+
const wholeRange = this.rangeFromContext(pcx);
|
|
961
964
|
if (right instanceof ast.ExprNULL) {
|
|
962
965
|
if (op === '=') {
|
|
963
966
|
this.warnWithReplacement('sql-is-null', "Use 'is null' to check for NULL instead of '= null'", wholeRange, `${this.getSourceCode(pcx.fieldExpr(0))} is null`);
|
|
@@ -1170,7 +1173,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1170
1173
|
});
|
|
1171
1174
|
const elseCx = pcx._caseElse;
|
|
1172
1175
|
const theElse = elseCx ? this.getFieldExpr(elseCx) : undefined;
|
|
1173
|
-
this.warnWithReplacement('sql-case', 'Use a `pick` statement instead of `case`', this.
|
|
1176
|
+
this.warnWithReplacement('sql-case', 'Use a `pick` statement instead of `case`', this.rangeFromContext(pcx), `${[
|
|
1174
1177
|
...(valueCx ? [`${this.getSourceCode(valueCx)} ?`] : []),
|
|
1175
1178
|
...whenCxs.map(whenCx => `pick ${this.getSourceCode(whenCx._result)} when ${this.getSourceCode(whenCx._condition)}`),
|
|
1176
1179
|
elseCx ? `else ${elseCx.text}` : 'else null',
|
|
@@ -1486,7 +1489,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1486
1489
|
let op = '~';
|
|
1487
1490
|
const left = pcx.fieldExpr(0);
|
|
1488
1491
|
const right = pcx.fieldExpr(1);
|
|
1489
|
-
const wholeRange = this.
|
|
1492
|
+
const wholeRange = this.rangeFromContext(pcx);
|
|
1490
1493
|
if (pcx.NOT()) {
|
|
1491
1494
|
op = '!~';
|
|
1492
1495
|
this.warnWithReplacement('sql-not-like', "Use Malloy operator '!~' instead of 'NOT LIKE'", wholeRange, `${this.getSourceCode(left)} !~ ${this.getSourceCode(right)}`);
|
|
@@ -1505,7 +1508,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1505
1508
|
const isNot = !!pcx.NOT();
|
|
1506
1509
|
const from = pcx.fieldExprList().fieldExpr();
|
|
1507
1510
|
const inStmt = this.astAt(new ast.ExprLegacyIn(expr, isNot, from.map(f => this.getFieldExpr(f))), pcx);
|
|
1508
|
-
this.warnWithReplacement('sql-in', `Use = (a|b|c) instead of${isNot ? ' NOT' : ''} IN (a,b,c)`, this.
|
|
1511
|
+
this.warnWithReplacement('sql-in', `Use = (a|b|c) instead of${isNot ? ' NOT' : ''} IN (a,b,c)`, this.rangeFromContext(pcx), `${this.getSourceCode(pcx.fieldExpr())} ${isNot ? '!=' : '='} (${from
|
|
1509
1512
|
.map(f => this.getSourceCode(f))
|
|
1510
1513
|
.join(' | ')})`);
|
|
1511
1514
|
return inStmt;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
+
import { ParseTree, TerminalNode } from 'antlr4ts/tree';
|
|
3
|
+
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
|
|
4
|
+
import { MalloyParserVisitor } from './lib/Malloy/MalloyParserVisitor';
|
|
5
|
+
import * as Malloy from '@malloydata/malloy-interfaces';
|
|
6
|
+
import * as parse from './lib/Malloy/MalloyParser';
|
|
7
|
+
import { LogMessageOptions, MessageCode, MessageLogger, MessageParameterType } from './parse-log';
|
|
8
|
+
import { DocumentLocation } from '../model/malloy_types';
|
|
9
|
+
import { ParseInfo } from './utils';
|
|
10
|
+
type HasAnnotations = ParserRuleContext & {
|
|
11
|
+
ANNOTATION: () => TerminalNode[];
|
|
12
|
+
};
|
|
13
|
+
type Node = Malloy.Query | Malloy.QueryDefinitionWithArrow | Malloy.QueryDefinitionWithQueryReference | Malloy.QueryDefinitionWithRefinement | null;
|
|
14
|
+
/**
|
|
15
|
+
* ANTLR visitor pattern parse tree traversal. Generates a Malloy
|
|
16
|
+
* AST from an ANTLR parse tree.
|
|
17
|
+
*/
|
|
18
|
+
export declare class MalloyToQuery extends AbstractParseTreeVisitor<Node> implements MalloyParserVisitor<Node> {
|
|
19
|
+
readonly parseInfo: ParseInfo;
|
|
20
|
+
readonly msgLog: MessageLogger;
|
|
21
|
+
constructor(parseInfo: ParseInfo, msgLog: MessageLogger);
|
|
22
|
+
/**
|
|
23
|
+
* Mostly used to flag a case where the grammar and the type system are
|
|
24
|
+
* no longer in sync. A visitor was written based on a grammar which
|
|
25
|
+
* apparently has changed and now an unexpected element type has appeared.
|
|
26
|
+
* This is a non recoverable error, since the parser and the grammar
|
|
27
|
+
* are not compatible.
|
|
28
|
+
* @return an error object to throw.
|
|
29
|
+
*/
|
|
30
|
+
protected internalError(cx: ParserRuleContext, message: string): Error;
|
|
31
|
+
protected getLocation(cx: ParserRuleContext): DocumentLocation;
|
|
32
|
+
/**
|
|
33
|
+
* Log an error message relative to a parse node
|
|
34
|
+
*/
|
|
35
|
+
protected contextError<T extends MessageCode>(cx: ParserRuleContext, code: T, data: MessageParameterType<T>, options?: LogMessageOptions): void;
|
|
36
|
+
protected getNumber(term: ParseTree): number;
|
|
37
|
+
protected defaultResult(): Node;
|
|
38
|
+
/**
|
|
39
|
+
* Get all the possibly missing annotations from this parse rule
|
|
40
|
+
* @param cx Any parse context which has an ANNOTATION* rules
|
|
41
|
+
* @returns Array of texts for the annotations
|
|
42
|
+
*/
|
|
43
|
+
protected getAnnotations(cx: HasAnnotations): Malloy.Annotation[] | undefined;
|
|
44
|
+
protected getIsAnnotations(cx?: parse.IsDefineContext): Malloy.Annotation[] | undefined;
|
|
45
|
+
protected notAllowed(pcx: ParserRuleContext, what: string): void;
|
|
46
|
+
protected illegal(pcx: ParserRuleContext, what: string): void;
|
|
47
|
+
visitMalloyDocument(pcx: parse.MalloyDocumentContext): Malloy.Query | null;
|
|
48
|
+
visitRunStatement(pcx: parse.RunStatementContext): Malloy.Query | null;
|
|
49
|
+
protected getQueryReference(cx: parse.SQIDContext): Malloy.Reference | null;
|
|
50
|
+
protected getQueryDefinition(cx: parse.SqExprContext): Malloy.QueryDefinition | null;
|
|
51
|
+
protected getRefinementSegment(cx: parse.SegExprContext): Malloy.ViewDefinition | null;
|
|
52
|
+
protected getGroupByStatement(gbcx: parse.GroupByStatementContext): Malloy.ViewOperationWithGroupBy[] | null;
|
|
53
|
+
protected getAggregateStatement(agcx: parse.AggregateStatementContext): Malloy.ViewOperationWithAggregate[] | null;
|
|
54
|
+
protected getOrderByStatement(obcx: parse.OrderByStatementContext): Malloy.ViewOperationWithOrderBy[] | null;
|
|
55
|
+
protected getNestStatement(nstcx: parse.NestStatementContext): Malloy.ViewOperationWithNest[] | null;
|
|
56
|
+
protected getViewExpression(cx: parse.VExprContext): Malloy.ViewDefinition | null;
|
|
57
|
+
protected getLimitStatement(cx: parse.LimitStatementContext): Malloy.ViewOperationWithLimit | null;
|
|
58
|
+
protected getSegmentOperation(cx: parse.QueryStatementContext): Malloy.ViewOperation[] | null;
|
|
59
|
+
protected getFieldPath(pcx: parse.FieldPathContext): {
|
|
60
|
+
name: string;
|
|
61
|
+
path?: string[];
|
|
62
|
+
};
|
|
63
|
+
protected getTimeframe(cx: parse.TimeframeContext): Malloy.TimestampTimeframe | null;
|
|
64
|
+
protected getQueryField(cx: parse.QueryFieldEntryContext): {
|
|
65
|
+
name?: string;
|
|
66
|
+
field: Malloy.Field;
|
|
67
|
+
} | null;
|
|
68
|
+
getFieldExpression(cx: parse.FieldExprContext): Malloy.Expression | null;
|
|
69
|
+
getWhere(_whereCx: parse.WhereStatementContext): Malloy.Where[] | null;
|
|
70
|
+
protected combineAnnotations(...a: (Malloy.Annotation[] | undefined)[]): Malloy.Annotation[] | undefined;
|
|
71
|
+
}
|
|
72
|
+
export declare function malloyToQuery(code: string): {
|
|
73
|
+
query?: Malloy.Query;
|
|
74
|
+
logs: Malloy.LogMessage[];
|
|
75
|
+
};
|
|
76
|
+
export {};
|