@malloydata/malloy 0.0.48 → 0.0.49-dev230626192330

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.
@@ -19,6 +19,8 @@ import { ExprNULLContext } from "./MalloyParser";
19
19
  import { ExprBoolContext } from "./MalloyParser";
20
20
  import { ExprRegexContext } from "./MalloyParser";
21
21
  import { ExprNowContext } from "./MalloyParser";
22
+ import { TableFunctionContext } from "./MalloyParser";
23
+ import { TableMethodContext } from "./MalloyParser";
22
24
  import { NestExistingContext } from "./MalloyParser";
23
25
  import { NestDefContext } from "./MalloyParser";
24
26
  import { ExprFieldPathContext } from "./MalloyParser";
@@ -97,6 +99,7 @@ import { PipelineFromNameContext } from "./MalloyParser";
97
99
  import { FirstSegmentContext } from "./MalloyParser";
98
100
  import { PipeElementContext } from "./MalloyParser";
99
101
  import { ExploreTableContext } from "./MalloyParser";
102
+ import { ConnectionIdContext } from "./MalloyParser";
100
103
  import { QueryPropertiesContext } from "./MalloyParser";
101
104
  import { FilterShortcutContext } from "./MalloyParser";
102
105
  import { ExploreQueryNameContext } from "./MalloyParser";
@@ -164,7 +167,8 @@ import { MalloyTypeContext } from "./MalloyParser";
164
167
  import { CompareOpContext } from "./MalloyParser";
165
168
  import { LiteralContext } from "./MalloyParser";
166
169
  import { DateLiteralContext } from "./MalloyParser";
167
- import { TableNameContext } from "./MalloyParser";
170
+ import { TablePathContext } from "./MalloyParser";
171
+ import { TableURIContext } from "./MalloyParser";
168
172
  import { IdContext } from "./MalloyParser";
169
173
  import { TimeframeContext } from "./MalloyParser";
170
174
  import { UngroupContext } from "./MalloyParser";
@@ -435,6 +439,30 @@ export interface MalloyParserListener extends ParseTreeListener {
435
439
  * @param ctx the parse tree
436
440
  */
437
441
  exitExprNow?: (ctx: ExprNowContext) => void;
442
+ /**
443
+ * Enter a parse tree produced by the `tableFunction`
444
+ * labeled alternative in `MalloyParser.exploreTable`.
445
+ * @param ctx the parse tree
446
+ */
447
+ enterTableFunction?: (ctx: TableFunctionContext) => void;
448
+ /**
449
+ * Exit a parse tree produced by the `tableFunction`
450
+ * labeled alternative in `MalloyParser.exploreTable`.
451
+ * @param ctx the parse tree
452
+ */
453
+ exitTableFunction?: (ctx: TableFunctionContext) => void;
454
+ /**
455
+ * Enter a parse tree produced by the `tableMethod`
456
+ * labeled alternative in `MalloyParser.exploreTable`.
457
+ * @param ctx the parse tree
458
+ */
459
+ enterTableMethod?: (ctx: TableMethodContext) => void;
460
+ /**
461
+ * Exit a parse tree produced by the `tableMethod`
462
+ * labeled alternative in `MalloyParser.exploreTable`.
463
+ * @param ctx the parse tree
464
+ */
465
+ exitTableMethod?: (ctx: TableMethodContext) => void;
438
466
  /**
439
467
  * Enter a parse tree produced by the `nestExisting`
440
468
  * labeled alternative in `MalloyParser.nestEntry`.
@@ -1319,6 +1347,16 @@ export interface MalloyParserListener extends ParseTreeListener {
1319
1347
  * @param ctx the parse tree
1320
1348
  */
1321
1349
  exitExploreTable?: (ctx: ExploreTableContext) => void;
1350
+ /**
1351
+ * Enter a parse tree produced by `MalloyParser.connectionId`.
1352
+ * @param ctx the parse tree
1353
+ */
1354
+ enterConnectionId?: (ctx: ConnectionIdContext) => void;
1355
+ /**
1356
+ * Exit a parse tree produced by `MalloyParser.connectionId`.
1357
+ * @param ctx the parse tree
1358
+ */
1359
+ exitConnectionId?: (ctx: ConnectionIdContext) => void;
1322
1360
  /**
1323
1361
  * Enter a parse tree produced by `MalloyParser.queryProperties`.
1324
1362
  * @param ctx the parse tree
@@ -1990,15 +2028,25 @@ export interface MalloyParserListener extends ParseTreeListener {
1990
2028
  */
1991
2029
  exitDateLiteral?: (ctx: DateLiteralContext) => void;
1992
2030
  /**
1993
- * Enter a parse tree produced by `MalloyParser.tableName`.
2031
+ * Enter a parse tree produced by `MalloyParser.tablePath`.
2032
+ * @param ctx the parse tree
2033
+ */
2034
+ enterTablePath?: (ctx: TablePathContext) => void;
2035
+ /**
2036
+ * Exit a parse tree produced by `MalloyParser.tablePath`.
2037
+ * @param ctx the parse tree
2038
+ */
2039
+ exitTablePath?: (ctx: TablePathContext) => void;
2040
+ /**
2041
+ * Enter a parse tree produced by `MalloyParser.tableURI`.
1994
2042
  * @param ctx the parse tree
1995
2043
  */
1996
- enterTableName?: (ctx: TableNameContext) => void;
2044
+ enterTableURI?: (ctx: TableURIContext) => void;
1997
2045
  /**
1998
- * Exit a parse tree produced by `MalloyParser.tableName`.
2046
+ * Exit a parse tree produced by `MalloyParser.tableURI`.
1999
2047
  * @param ctx the parse tree
2000
2048
  */
2001
- exitTableName?: (ctx: TableNameContext) => void;
2049
+ exitTableURI?: (ctx: TableURIContext) => void;
2002
2050
  /**
2003
2051
  * Enter a parse tree produced by `MalloyParser.id`.
2004
2052
  * @param ctx the parse tree
@@ -19,6 +19,8 @@ import { ExprNULLContext } from "./MalloyParser";
19
19
  import { ExprBoolContext } from "./MalloyParser";
20
20
  import { ExprRegexContext } from "./MalloyParser";
21
21
  import { ExprNowContext } from "./MalloyParser";
22
+ import { TableFunctionContext } from "./MalloyParser";
23
+ import { TableMethodContext } from "./MalloyParser";
22
24
  import { NestExistingContext } from "./MalloyParser";
23
25
  import { NestDefContext } from "./MalloyParser";
24
26
  import { ExprFieldPathContext } from "./MalloyParser";
@@ -97,6 +99,7 @@ import { PipelineFromNameContext } from "./MalloyParser";
97
99
  import { FirstSegmentContext } from "./MalloyParser";
98
100
  import { PipeElementContext } from "./MalloyParser";
99
101
  import { ExploreTableContext } from "./MalloyParser";
102
+ import { ConnectionIdContext } from "./MalloyParser";
100
103
  import { QueryPropertiesContext } from "./MalloyParser";
101
104
  import { FilterShortcutContext } from "./MalloyParser";
102
105
  import { ExploreQueryNameContext } from "./MalloyParser";
@@ -164,7 +167,8 @@ import { MalloyTypeContext } from "./MalloyParser";
164
167
  import { CompareOpContext } from "./MalloyParser";
165
168
  import { LiteralContext } from "./MalloyParser";
166
169
  import { DateLiteralContext } from "./MalloyParser";
167
- import { TableNameContext } from "./MalloyParser";
170
+ import { TablePathContext } from "./MalloyParser";
171
+ import { TableURIContext } from "./MalloyParser";
168
172
  import { IdContext } from "./MalloyParser";
169
173
  import { TimeframeContext } from "./MalloyParser";
170
174
  import { UngroupContext } from "./MalloyParser";
@@ -338,6 +342,20 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
338
342
  * @return the visitor result
339
343
  */
340
344
  visitExprNow?: (ctx: ExprNowContext) => Result;
345
+ /**
346
+ * Visit a parse tree produced by the `tableFunction`
347
+ * labeled alternative in `MalloyParser.exploreTable`.
348
+ * @param ctx the parse tree
349
+ * @return the visitor result
350
+ */
351
+ visitTableFunction?: (ctx: TableFunctionContext) => Result;
352
+ /**
353
+ * Visit a parse tree produced by the `tableMethod`
354
+ * labeled alternative in `MalloyParser.exploreTable`.
355
+ * @param ctx the parse tree
356
+ * @return the visitor result
357
+ */
358
+ visitTableMethod?: (ctx: TableMethodContext) => Result;
341
359
  /**
342
360
  * Visit a parse tree produced by the `nestExisting`
343
361
  * labeled alternative in `MalloyParser.nestEntry`.
@@ -858,6 +876,12 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
858
876
  * @return the visitor result
859
877
  */
860
878
  visitExploreTable?: (ctx: ExploreTableContext) => Result;
879
+ /**
880
+ * Visit a parse tree produced by `MalloyParser.connectionId`.
881
+ * @param ctx the parse tree
882
+ * @return the visitor result
883
+ */
884
+ visitConnectionId?: (ctx: ConnectionIdContext) => Result;
861
885
  /**
862
886
  * Visit a parse tree produced by `MalloyParser.queryProperties`.
863
887
  * @param ctx the parse tree
@@ -1261,11 +1285,17 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
1261
1285
  */
1262
1286
  visitDateLiteral?: (ctx: DateLiteralContext) => Result;
1263
1287
  /**
1264
- * Visit a parse tree produced by `MalloyParser.tableName`.
1288
+ * Visit a parse tree produced by `MalloyParser.tablePath`.
1289
+ * @param ctx the parse tree
1290
+ * @return the visitor result
1291
+ */
1292
+ visitTablePath?: (ctx: TablePathContext) => Result;
1293
+ /**
1294
+ * Visit a parse tree produced by `MalloyParser.tableURI`.
1265
1295
  * @param ctx the parse tree
1266
1296
  * @return the visitor result
1267
1297
  */
1268
- visitTableName?: (ctx: TableNameContext) => Result;
1298
+ visitTableURI?: (ctx: TableURIContext) => Result;
1269
1299
  /**
1270
1300
  * Visit a parse tree produced by `MalloyParser.id`.
1271
1301
  * @param ctx the parse tree
@@ -65,7 +65,8 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
65
65
  visitSourceDefinition(pcx: parse.SourceDefinitionContext): ast.DefineSource;
66
66
  visitExplore(pcx: parse.ExploreContext): ast.Source;
67
67
  visitExploreProperties(pcx: parse.ExplorePropertiesContext): ast.SourceDesc;
68
- visitExploreTable(pcx: parse.ExploreTableContext): ast.TableSource;
68
+ visitTableFunction(pcx: parse.TableFunctionContext): ast.TableSource;
69
+ visitTableMethod(pcx: parse.TableMethodContext): ast.TableSource;
69
70
  visitTableSource(pcx: parse.TableSourceContext): ast.TableSource;
70
71
  visitSQLSourceName(pcx: parse.SQLSourceNameContext): ast.SQLSource;
71
72
  visitQuerySource(pcx: parse.QuerySourceContext): ast.Source;
@@ -49,6 +49,7 @@ exports.MalloyToAST = void 0;
49
49
  const AbstractParseTreeVisitor_1 = require("antlr4ts/tree/AbstractParseTreeVisitor");
50
50
  const ast = __importStar(require("./ast"));
51
51
  const Interval_1 = require("antlr4ts/misc/Interval");
52
+ const ast_1 = require("./ast");
52
53
  const ENABLE_M4_WARNINGS = false;
53
54
  class IgnoredElement extends ast.MalloyElement {
54
55
  constructor(src) {
@@ -262,12 +263,26 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
262
263
  }
263
264
  return propList;
264
265
  }
265
- visitExploreTable(pcx) {
266
- const tableName = this.stripQuotes(pcx.tableName().text);
267
- return this.astAt(new ast.TableSource(tableName), pcx);
266
+ visitTableFunction(pcx) {
267
+ const tableURI = this.stripQuotes(pcx.tableURI().text);
268
+ const el = this.astAt(new ast.TableFunctionSource(tableURI), pcx);
269
+ if (ENABLE_M4_WARNINGS) {
270
+ this.astError(el, "`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`", 'warn');
271
+ }
272
+ return el;
273
+ }
274
+ visitTableMethod(pcx) {
275
+ const connId = pcx.connectionId();
276
+ const connectionName = this.astAt(this.getModelEntryName(connId), connId);
277
+ const tablePath = this.stripQuotes(pcx.tablePath().text);
278
+ return this.astAt(new ast.TableMethodSource(connectionName, tablePath), pcx);
268
279
  }
269
280
  visitTableSource(pcx) {
270
- return this.visitExploreTable(pcx.exploreTable());
281
+ const result = this.visit(pcx.exploreTable());
282
+ if (result instanceof ast_1.TableSource) {
283
+ return result;
284
+ }
285
+ throw this.internalError(pcx, `Table source matched, but ${result.elementType} found`);
271
286
  }
272
287
  visitSQLSourceName(pcx) {
273
288
  const name = this.getModelEntryName(pcx.sqlExploreNameRef());
@@ -56,7 +56,7 @@ declare class ParseStep implements TranslationStep {
56
56
  }
57
57
  declare class ImportsAndTablesStep implements TranslationStep {
58
58
  readonly parseStep: ParseStep;
59
- private alreadyLooked;
59
+ private parseReferences;
60
60
  constructor(parseStep: ParseStep);
61
61
  step(that: MalloyTranslation): DataRequestResponse | ParseResponse;
62
62
  }
@@ -194,45 +194,40 @@ class ParseStep {
194
194
  class ImportsAndTablesStep {
195
195
  constructor(parseStep) {
196
196
  this.parseStep = parseStep;
197
- this.alreadyLooked = false;
197
+ this.parseReferences = undefined;
198
198
  }
199
199
  step(that) {
200
200
  const parseReq = this.parseStep.step(that);
201
201
  if (parseReq.parse === undefined) {
202
202
  return parseReq;
203
203
  }
204
- if (!this.alreadyLooked) {
205
- this.alreadyLooked = true;
206
- const parseRefs = (0, find_external_references_1.findReferences)(that, parseReq.parse.tokenStream, parseReq.parse.root);
207
- if (parseRefs === null || parseRefs === void 0 ? void 0 : parseRefs.tables) {
208
- for (const ref in parseRefs.tables) {
209
- that.root.schemaZone.reference(ref, {
204
+ if (!this.parseReferences) {
205
+ this.parseReferences = (0, find_external_references_1.findReferences)(that, parseReq.parse.tokenStream, parseReq.parse.root);
206
+ for (const ref in this.parseReferences.tables) {
207
+ that.root.schemaZone.reference(ref, {
208
+ url: that.sourceURL,
209
+ range: this.parseReferences.tables[ref].firstReference,
210
+ });
211
+ }
212
+ for (const relativeRef in this.parseReferences.urls) {
213
+ const firstRef = this.parseReferences.urls[relativeRef];
214
+ try {
215
+ const ref = decodeURI(new URL(relativeRef, that.sourceURL).toString());
216
+ that.addChild(ref);
217
+ that.root.importZone.reference(ref, {
210
218
  url: that.sourceURL,
211
- range: parseRefs.tables[ref],
219
+ range: firstRef,
212
220
  });
213
221
  }
214
- }
215
- if (parseRefs === null || parseRefs === void 0 ? void 0 : parseRefs.urls) {
216
- for (const relativeRef in parseRefs.urls) {
217
- const firstRef = parseRefs.urls[relativeRef];
218
- try {
219
- const ref = decodeURI(new URL(relativeRef, that.sourceURL).toString());
220
- that.addChild(ref);
221
- that.root.importZone.reference(ref, {
222
- url: that.sourceURL,
223
- range: firstRef,
224
- });
225
- }
226
- catch (err) {
227
- // This import spec is so bad the URL library threw up, this
228
- // may be impossible, because it will append any garbage
229
- // to the known good rootURL assuming it is relative
230
- that.root.logger.log({
231
- message: `Malformed URL '${relativeRef}'"`,
232
- at: { url: that.sourceURL, range: firstRef },
233
- severity: 'error',
234
- });
235
- }
222
+ catch (err) {
223
+ // This import spec is so bad the URL library threw up, this
224
+ // may be impossible, because it will append any garbage
225
+ // to the known good rootURL assuming it is relative
226
+ that.root.logger.log({
227
+ message: `Malformed URL '${relativeRef}'"`,
228
+ at: { url: that.sourceURL, range: firstRef },
229
+ severity: 'error',
230
+ });
236
231
  }
237
232
  }
238
233
  }
@@ -244,7 +239,15 @@ class ImportsAndTablesStep {
244
239
  let allMissing = {};
245
240
  const missingTables = that.root.schemaZone.getUndefined();
246
241
  if (missingTables) {
247
- allMissing = { tables: missingTables };
242
+ const tables = {};
243
+ for (const key of missingTables) {
244
+ const info = this.parseReferences.tables[key];
245
+ tables[key] = {
246
+ connectionName: info.connectionName,
247
+ tablePath: info.tablePath,
248
+ };
249
+ }
250
+ allMissing = { tables };
248
251
  }
249
252
  const missingImports = that.root.importZone.getUndefined();
250
253
  if (missingImports) {
@@ -2,10 +2,29 @@ import { CommonTokenStream } from 'antlr4ts';
2
2
  import { ParseTree } from 'antlr4ts/tree';
3
3
  import { DocumentRange } from '../../model/malloy_types';
4
4
  import { MalloyTranslation } from '../parse-malloy';
5
- declare type References = Record<string, DocumentRange>;
6
- interface FinderFound {
7
- tables?: References;
8
- urls?: References;
5
+ declare type NeedImports = Record<string, DocumentRange>;
6
+ declare type NeedTables = Record<string, {
7
+ connectionName: string | undefined;
8
+ tablePath: string;
9
+ firstReference: DocumentRange;
10
+ }>;
11
+ export declare function constructTableKey(connectionName: string | undefined, tablePath: string): string;
12
+ /**
13
+ * This function parses an old-style `tableURI` into a connection name and
14
+ * table path. The name includes `deprecated` because it should only be used
15
+ * in the (deprecated) old-style `table('conn:tab')` syntax. Any use of this
16
+ * anywhere else is bad.
17
+ * @param tableURI The sting that is passed into the `table('conn:tab')` syntax.
18
+ * @returns A connection name and table path.
19
+ * @deprecated
20
+ */
21
+ export declare function deprecatedParseTableURI(tableURI: string): {
22
+ connectionName?: string;
23
+ tablePath: string;
24
+ };
25
+ export interface FindReferencesData {
26
+ tables: NeedTables;
27
+ urls: NeedImports;
9
28
  }
10
- export declare function findReferences(trans: MalloyTranslation, tokens: CommonTokenStream, parseTree: ParseTree): FinderFound | null;
29
+ export declare function findReferences(trans: MalloyTranslation, tokens: CommonTokenStream, parseTree: ParseTree): FindReferencesData;
11
30
  export {};
@@ -22,7 +22,7 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.findReferences = void 0;
25
+ exports.findReferences = exports.deprecatedParseTableURI = exports.constructTableKey = void 0;
26
26
  const ParseTreeWalker_1 = require("antlr4ts/tree/ParseTreeWalker");
27
27
  class FindExternalReferences {
28
28
  constructor(trans, tokens) {
@@ -31,31 +31,77 @@ class FindExternalReferences {
31
31
  this.needTables = {};
32
32
  this.needImports = {};
33
33
  }
34
- enterTableName(pcx) {
35
- const tableName = this.tokens.getText(pcx).slice(1, -1);
36
- if (!this.needTables[tableName]) {
37
- this.needTables[tableName] = this.trans.rangeFromContext(pcx);
34
+ registerTableReference(connectionName, tablePath, reference) {
35
+ const key = constructTableKey(connectionName, tablePath);
36
+ if (!this.needTables[key]) {
37
+ this.needTables[key] = {
38
+ connectionName,
39
+ tablePath,
40
+ firstReference: reference,
41
+ };
38
42
  }
39
43
  }
44
+ enterTableMethod(pcx) {
45
+ const connectionName = this.stripIdQuotes(pcx.connectionId().text);
46
+ const tablePath = this.tokens.getText(pcx.tablePath()).slice(1, -1);
47
+ const reference = this.trans.rangeFromContext(pcx);
48
+ this.registerTableReference(connectionName, tablePath, reference);
49
+ }
50
+ enterTableFunction(pcx) {
51
+ const tableURI = this.tokens.getText(pcx.tableURI()).slice(1, -1);
52
+ // This use of `deprecatedParseTableURI` is ok because it is for handling the
53
+ // old, soon-to-be-deprecated table syntax.
54
+ const { connectionName, tablePath } = deprecatedParseTableURI(tableURI);
55
+ const reference = this.trans.rangeFromContext(pcx);
56
+ this.registerTableReference(connectionName, tablePath, reference);
57
+ }
40
58
  enterImportURL(pcx) {
41
59
  const url = JSON.parse(pcx.JSON_STRING().text);
42
60
  if (!this.needImports[url]) {
43
61
  this.needImports[url] = this.trans.rangeFromContext(pcx);
44
62
  }
45
63
  }
64
+ stripIdQuotes(id) {
65
+ if (id[0] === '`' && id[id.length - 1] === '`') {
66
+ return id.slice(1, -1);
67
+ }
68
+ return id;
69
+ }
70
+ }
71
+ function constructTableKey(connectionName, tablePath) {
72
+ return connectionName === undefined
73
+ ? tablePath
74
+ : `${connectionName}:${tablePath}`;
46
75
  }
76
+ exports.constructTableKey = constructTableKey;
77
+ /**
78
+ * This function parses an old-style `tableURI` into a connection name and
79
+ * table path. The name includes `deprecated` because it should only be used
80
+ * in the (deprecated) old-style `table('conn:tab')` syntax. Any use of this
81
+ * anywhere else is bad.
82
+ * @param tableURI The sting that is passed into the `table('conn:tab')` syntax.
83
+ * @returns A connection name and table path.
84
+ * @deprecated
85
+ */
86
+ function deprecatedParseTableURI(tableURI) {
87
+ const parts = tableURI.match(/^([^:]*):(.*)$/);
88
+ if (parts) {
89
+ const [, firstPart, secondPart] = parts;
90
+ return { connectionName: firstPart, tablePath: secondPart };
91
+ }
92
+ else {
93
+ return { tablePath: tableURI };
94
+ }
95
+ }
96
+ exports.deprecatedParseTableURI = deprecatedParseTableURI;
47
97
  function findReferences(trans, tokens, parseTree) {
48
98
  const finder = new FindExternalReferences(trans, tokens);
49
99
  const listener = finder;
50
100
  ParseTreeWalker_1.ParseTreeWalker.DEFAULT.walk(listener, parseTree);
51
- let refs = {};
52
- if (Object.keys(finder.needTables).length > 0) {
53
- refs = { tables: finder.needTables };
54
- }
55
- if (Object.keys(finder.needImports).length > 0) {
56
- refs = { ...refs, urls: finder.needImports };
57
- }
58
- return Object.keys(refs).length > 0 ? refs : null;
101
+ return {
102
+ tables: finder.needTables,
103
+ urls: finder.needImports,
104
+ };
59
105
  }
60
106
  exports.findReferences = findReferences;
61
107
  //# sourceMappingURL=find-external-references.js.map
@@ -44,7 +44,9 @@ function prettyNeeds(response) {
44
44
  let needString = '';
45
45
  if (response.tables) {
46
46
  needString += 'Tables:\n';
47
- response.tables.forEach(table => (needString += ` - ${table}`));
47
+ for (const table in response.tables) {
48
+ needString += ` - ${table}`;
49
+ }
48
50
  }
49
51
  if (response.compileSQL) {
50
52
  needString += `Compile SQL: ${response.compileSQL.name}`;
@@ -28,6 +28,35 @@ const test_translator_1 = require("./test-translator");
28
28
  const granular_result_1 = require("../ast/types/granular-result");
29
29
  require("./parse-expects");
30
30
  describe('model statements', () => {
31
+ describe('table method', () => {
32
+ test('table method works', () => {
33
+ expect("source: testA is conn.table('aTable')").toTranslate();
34
+ });
35
+ test('table method works with quoted connection name', () => {
36
+ expect("source: testA is `conn`.table('aTable')").toTranslate();
37
+ });
38
+ test('table method fails when connection name is wrong', () => {
39
+ expect("source: testA is bad_conn.table('aTable')").not.toTranslate();
40
+ });
41
+ test('table method fails when connection is not a connection', () => {
42
+ const m = (0, test_translator_1.model) `source: a1 is a; source: testA is a1.table('aTable')`;
43
+ // This is a somewhat weird behavior from the translator. During the
44
+ // first phase of translation, we fetch "external references" (tables and
45
+ // imports). At that point we don't know that there will be an error
46
+ // "a1 is not a connection," so we attempt to fetch `aTable` from connection
47
+ // `a1` even though a1 is not a connection. The response from the application
48
+ // is basically ignored in the end, as we still eventually get the error
49
+ // that a1 is not a connection (it's a source).
50
+ m.translator.update({
51
+ errors: { tables: { 'a1:aTable': 'invalid connection' } },
52
+ });
53
+ expect(m).translationToFailWith('a1 is not a connection');
54
+ });
55
+ // TODO unskip this when ENABLE_M4_WARNINGS becomes a document annotation
56
+ test.skip('table function is deprecated', () => {
57
+ expect("testA is table('conn:aTable')").toTranslateWithWarnings("`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
58
+ });
59
+ });
31
60
  describe('source:', () => {
32
61
  test('table', () => {
33
62
  expect("source: testA is table('aTable')").toTranslate();
@@ -2409,7 +2438,7 @@ describe('translation need error locations', () => {
2409
2438
  const result = m.translate();
2410
2439
  m.update({
2411
2440
  errors: {
2412
- tables: { [(result.tables || [])[0]]: 'Bad table!' },
2441
+ tables: { [Object.keys(result.tables || {})[0]]: 'Bad table!' },
2413
2442
  },
2414
2443
  });
2415
2444
  expect(m).translationToFailWith(/Bad table!/);
@@ -254,6 +254,7 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
254
254
  this.importZone.define(testURI, testSrc);
255
255
  for (const tableName in mockSchema) {
256
256
  this.schemaZone.define(tableName, mockSchema[tableName]);
257
+ this.schemaZone.define('conn:' + tableName, mockSchema[tableName]);
257
258
  }
258
259
  }
259
260
  translate() {
@@ -17,7 +17,10 @@ export interface ProblemResponse {
17
17
  }
18
18
  export declare type FatalResponse = FinalResponse & ProblemResponse;
19
19
  export interface NeedSchemaData {
20
- tables: string[];
20
+ tables: Record<string, {
21
+ connectionName: string | undefined;
22
+ tablePath: string;
23
+ }>;
21
24
  }
22
25
  export interface NeedURLData {
23
26
  urls: string[];
package/dist/malloy.d.ts CHANGED
@@ -231,10 +231,6 @@ export declare class PreparedQuery implements Taggable {
231
231
  */
232
232
  getFlattenedQuery(defaultName: string): PreparedQuery;
233
233
  }
234
- export declare function parseTableURI(tableURI: string): {
235
- connectionName?: string;
236
- tablePath: string;
237
- };
238
234
  /**
239
235
  * A parsed Malloy document.
240
236
  */
package/dist/malloy.js CHANGED
@@ -22,7 +22,7 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.CSVWriter = exports.JSONWriter = exports.DataWriter = exports.DataRecord = exports.DataArray = exports.Result = exports.ExploreMaterializer = exports.SQLBlockMaterializer = exports.PreparedResultMaterializer = exports.QueryMaterializer = exports.ModelMaterializer = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.Runtime = exports.ExploreField = exports.JoinRelationship = exports.QueryField = exports.Query = exports.StringField = exports.UnsupportedField = exports.JSONField = exports.BooleanField = exports.NumberField = exports.TimestampField = exports.DateField = exports.TimestampTimeframe = exports.DateTimeframe = exports.AtomicField = exports.AtomicFieldType = exports.Explore = exports.SourceRelationship = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.PreparedResult = exports.DocumentCompletion = exports.DocumentSymbol = exports.DocumentPosition = exports.DocumentRange = exports.DocumentHighlight = exports.Parse = exports.parseTableURI = exports.PreparedQuery = exports.Model = exports.MalloyError = exports.Malloy = void 0;
25
+ exports.CSVWriter = exports.JSONWriter = exports.DataWriter = exports.DataRecord = exports.DataArray = exports.Result = exports.ExploreMaterializer = exports.SQLBlockMaterializer = exports.PreparedResultMaterializer = exports.QueryMaterializer = exports.ModelMaterializer = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.Runtime = exports.ExploreField = exports.JoinRelationship = exports.QueryField = exports.Query = exports.StringField = exports.UnsupportedField = exports.JSONField = exports.BooleanField = exports.NumberField = exports.TimestampField = exports.DateField = exports.TimestampTimeframe = exports.DateTimeframe = exports.AtomicField = exports.AtomicFieldType = exports.Explore = exports.SourceRelationship = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.PreparedResult = exports.DocumentCompletion = exports.DocumentSymbol = exports.DocumentPosition = exports.DocumentRange = exports.DocumentHighlight = exports.Parse = exports.PreparedQuery = exports.Model = exports.MalloyError = exports.Malloy = void 0;
26
26
  const lang_1 = require("./lang");
27
27
  const model_1 = require("./model");
28
28
  const luxon_1 = require("luxon");
@@ -110,25 +110,24 @@ class Malloy {
110
110
  if (result.tables) {
111
111
  // collect tables by connection name since there may be multiple connections
112
112
  const tablesByConnection = new Map();
113
- for (const connectionTableString of result.tables) {
114
- const { connectionName } = parseTableURI(connectionTableString);
115
- let connectionToTablesMap = tablesByConnection.get(connectionName);
116
- if (!connectionToTablesMap) {
117
- connectionToTablesMap = [connectionTableString];
113
+ for (const tableKey in result.tables) {
114
+ const { connectionName, tablePath } = result.tables[tableKey];
115
+ const connectionToTablesMap = tablesByConnection.get(connectionName);
116
+ if (connectionToTablesMap === undefined) {
117
+ tablesByConnection.set(connectionName, { [tableKey]: tablePath });
118
118
  }
119
119
  else {
120
- connectionToTablesMap.push(connectionTableString);
120
+ connectionToTablesMap[tableKey] = tablePath;
121
121
  }
122
- tablesByConnection.set(connectionName, connectionToTablesMap);
123
122
  }
124
123
  // iterate over connections, fetching schema for all missing tables
125
- for (const [connectionName, connectionTableString,] of tablesByConnection) {
124
+ for (const [connectionName, tablePathByKey] of tablesByConnection) {
126
125
  try {
127
126
  const connection = await connections.lookupConnection(connectionName);
128
127
  // TODO detect if the union of `Object.keys(tables)` and `Object.keys(errors)` is not the same
129
- // as `Object.keys(connectionTableString)`, i.e. that all tables are accounted for. Otherwise
128
+ // as `Object.keys(tablePathByKey)`, i.e. that all tables are accounted for. Otherwise
130
129
  // the translator runs into an infinite loop fetching tables.
131
- const { schemas: tables, errors } = await connection.fetchSchemaForTables(connectionTableString);
130
+ const { schemas: tables, errors } = await connection.fetchSchemaForTables(tablePathByKey);
132
131
  translator.update({ tables, errors: { tables: errors } });
133
132
  }
134
133
  catch (error) {
@@ -136,8 +135,8 @@ class Malloy {
136
135
  // with all its tables
137
136
  const tables = {};
138
137
  const errors = {};
139
- for (const table of connectionTableString) {
140
- errors[table] = error.toString();
138
+ for (const tableKey in tablePathByKey) {
139
+ errors[tableKey] = error.toString();
141
140
  }
142
141
  translator.update({ tables, errors: { tables: errors } });
143
142
  }
@@ -491,17 +490,6 @@ class PreparedQuery {
491
490
  }
492
491
  }
493
492
  exports.PreparedQuery = PreparedQuery;
494
- function parseTableURI(tableURI) {
495
- const parts = tableURI.match(/^([^:]*):(.*)$/);
496
- if (parts) {
497
- const [, firstPart, secondPart] = parts;
498
- return { connectionName: firstPart, tablePath: secondPart };
499
- }
500
- else {
501
- return { tablePath: tableURI };
502
- }
503
- }
504
- exports.parseTableURI = parseTableURI;
505
493
  /**
506
494
  * A parsed Malloy document.
507
495
  */
@@ -533,6 +533,9 @@ export interface FunctionDef extends NamedObject {
533
533
  type: 'function';
534
534
  overloads: FunctionOverloadDef[];
535
535
  }
536
+ export interface ConnectionDef extends NamedObject {
537
+ type: 'connection';
538
+ }
536
539
  export interface SQLBlockStructDef extends StructDef {
537
540
  structSource: SubquerySource;
538
541
  declaredSQLBlock?: boolean;
@@ -561,7 +564,7 @@ export interface FilterExpression {
561
564
  }
562
565
  /** Get the output name for a NamedObject */
563
566
  export declare function getIdentifier(n: AliasedName): string;
564
- export declare type NamedModelObject = StructDef | NamedQuery | FunctionDef;
567
+ export declare type NamedModelObject = StructDef | NamedQuery | FunctionDef | ConnectionDef;
565
568
  /** Result of parsing a model file */
566
569
  export interface ModelDef {
567
570
  name: string;