@malloydata/malloy 0.0.94 → 0.0.95-dev231024220124

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/README.md CHANGED
@@ -14,8 +14,8 @@ const bigquery = require("@malloydata/db-bigquery")
14
14
 
15
15
  const connection = new bigquery.BigQueryConnection("bigquery");
16
16
  const runtime = new malloy.SingleConnectionRuntime(connection);
17
- const model = runtime.loadModel("source: airports is table('malloytest.airports')")
18
- const runner = model.loadQuery("query: airports->{aggregate: airport_count is count()}")
17
+ const model = runtime.loadModel("source: airports is bigquery.table('malloytest.airports')")
18
+ const runner = model.loadQuery("run: airports->{aggregate: airport_count is count()}")
19
19
  runner.run().then((result) => {
20
20
  console.log(result.data.value) // [ { airport_count: 19793 } ]
21
21
  })
@@ -8,6 +8,7 @@ import { SourceDesc } from '../types/source-desc';
8
8
  import { QOPDesc } from '../query-properties/qop-desc';
9
9
  import { FullQuery } from '../query-elements/full-query';
10
10
  import { ViewFieldReference } from '../query-items/field-references';
11
+ import { LogSeverity } from '../../parse-log';
11
12
  export declare class SQReference extends SourceQueryNode {
12
13
  readonly ref: ModelEntryReference;
13
14
  elementType: string;
@@ -21,9 +22,9 @@ export declare class SQLegacyModify extends SourceQueryNode {
21
22
  readonly sourceQuery: SourceQueryNode;
22
23
  readonly plus: MalloyElement[];
23
24
  readonly hasPlus: boolean;
24
- readonly m4Warnings: boolean;
25
+ readonly m4Severity: LogSeverity | false;
25
26
  elementType: string;
26
- constructor(sourceQuery: SourceQueryNode, plus: MalloyElement[], hasPlus: boolean, m4Warnings: boolean);
27
+ constructor(sourceQuery: SourceQueryNode, plus: MalloyElement[], hasPlus: boolean, m4Severity: LogSeverity | false);
27
28
  getQuery(): QueryElement | undefined;
28
29
  getSource(): Source | undefined;
29
30
  isSource(): boolean;
@@ -92,12 +92,12 @@ class SQReference extends source_query_1.SourceQueryNode {
92
92
  }
93
93
  exports.SQReference = SQReference;
94
94
  class SQLegacyModify extends source_query_1.SourceQueryNode {
95
- constructor(sourceQuery, plus, hasPlus, m4Warnings) {
95
+ constructor(sourceQuery, plus, hasPlus, m4Severity) {
96
96
  super({ sourceQuery, plus });
97
97
  this.sourceQuery = sourceQuery;
98
98
  this.plus = plus;
99
99
  this.hasPlus = hasPlus;
100
- this.m4Warnings = m4Warnings;
100
+ this.m4Severity = m4Severity;
101
101
  this.elementType = 'sqLogacyModify';
102
102
  }
103
103
  getQuery() {
@@ -105,8 +105,8 @@ class SQLegacyModify extends source_query_1.SourceQueryNode {
105
105
  if (!asQuery) {
106
106
  return;
107
107
  }
108
- if (!this.hasPlus && this.m4Warnings) {
109
- this.log('Implicit query refinement is deprecated, use the `+` operator', 'warn');
108
+ if (!this.hasPlus && this.m4Severity) {
109
+ this.log('Implicit query refinement is deprecated, use the `+` operator', this.m4Severity);
110
110
  }
111
111
  const stmts = [];
112
112
  for (const stmt of this.plus) {
@@ -127,12 +127,12 @@ class SQLegacyModify extends source_query_1.SourceQueryNode {
127
127
  if (!asSource) {
128
128
  return;
129
129
  }
130
- if (this.m4Warnings) {
130
+ if (this.m4Severity) {
131
131
  if (this.hasPlus) {
132
- this.log('Source extension with "+" is deprecated, use the "extend" operator', 'warn');
132
+ this.log('Source extension with "+" is deprecated, use the "extend" operator', this.m4Severity);
133
133
  }
134
134
  else {
135
- this.log('Implicit source extension is deprecated, use the `extend` operator.', 'warn');
135
+ this.log('Implicit source extension is deprecated, use the `extend` operator.', this.m4Severity);
136
136
  }
137
137
  }
138
138
  const stmts = [];
@@ -38,7 +38,7 @@ class ProjectBuilder extends reduce_builder_1.ReduceBuilder {
38
38
  }
39
39
  execute(qp) {
40
40
  if (qp.elementType === 'having' || qp instanceof group_by_1.GroupBy) {
41
- qp.log('Illegal statement in a project query operation');
41
+ qp.log('Illegal statement in a select query operation');
42
42
  }
43
43
  else {
44
44
  super.execute(qp);
@@ -51,7 +51,7 @@ class ProjectBuilder extends reduce_builder_1.ReduceBuilder {
51
51
  from = fromSeg;
52
52
  }
53
53
  else {
54
- this.resultFS.log(`Can't refine project with ${fromSeg.type}`);
54
+ this.resultFS.log(`Can't refine select with ${fromSeg.type}`);
55
55
  return error_factory_1.ErrorFactory.projectSegment;
56
56
  }
57
57
  }
@@ -43,7 +43,7 @@ function typecheckProject(type, logTo) {
43
43
  else {
44
44
  throw new Error(`Unexpected expression type ${type.expressionType} not handled here`);
45
45
  }
46
- logTo.log(`Cannot use ${kind} field in a project operation, did you mean to use ${useInstead} operation instead?`);
46
+ logTo.log(`Cannot use ${kind} field in a select operation, did you mean to use ${useInstead} operation instead?`);
47
47
  }
48
48
  }
49
49
  exports.typecheckProject = typecheckProject;
@@ -117,7 +117,7 @@ function typecheckCalculate(type, logTo) {
117
117
  kind = 'an aggregate';
118
118
  }
119
119
  else if ((0, model_1.expressionIsScalar)(type.expressionType)) {
120
- useInstead = 'a group_by or project';
120
+ useInstead = 'a group_by or select';
121
121
  kind = 'a scalar';
122
122
  }
123
123
  else {
@@ -141,7 +141,7 @@ function typecheckAggregate(type, logTo) {
141
141
  kind = 'an analytic';
142
142
  }
143
143
  else if ((0, model_1.expressionIsScalar)(type.expressionType)) {
144
- useInstead = 'a group_by or project';
144
+ useInstead = 'a group_by or select';
145
145
  kind = 'a scalar';
146
146
  }
147
147
  else {
@@ -67,7 +67,7 @@ class TurtleDeclRoot extends pipeline_desc_1.TurtleHeadedPipe {
67
67
  }
68
68
  }
69
69
  else if (this.withRefinement) {
70
- throw this.internalError("Can't refine the head of a turtle in its definition");
70
+ throw this.internalError("Can't refine the head of a view in its definition");
71
71
  }
72
72
  let pipeOutFS = fs;
73
73
  if (modelPipe.pipeline.length > 0) {
@@ -193,7 +193,7 @@ class NestReference extends field_references_1.FieldReference {
193
193
  kind = 'an analytic';
194
194
  }
195
195
  else if ((0, model_1.expressionIsScalar)(type.expressionType)) {
196
- useInstead = 'a group_by or project';
196
+ useInstead = 'a group_by or select';
197
197
  kind = 'a scalar';
198
198
  }
199
199
  else if ((0, model_1.expressionIsAggregate)(type.expressionType)) {
@@ -49,8 +49,9 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
49
49
  * Log an error message relative to a parse node
50
50
  */
51
51
  protected contextError(cx: ParserRuleContext, msg: string, sev?: LogSeverity): void;
52
+ protected m4Severity(): LogSeverity | false;
53
+ protected m4advisory(cx: ParserRuleContext, msg: string): void;
52
54
  protected only<T extends ast.MalloyElement>(els: ast.MalloyElement[], isGood: (el: ast.MalloyElement) => T | false, desc: string): T[];
53
- protected m4WarningsEnabled(): boolean;
54
55
  protected getNumber(term: ParseTree): number;
55
56
  protected getFieldName(cx: HasID): ast.FieldName;
56
57
  protected getModelEntryName(cx: HasID): ast.ModelEntryReference;
@@ -67,7 +67,7 @@ class IgnoredElement extends ast.MalloyElement {
67
67
  this.malloySrc = src;
68
68
  }
69
69
  }
70
- const DEFAULT_COMPILER_FLAGS = ['##! m4warnings'];
70
+ const DEFAULT_COMPILER_FLAGS = ['##! m4warnings=error'];
71
71
  /**
72
72
  * ANTLR visitor pattern parse tree traversal. Generates a Malloy
73
73
  * AST from an ANTLR parse tree.
@@ -118,6 +118,19 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
118
118
  severity: sev,
119
119
  });
120
120
  }
121
+ m4Severity() {
122
+ const m4severityTag = this.compilerFlags.tag('m4warnings');
123
+ if (m4severityTag) {
124
+ return m4severityTag.text() === 'warn' ? 'warn' : 'error';
125
+ }
126
+ return false;
127
+ }
128
+ m4advisory(cx, msg) {
129
+ const m4 = this.m4Severity();
130
+ if (m4) {
131
+ this.contextError(cx, msg, m4);
132
+ }
133
+ }
121
134
  only(els, isGood, desc) {
122
135
  const acceptable = [];
123
136
  for (const el of els) {
@@ -131,9 +144,6 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
131
144
  }
132
145
  return acceptable;
133
146
  }
134
- m4WarningsEnabled() {
135
- return this.compilerFlags.has('m4warnings');
136
- }
137
147
  getNumber(term) {
138
148
  return Number.parseInt(term.text);
139
149
  }
@@ -179,9 +189,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
179
189
  }
180
190
  getFilterShortcut(cx) {
181
191
  const el = this.getFilterElement(cx.fieldExpr());
182
- if (this.m4WarningsEnabled()) {
183
- this.astError(el, 'Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead', 'warn');
184
- }
192
+ this.m4advisory(cx, 'Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
185
193
  return new ast.Filter([el]);
186
194
  }
187
195
  getPlainString(cx) {
@@ -208,8 +216,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
208
216
  }
209
217
  makeSqlString(pcx, sqlStr) {
210
218
  for (const part of pcx.sqlInterpolation()) {
211
- if (part.CLOSE_CODE() && this.m4WarningsEnabled()) {
212
- this.contextError(part, 'Use %{ ... } instead of %{ ... }%', 'warn');
219
+ if (part.CLOSE_CODE()) {
220
+ this.m4advisory(part, 'Use %{ ... } instead of %{ ... }%');
213
221
  }
214
222
  }
215
223
  for (const part of (0, parse_utils_1.getStringParts)(pcx)) {
@@ -288,13 +296,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
288
296
  getSourceExtensions(pcx) {
289
297
  const extensions = pcx === null || pcx === void 0 ? void 0 : pcx.exploreProperties();
290
298
  const sourceDesc = this.astAt(this.visitExploreProperties(extensions), pcx);
291
- if (this.m4WarningsEnabled()) {
292
- if (pcx.refineOperator()) {
293
- this.contextError(pcx, 'Source extension with "+" is deprecated, use the "extend" operator', 'warn');
294
- }
295
- else if (pcx.EXTEND() === undefined && this.m4WarningsEnabled()) {
296
- this.contextError(pcx, 'Implicit source extension is deprecated, use the `extend` operator.', 'warn');
297
- }
299
+ if (pcx.refineOperator()) {
300
+ this.m4advisory(pcx, 'Source extension with "+" is deprecated, use the "extend" operator');
301
+ }
302
+ else if (pcx.EXTEND() === undefined) {
303
+ this.m4advisory(pcx, 'Implicit source extension is deprecated, use the `extend` operator.');
298
304
  }
299
305
  return sourceDesc;
300
306
  }
@@ -310,9 +316,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
310
316
  visitTableFunction(pcx) {
311
317
  const tableURI = this.getPlainString(pcx.tableURI());
312
318
  const el = this.astAt(new ast.TableFunctionSource(tableURI), pcx);
313
- if (this.m4WarningsEnabled()) {
314
- this.astError(el, "`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`", 'warn');
315
- }
319
+ this.m4advisory(pcx, "`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
316
320
  return el;
317
321
  }
318
322
  visitTableMethod(pcx) {
@@ -324,9 +328,6 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
324
328
  getLegacySQLSouce(pcx) {
325
329
  const name = this.getModelEntryName(pcx);
326
330
  const res = this.astAt(new ast.FromSQLSource(name), pcx);
327
- if (this.m4WarningsEnabled()) {
328
- this.astError(res, '`from_sql` is deprecated; use `connection_name.sql(...)` as a source directly', 'warn');
329
- }
330
331
  return res;
331
332
  }
332
333
  visitSqlSource(pcx) {
@@ -411,9 +412,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
411
412
  }
412
413
  visitQueryJoinStatement(pcx) {
413
414
  const result = this.astAt(this.visit(pcx.joinStatement()), pcx);
414
- if (this.m4WarningsEnabled()) {
415
- this.astError(result, 'Joins in queries are deprecated, move into an `extend:` block.', 'warn');
416
- }
415
+ this.m4advisory(pcx, 'Joins in queries are deprecated, move into an `extend:` block.');
417
416
  return result;
418
417
  }
419
418
  visitJoinOn(pcx) {
@@ -477,9 +476,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
477
476
  const defs = this.getFieldDefs(pcx.defList().fieldDef(), ast.DeclareFieldDeclaration);
478
477
  const stmt = new ast.DeclareFields(defs);
479
478
  const result = this.astAt(stmt, pcx);
480
- if (this.m4WarningsEnabled()) {
481
- this.astError(result, '`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block', 'warn');
482
- }
479
+ this.m4advisory(pcx, '`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
483
480
  return result;
484
481
  }
485
482
  visitExploreRenameDef(pcx) {
@@ -500,9 +497,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
500
497
  visitFilterByShortcut(pcx) {
501
498
  const el = this.getFilterElement(pcx.fieldExpr());
502
499
  const res = this.astAt(new ast.Filter([el]), pcx);
503
- if (this.m4WarningsEnabled()) {
504
- this.astError(el, 'Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead', 'warn');
505
- }
500
+ this.m4advisory(pcx, 'Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
506
501
  return res;
507
502
  }
508
503
  visitWhereStatement(pcx) {
@@ -525,8 +520,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
525
520
  const queryDefs = this.visitSubQueryDefList(pcx.subQueryDefList());
526
521
  const blockNotes = this.getNotes(pcx.tags());
527
522
  queryDefs.extendNote({ blockNotes });
528
- if (this.m4WarningsEnabled() && pcx.QUERY()) {
529
- this.contextError(pcx, 'Use view: inside of a source instead of query:', 'warn');
523
+ if (pcx.QUERY()) {
524
+ this.m4advisory(pcx, 'Use view: inside of a source instead of query:');
530
525
  }
531
526
  return queryDefs;
532
527
  }
@@ -647,7 +642,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
647
642
  }
648
643
  throw this.internalError(pcx, 'Unexpected element in fieldCollectionMember');
649
644
  }
650
- // "FieldCollection" can only mean a project statement today
645
+ // "FieldCollection" can only mean a select statement today
651
646
  visitFieldCollection(pcx) {
652
647
  const fields = pcx
653
648
  .collectionMember()
@@ -655,8 +650,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
655
650
  return this.astAt(new ast.ProjectStatement(fields), pcx);
656
651
  }
657
652
  visitProjectStatement(pcx) {
658
- if (this.m4WarningsEnabled() && pcx.PROJECT()) {
659
- this.contextError(pcx, 'project: keyword is deprecated, use select:', 'warn');
653
+ if (pcx.PROJECT()) {
654
+ this.m4advisory(pcx, 'project: keyword is deprecated, use select:');
660
655
  }
661
656
  const stmt = this.visitFieldCollection(pcx.fieldCollection());
662
657
  stmt.extendNote({ blockNotes: this.getNotes(pcx.tags()) });
@@ -724,9 +719,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
724
719
  const topN = this.getNumber(pcx.INTEGER_LITERAL());
725
720
  let top;
726
721
  if (byCx) {
727
- if (this.m4WarningsEnabled()) {
728
- this.contextError(byCx, 'by clause of top statement unupported. Use order_by instead', 'warn');
729
- }
722
+ this.m4advisory(byCx, 'by clause of top statement unupported. Use order_by instead');
730
723
  const nameCx = byCx.fieldName();
731
724
  if (nameCx) {
732
725
  const name = this.getFieldName(nameCx);
@@ -747,8 +740,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
747
740
  }
748
741
  buildPipelineFromName(pipe, pipeCx) {
749
742
  const firstCx = pipeCx.firstSegment();
750
- if (this.m4WarningsEnabled() && firstCx.ARROW()) {
751
- this.contextError(firstCx, "Leading '->' in a view or nest definition is no longer needed.", 'warn');
743
+ if (firstCx.ARROW()) {
744
+ this.m4advisory(firstCx, "Leading '->' in a view or nest definition is no longer needed.");
752
745
  }
753
746
  const nameCx = firstCx.exploreQueryName();
754
747
  if (nameCx) {
@@ -794,9 +787,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
794
787
  const notes = this.getNotes(pcx.topLevelAnonQueryDef().tags());
795
788
  const blockNotes = this.getNotes(pcx.tags());
796
789
  theQuery.extendNote({ notes, blockNotes });
797
- if (this.m4WarningsEnabled()) {
798
- this.contextError(defCx, 'Anonymous `query:` statements are deprecated, use `run:` instead', 'warn');
799
- }
790
+ this.m4advisory(defCx, 'Anonymous `query:` statements are deprecated, use `run:` instead');
800
791
  return this.astAt(theQuery, pcx);
801
792
  }
802
793
  visitRunStatement(pcx) {
@@ -939,9 +930,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
939
930
  throw this.internalError(pcx, `untranslatable comparison operator '${op}'`);
940
931
  }
941
932
  visitExprCountDisinct(pcx) {
942
- if (this.m4WarningsEnabled()) {
943
- this.contextError(pcx, '`count(distinct expression)` deprecated, use `count(expression)` instead', 'warn');
944
- }
933
+ this.m4advisory(pcx, '`count(distinct expression)` deprecated, use `count(expression)` instead');
945
934
  return this.astAt(new ast.ExprCountDistinct(this.getFieldExpr(pcx.fieldExpr())), pcx);
946
935
  }
947
936
  visitExprUngroup(pcx) {
@@ -1008,10 +997,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1008
997
  const expr = exprDef ? this.getFieldExpr(exprDef) : undefined;
1009
998
  const source = undefined;
1010
999
  const aggFunc = pcx.aggregate().text.toLowerCase();
1011
- if (this.m4WarningsEnabled()) {
1012
- if (pcx.STAR()) {
1013
- this.contextError(pcx, `* illegal inside ${aggFunc}()`, 'warn');
1014
- }
1000
+ if (pcx.STAR()) {
1001
+ this.m4advisory(pcx, `* illegal inside ${aggFunc}()`);
1015
1002
  }
1016
1003
  if (aggFunc === 'count') {
1017
1004
  return this.astAt(expr ? new ast.ExprCountDistinct(expr) : new ast.ExprCount(), pcx);
@@ -1214,9 +1201,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1214
1201
  }
1215
1202
  stmt.is = blockName;
1216
1203
  const result = this.astAt(stmt, pcx);
1217
- if (this.m4WarningsEnabled()) {
1218
- this.astError(result, '`sql:` statement is deprecated, use `connection_name.sql(...)` instead', 'warn');
1219
- }
1204
+ this.m4advisory(pcx, '`sql:` statement is deprecated, use `connection_name.sql(...)` instead');
1220
1205
  return result;
1221
1206
  }
1222
1207
  visitSampleStatement(pcx) {
@@ -1266,11 +1251,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1266
1251
  }
1267
1252
  const sqExpr = this.getSqExpr(pcx.sqExpr());
1268
1253
  const hasPlus = !!pcx.PLUS();
1269
- return this.astAt(new ast.SQLegacyModify(sqExpr, plus, hasPlus, this.m4WarningsEnabled()), pcx);
1254
+ return this.astAt(new ast.SQLegacyModify(sqExpr, plus, hasPlus, this.m4Severity()), pcx);
1270
1255
  }
1271
1256
  visitSQID(pcx) {
1272
- if (this.m4WarningsEnabled() && pcx.ARROW()) {
1273
- this.contextError(pcx, 'Leading arrow (`->`) when referencing a query is deprecated; remove the arrow', 'warn');
1257
+ if (pcx.ARROW()) {
1258
+ this.m4advisory(pcx, 'Leading arrow (`->`) when referencing a query is deprecated; remove the arrow');
1274
1259
  }
1275
1260
  const ref = this.getModelEntryName(pcx);
1276
1261
  return this.astAt(new ast.SQReference(ref), pcx.id());
@@ -1323,16 +1308,16 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1323
1308
  visitSQRefinedQuery(pcx) {
1324
1309
  const refineThis = this.getSqExpr(pcx.sqExpr());
1325
1310
  const refine = pcx.queryRefinement();
1326
- if (this.m4WarningsEnabled() && refine.REFINE()) {
1327
- this.contextError(refine, 'The `refine` keyword is deprecated, use the `+` operator', 'warn');
1311
+ if (refine.REFINE()) {
1312
+ this.m4advisory(refine, 'The `refine` keyword is deprecated, use the `+` operator');
1328
1313
  }
1329
1314
  const refined = new ast.SQRefinedQuery(refineThis, this.getQueryRefinements(refine));
1330
1315
  return this.astAt(refined, pcx);
1331
1316
  }
1332
1317
  visitSQFrom(pcx) {
1333
1318
  const fromThis = this.getSqExpr(pcx.sqExpr());
1334
- if (pcx.FROM() && this.m4WarningsEnabled()) {
1335
- this.contextError(pcx, '`from(some_query)` is deprecated; use `some_query` directly', 'warn');
1319
+ if (pcx.FROM()) {
1320
+ this.m4advisory(pcx, '`from(some_query)` is deprecated; use `some_query` directly');
1336
1321
  }
1337
1322
  return this.astAt(new ast.SQFrom(fromThis), pcx);
1338
1323
  }
@@ -1347,6 +1332,7 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
1347
1332
  visitSQLegacySQLBlock(pcx) {
1348
1333
  const theBlock = this.getLegacySQLSouce(pcx.sqlExploreNameRef());
1349
1334
  const sqExpr = new ast.SQSourceWrapper(theBlock);
1335
+ this.m4advisory(pcx, '`from_sql` is deprecated; use `connection_name.sql(...)` as a source directly');
1350
1336
  return this.astAt(sqExpr, pcx);
1351
1337
  }
1352
1338
  visitSQSQL(pcx) {
@@ -189,8 +189,8 @@ class ParseStep {
189
189
  sourceURL: that.sourceURL,
190
190
  importBaseURL: that.importBaseURL || that.sourceURL,
191
191
  rangeFromContext: cx => that.rangeFromContext(cx),
192
- // TODO put the real version here
193
- malloyVersion: '0.0.0-m4',
192
+ // TODO some way to not forget to update this
193
+ malloyVersion: '4.0.0',
194
194
  };
195
195
  }
196
196
  }
@@ -29,7 +29,7 @@ function testHelpContext(source, position, type, token) {
29
29
  const helpContext = doc.helpContext(position).helpContext;
30
30
  expect(helpContext).toEqual({ type, token });
31
31
  }
32
- const source = `source: foo is table('bar') {
32
+ const source = `source: foo is DB.table('bar') extend {
33
33
  where: bazz ~ 'biff'
34
34
  query: foo is {
35
35
  group_by: bazz
@@ -61,7 +61,7 @@ function testLens(source, path) {
61
61
  expect(current).toMatchObject(expected);
62
62
  }
63
63
  test('source symbols are included', () => {
64
- testSymbol((0, test_translator_1.markSource) `source: ${"flights is table('my.table.flights')"}`, 'flights', 'explore', [0]);
64
+ testSymbol((0, test_translator_1.markSource) `source: ${"flights is DB.table('my.table.flights')"}`, 'flights', 'explore', [0]);
65
65
  });
66
66
  test('query symbols are included', () => {
67
67
  testSymbol((0, test_translator_1.markSource) `query: ${'flights_by_carrier is flights -> by_carrier'}`, 'flights_by_carrier', 'query', [0]);
@@ -75,56 +75,56 @@ test('run (ref) symbols are included', () => {
75
75
  });
76
76
  test('expression field defs are included', () => {
77
77
  testSymbol((0, test_translator_1.markSource) `
78
- source: flights is table('my.table.flights') {
78
+ source: flights is DB.table('my.table.flights') extend {
79
79
  dimension: ${'one is 1'}
80
80
  }
81
81
  `, 'one', 'field', [0, 0]);
82
82
  });
83
83
  test('renamed fields are included', () => {
84
84
  testSymbol((0, test_translator_1.markSource) `
85
- source: flights is table('my.table.flights') {
85
+ source: flights is DB.table('my.table.flights') extend {
86
86
  rename: ${'field_two is field_2'}
87
87
  }
88
88
  `, 'field_two', 'field', [0, 0]);
89
89
  });
90
90
  test('name only fields are included', () => {
91
91
  testSymbol((0, test_translator_1.markSource) `
92
- source: flights is table('my.table.flights') {
92
+ source: flights is DB.table('my.table.flights') extend {
93
93
  dimension: ${'field_two is field_2'}
94
94
  }
95
95
  `, 'field_two', 'field', [0, 0]);
96
96
  });
97
97
  test('turtle fields are included', () => {
98
98
  testSymbol((0, test_translator_1.markSource) `
99
- source: flights is table('my.table.flights') {
99
+ source: flights is DB.table('my.table.flights') extend {
100
100
  query: ${'my_turtle is { group_by: a }'}
101
101
  }
102
102
  `, 'my_turtle', 'query', [0, 0]);
103
103
  });
104
104
  test('turtle children fields are included', () => {
105
105
  testSymbol((0, test_translator_1.markSource) `
106
- source: flights is table('my.table.flights') {
106
+ source: flights is DB.table('my.table.flights') extend {
107
107
  query: my_turtle is { group_by: ${'a'} }
108
108
  }
109
109
  `, 'a', 'field', [0, 0, 0]);
110
110
  });
111
111
  test('turtle children turtles are included', () => {
112
112
  testSymbol((0, test_translator_1.markSource) `
113
- source: flights is table('my.table.flights') {
113
+ source: flights is DB.table('my.table.flights') extend {
114
114
  query: my_turtle is { nest: ${'inner_turtle is { group_by: a }'} }
115
115
  }
116
116
  `, 'inner_turtle', 'query', [0, 0, 0]);
117
117
  });
118
118
  test('join withs are included', () => {
119
119
  testSymbol((0, test_translator_1.markSource) `
120
- source: flights is table('my.table.flights') {
120
+ source: flights is DB.table('my.table.flights') extend {
121
121
  join_one: ${'a is b with c'}
122
122
  }
123
123
  `, 'a', 'join', [0, 0]);
124
124
  });
125
125
  test('join ons are included', () => {
126
126
  testSymbol((0, test_translator_1.markSource) `
127
- source: flights is table('my.table.flights') {
127
+ source: flights is DB.table('my.table.flights') extend {
128
128
  join_one: ${'a is b on c'}
129
129
  }
130
130
  `, 'a', 'join', [0, 0]);
@@ -134,15 +134,15 @@ test('source lenses go before block annotations when one source', () => {
134
134
  # tag2
135
135
  source:
136
136
  # tag3
137
- flights is table('my.table.flights')`}`, [0]);
137
+ flights is DB.table('my.table.flights')`}`, [0]);
138
138
  });
139
139
  test('source lenses go before individual annotations when more than one source', () => {
140
140
  testLens((0, test_translator_1.markSource) `# tag
141
141
  source:
142
142
  ${`# tag2
143
- flights is table('my.table.flights')`}
143
+ flights is DB.table('my.table.flights')`}
144
144
 
145
- flights2 is table('my.table.flights')`, [0]);
145
+ flights2 is DB.table('my.table.flights')`, [0]);
146
146
  });
147
147
  test('query lenses go before block annotations when one source', () => {
148
148
  testLens((0, test_translator_1.markSource) `${`# tag
@@ -174,7 +174,7 @@ test('run lenses go before block annotations', () => {
174
174
  flights -> by_carrier`}`, [0]);
175
175
  });
176
176
  test('(regression) query does not use source block range', () => {
177
- testLens((0, test_translator_1.markSource) `source: a is table('b') {
177
+ testLens((0, test_translator_1.markSource) `source: a is DB.table('b') extend {
178
178
  query:
179
179
  ${'x is {select: *}'}
180
180
  y is {select: *}
@@ -152,7 +152,7 @@ describe('expressions', () => {
152
152
  });
153
153
  test('shortcut filtered measure m4warning', () => {
154
154
  expect(`
155
- ##! m4warnings
155
+ ##! m4warnings=warn
156
156
  run: a -> {
157
157
  group_by: ai
158
158
  aggregate: x is avg(ai) {? astr = 'why?' }
@@ -173,43 +173,44 @@ describe('expressions', () => {
173
173
  });
174
174
  describe('aggregate forms', () => {
175
175
  const m = (0, test_translator_1.model) `
176
- source: root is a {
176
+ source: root is a extend {
177
177
  rename: column is ai
178
178
  rename: nested is astruct
179
179
  rename: inline is aninline
180
180
  dimension: field is column * 2
181
181
  dimension: many_field is many.column * 2
182
182
  dimension: many_one_field is many.column + one.column
183
- join_one: one is a {
183
+ join_one: one is a extend {
184
184
  rename: column is ai
185
185
  dimension: field is column * 2
186
186
  dimension: many_field is many.column * 2
187
- join_many: many is a {
187
+ join_many: many is a extend {
188
188
  rename: column is ai
189
189
  dimension: field is column * 2
190
190
  dimension: constant is 1
191
191
  } on true
192
192
  } on true
193
- join_many: many is a {
193
+ join_many: many is a extend {
194
194
  rename: column is ai
195
195
  dimension: field is column * 2
196
196
  dimension: constant is 1
197
- join_one: one is a {
197
+ join_one: one is a extend {
198
198
  rename: column is ai
199
199
  dimension: field is column * 2
200
- join_one: one is a {
200
+ join_one: one is a extend {
201
201
  rename: column is ai
202
202
  dimension: field is column * 2
203
203
  } on true
204
204
  } on true
205
205
  } on true
206
- join_cross: cross is a {
206
+ join_cross: cross is a extend {
207
207
  rename: column is ai
208
208
  dimension: field is column * 2
209
209
  } on true
210
210
  }
211
211
  `;
212
212
  m.translator.translate();
213
+ expect(m).toTranslate();
213
214
  const modelX = (0, test_translator_1.makeModelFunc)({
214
215
  model: m.translator.modelDef,
215
216
  wrap: x => `run: root -> { aggregate: x is ${x} }`,
@@ -417,7 +418,7 @@ describe('expressions', () => {
417
418
  });
418
419
  test('lag(sum(output))', () => {
419
420
  expect((0, test_translator_1.model) `
420
- ##! m4warnings
421
+ ##! m4warnings=warn
421
422
  run: a -> {
422
423
  group_by: output is 1
423
424
  calculate: bar is lag(sum(output))
@@ -425,7 +426,7 @@ describe('expressions', () => {
425
426
  });
426
427
  test('count(distinct column)', () => {
427
428
  expect((0, test_translator_1.model) `
428
- ##! m4warnings
429
+ ##! m4warnings=warn
429
430
  run: a -> {
430
431
  aggregate: x is count(distinct astr)
431
432
  }`).toTranslateWithWarnings('`count(distinct expression)` deprecated, use `count(expression)` instead');
@@ -51,7 +51,7 @@ describe('model statements', () => {
51
51
  expect(m).translationToFailWith('a1 is not a connection');
52
52
  });
53
53
  test('table function is deprecated', () => {
54
- expect(`##! m4warnings
54
+ expect(`##! m4warnings=warn
55
55
  source: testA is table('_db_:aTable')
56
56
  `).toTranslateWithWarnings("`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
57
57
  });
@@ -541,7 +541,7 @@ describe('sql expressions', () => {
541
541
  }
542
542
  });
543
543
  test('sql statement deprecation warning', () => {
544
- const m = new test_translator_1.TestTranslator(`##! m4warnings
544
+ const m = new test_translator_1.TestTranslator(`##! m4warnings=warn
545
545
  sql: bad_sql is {select: """SELECT 1 as one"""}`);
546
546
  const req = m.translate().compileSQL;
547
547
  if (req) {
@@ -552,7 +552,7 @@ describe('sql expressions', () => {
552
552
  expect(m).toTranslateWithWarnings('`sql:` statement is deprecated, use `connection_name.sql(...)` instead');
553
553
  });
554
554
  test('from_sql deprecation warning', () => {
555
- const m = new test_translator_1.TestTranslator(`##! m4warnings
555
+ const m = new test_translator_1.TestTranslator(`##! m4warnings=warn
556
556
  sql: bad_sql is {select: """SELECT 1 as one"""}
557
557
  source: foo is from_sql(bad_sql)`);
558
558
  const req = m.translate().compileSQL;
@@ -812,7 +812,7 @@ describe('extend and refine', () => {
812
812
  });
813
813
  describe('m4 warnings', () => {
814
814
  test('implicit refine in nest', () => {
815
- expect(`##! m4warnings
815
+ expect(`##! m4warnings=warn
816
816
  source: c is a extend {
817
817
  view: x is { select: * }
818
818
  }
@@ -822,20 +822,20 @@ describe('extend and refine', () => {
822
822
  }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `+` operator');
823
823
  });
824
824
  test('implicit refine in turtle works', () => {
825
- expect(`##! m4warnings
825
+ expect(`##! m4warnings=warn
826
826
  source: c is a extend {
827
827
  view: x is { select: * }
828
828
  view: y is x { limit: 1 }
829
829
  }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `+` operator');
830
830
  });
831
831
  test('implicit query refinement', () => {
832
- expect(`##! m4warnings
832
+ expect(`##! m4warnings=warn
833
833
  query: q is a -> { group_by: ai }
834
834
  run: q { group_by: three is 3 }
835
835
  `).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `+` operator');
836
836
  });
837
837
  test('implicit source extension', () => {
838
- expect(`##! m4warnings
838
+ expect(`##! m4warnings=warn
839
839
  source: s is a { dimension: ai_2 is ai + ai }`).toTranslateWithWarnings('Implicit source extension is deprecated, use the `extend` operator.');
840
840
  });
841
841
  });
@@ -843,13 +843,13 @@ describe('extend and refine', () => {
843
843
  describe('miscellaneous m4 warnings', () => {
844
844
  test('from() is deprecated', () => {
845
845
  expect(`
846
- ##! m4warnings
846
+ ##! m4warnings=warn
847
847
  query: q is a -> { select: * }
848
848
  source: c is from(q) extend {
849
849
  dimension: x is 1
850
850
  }`).toTranslateWithWarnings('`from(some_query)` is deprecated; use `some_query` directly');
851
851
  expect(`
852
- ##! m4warnings
852
+ ##! m4warnings=warn
853
853
  query: q is a -> { select: * }
854
854
  source: c is q extend {
855
855
  dimension: x is 1
@@ -857,28 +857,28 @@ describe('miscellaneous m4 warnings', () => {
857
857
  });
858
858
  test('project is deprecated', () => {
859
859
  expect(`
860
- ##! m4warnings
860
+ ##! m4warnings=warn
861
861
  query: q is a -> { project: * }
862
862
  `).toTranslateWithWarnings('project: keyword is deprecated, use select:');
863
863
  });
864
864
  test('query leading arrow', () => {
865
865
  expect(`
866
- ##! m4warnings
866
+ ##! m4warnings=warn
867
867
  query: x is a -> { select: * }
868
868
  run: x
869
869
  `).toTranslate();
870
870
  expect(`
871
- ##! m4warnings
871
+ ##! m4warnings=warn
872
872
  query: x is a -> { select: * }
873
873
  run: x -> { select: * }
874
874
  `).toTranslate();
875
875
  expect(`
876
- ##! m4warnings
876
+ ##! m4warnings=warn
877
877
  query: x is a -> { select: * }
878
878
  run: -> x
879
879
  `).toTranslateWithWarnings('Leading arrow (`->`) when referencing a query is deprecated; remove the arrow');
880
880
  expect(`
881
- ##! m4warnings
881
+ ##! m4warnings=warn
882
882
  query: x is a -> { select: * }
883
883
  run: -> x -> { select: * }
884
884
  `).toTranslateWithWarnings('Leading arrow (`->`) when referencing a query is deprecated; remove the arrow');
@@ -29,7 +29,7 @@ describe('query:', () => {
29
29
  describe('basic query syntax', () => {
30
30
  test('run:anonymous query', () => expect('run: a -> { group_by: astr }').toTranslate());
31
31
  test('query:anonymous query m4 warning', () => {
32
- expect((0, test_translator_1.markSource) `##! m4warnings
32
+ expect((0, test_translator_1.markSource) `##! m4warnings=warn
33
33
  query: ${'a -> { group_by: astr }'}`).toTranslateWithWarnings('Anonymous `query:` statements are deprecated, use `run:` instead');
34
34
  });
35
35
  test('named query:', () => expect('query: aq is a -> { group_by: astr }').toTranslate());
@@ -67,7 +67,7 @@ describe('query:', () => {
67
67
  });
68
68
  test('query with shortcut filtered turtle', () => {
69
69
  expect(`##! -m4warnings
70
- query: allA is ab -> aturtle + {? astr ~ 'a%' }`).toTranslate();
70
+ query: allA is ab -> aturtle + { where: astr ~ 'a%' }`).toTranslate();
71
71
  });
72
72
  test('query with filtered turtle', () => {
73
73
  expect("query: allA is ab -> aturtle + { where: astr ~ 'a%' }").toTranslate();
@@ -215,24 +215,24 @@ describe('query:', () => {
215
215
  expect('source: a1 is a extend { measure: s is lag(count())}').translationToFailWith('Cannot use an analytic field in a measure declaration');
216
216
  });
217
217
  test('cannot use scalar in aggregate', () => {
218
- expect('run: a -> { aggregate: s is 1}').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
218
+ expect('run: a -> { aggregate: s is 1}').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or select operation instead?');
219
219
  });
220
220
  test('cannot use analytic in aggregate', () => {
221
221
  expect('run: a -> { aggregate: s is lag(count())}').translationToFailWith('Cannot use an analytic field in an aggregate operation, did you mean to use a calculate operation instead?');
222
222
  });
223
223
  test('cannot use scalar in calculate', () => {
224
- expect('run: a -> { group_by: a is 1; calculate: s is 1 }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
224
+ expect('run: a -> { group_by: a is 1; calculate: s is 1 }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?');
225
225
  });
226
226
  test('cannot use aggregate in calculate', () => {
227
227
  expect('run: a -> { group_by: a is 1; calculate: s is count() }').translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
228
228
  });
229
229
  test('cannot use aggregate in project', () => {
230
- expect('run: a -> { select: s is count() }').translationToFailWith('Cannot use an aggregate field in a project operation, did you mean to use an aggregate operation instead?');
230
+ expect('run: a -> { select: s is count() }').translationToFailWith('Cannot use an aggregate field in a select operation, did you mean to use an aggregate operation instead?');
231
231
  });
232
232
  test('cannot use analytic in project', () => {
233
- expect('run: a -> { select: s is row_number() }').translationToFailWith('Cannot use an analytic field in a project operation, did you mean to use a calculate operation instead?');
233
+ expect('run: a -> { select: s is row_number() }').translationToFailWith('Cannot use an analytic field in a select operation, did you mean to use a calculate operation instead?');
234
234
  });
235
- test('cannot use analytic in declare', () => {
235
+ test('cannot use analytic in extended source', () => {
236
236
  expect(`##! -m4warnings
237
237
  run: a -> { group_by: a is 1; declare: s is row_number() }`).translationToFailWith('Analytic expressions can not be used in a declare block');
238
238
  });
@@ -254,16 +254,16 @@ describe('query:', () => {
254
254
  expect('run: a extend { view: q is { group_by: x is 1 } } -> { group_by: q }').translationToFailWith('Cannot use a view field in a group_by operation, did you mean to use a nest operation instead?');
255
255
  });
256
256
  test('cannot use scalar in aggregate', () => {
257
- expect('run: a -> { extend: {dimension: aconst is 1} aggregate: aconst }').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
257
+ expect('run: a -> { extend: {dimension: aconst is 1} aggregate: aconst }').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or select operation instead?');
258
258
  });
259
259
  test('cannot use scalar in calculate', () => {
260
- expect('run: a -> { extend: {dimension: aconst is 1} group_by: x is 1; calculate: aconst }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
260
+ expect('run: a -> { extend: {dimension: aconst is 1} group_by: x is 1; calculate: aconst }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?');
261
261
  });
262
262
  test('cannot use aggregate in calculate', () => {
263
263
  expect('run: a -> { extend: {measure: acount is count()} group_by: x is 1; calculate: acount }').translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
264
264
  });
265
265
  test('cannot use query in project', () => {
266
- expect('run: a extend { view: q is { group_by: x is 1 } } -> { select: q }').translationToFailWith('Cannot use a view field in a project operation, did you mean to use a nest operation instead?');
266
+ expect('run: a extend { view: q is { group_by: x is 1 } } -> { select: q }').translationToFailWith('Cannot use a view field in a select operation, did you mean to use a nest operation instead?');
267
267
  });
268
268
  test('cannot use query in index', () => {
269
269
  expect('run: a extend { view: q is { group_by: x is 1 } } -> { index: q }').translationToFailWith('Cannot use a view field in an index operation');
@@ -288,7 +288,7 @@ describe('query:', () => {
288
288
  }
289
289
  run: a1 + {
290
290
  calculate: b is c
291
- }`).translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
291
+ }`).translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?');
292
292
  });
293
293
  test('cannot use analytic in group_by, preserved over refinement', () => {
294
294
  expect(`query: a1 is a -> {
@@ -765,11 +765,11 @@ describe('query:', () => {
765
765
  expect('run: a->{ top: 5; group_by: astr }').toTranslate();
766
766
  });
767
767
  test('top N by field', () => {
768
- expect(`##! m4warnings
768
+ expect(`##! m4warnings=warn
769
769
  run: a->{top: 5 ${'by astr'}; group_by: astr}`).toTranslateWithWarnings('by clause of top statement unupported. Use order_by instead');
770
770
  });
771
771
  test('top N by expression', () => {
772
- expect(`##! m4warnings
772
+ expect(`##! m4warnings=warn
773
773
  run: ab->{top: 5 by ai + 1; group_by: ai}`).toTranslateWithWarnings('by clause of top statement unupported. Use order_by instead');
774
774
  });
775
775
  test('limit N', () => {
@@ -895,15 +895,15 @@ describe('query:', () => {
895
895
  });
896
896
  describe('declare/query join warnings', () => {
897
897
  test('declare warning in query', () => {
898
- expect((0, test_translator_1.markSource) `##! m4warnings
898
+ expect((0, test_translator_1.markSource) `##! m4warnings=warn
899
899
  run: a -> { ${'declare: x is 1'}; group_by: x }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
900
900
  });
901
901
  test('declare warning in source', () => {
902
- expect((0, test_translator_1.markSource) `##! m4warnings
902
+ expect((0, test_translator_1.markSource) `##! m4warnings=warn
903
903
  source: a2 is a extend { ${'declare: x is 1'} }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
904
904
  });
905
905
  test('joins in query', () => {
906
- expect((0, test_translator_1.markSource) `##! m4warnings
906
+ expect((0, test_translator_1.markSource) `##! m4warnings=warn
907
907
  run: a -> { ${'join_one: b on true'}; ${'join_many: c is b on true'}; ${'join_cross: d is b on true'}; group_by: b.astr }`).toTranslateWithWarnings('Joins in queries are deprecated, move into an `extend:` block.', 'Joins in queries are deprecated, move into an `extend:` block.', 'Joins in queries are deprecated, move into an `extend:` block.');
908
908
  });
909
909
  });
@@ -34,7 +34,7 @@ describe('source:', () => {
34
34
  });
35
35
  test('shorcut fitlered table m4warning', () => {
36
36
  expect(`
37
- ##! m4warnings
37
+ ##! m4warnings=warn
38
38
  source: xA is _db_.table('aTable') extend {? astr ~ 'a%' }
39
39
  `).toTranslateWithWarnings('Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
40
40
  });
@@ -210,12 +210,12 @@ describe('source:', () => {
210
210
  expect('source: c is a extend {view: q is { group_by: astr } }').toTranslate();
211
211
  });
212
212
  test('turtle in source can be called query with m4 warning', () => {
213
- expect(`##! m4warnings
213
+ expect(`##! m4warnings=warn
214
214
  source: c is a extend {query: q is { group_by: astr } }
215
215
  `).toTranslateWithWarnings('Use view: inside of a source instead of query:');
216
216
  });
217
217
  test('turtle in source m4 warning on arrow', () => {
218
- expect(`##! m4warnings
218
+ expect(`##! m4warnings=warn
219
219
  source: c is a extend {view: q is -> { group_by: astr } }
220
220
  `).toTranslateWithWarnings("Leading '->' in a view or nest definition is no longer needed.");
221
221
  });
@@ -228,7 +228,7 @@ describe('source:', () => {
228
228
  });
229
229
  test('refined explore-query m4warning', () => {
230
230
  expect(`
231
- ##! m4warnings
231
+ ##! m4warnings=warn
232
232
  source: abNew is ab extend {
233
233
  view: for1 is aturtle + {? ai = 1 }
234
234
  }
@@ -94,7 +94,7 @@ describe('sql:', () => {
94
94
  const model = new test_translator_1.TestTranslator(`
95
95
  ##! -m4warnings
96
96
  sql: users IS { select: """${selStmt}""" }
97
- source: malloyUsers is from_sql(users) { primary_key: ai }
97
+ source: malloyUsers is from_sql(users) extend { primary_key: ai }
98
98
  `);
99
99
  expect(model).toParse();
100
100
  const needReq = model.translate();
@@ -222,7 +222,7 @@ describe('sql:', () => {
222
222
  const model = new test_translator_1.TestTranslator(`
223
223
  ##! -m4warnings
224
224
  sql: sql_block IS { select: """${selStmt}""" }
225
- source: malloy_source is from_sql(sql_block) { primary_key: ai }
225
+ source: malloy_source is from_sql(sql_block) extend { primary_key: ai }
226
226
  `);
227
227
  const sql = (0, sql_block_1.makeSQLBlock)([{ sql: selStmt }]);
228
228
  model.update({ compileSQL: { [sql.name]: makeSchemaResponse(sql) } });
package/dist/malloy.js CHANGED
@@ -1835,7 +1835,7 @@ class ModelMaterializer extends FluentState {
1835
1835
  indexQuery = 'search_index';
1836
1836
  }
1837
1837
  const searchMapMalloy = `
1838
- query: ${sourceName}
1838
+ run: ${sourceName}
1839
1839
  -> ${indexQuery}
1840
1840
  -> {
1841
1841
  where: fieldType = 'string'
@@ -1589,7 +1589,7 @@ class QueryQuery extends QueryField {
1589
1589
  const { as, field } = this.expandField(f);
1590
1590
  if (field instanceof QueryTurtle || field instanceof QueryQuery) {
1591
1591
  if (this.firstSegment.type === 'project') {
1592
- throw new Error(`Turtled Queries cannot be used in PROJECT - '${field.fieldDef.name}'`);
1592
+ throw new Error(`Nested views cannot be used in select - '${field.fieldDef.name}'`);
1593
1593
  }
1594
1594
  const fir = new FieldInstanceResult(field.fieldDef, resultStruct);
1595
1595
  this.expandFields(fir);
@@ -1603,7 +1603,7 @@ class QueryQuery extends QueryField {
1603
1603
  this.addDependancies(resultStruct, field);
1604
1604
  if (isAggregateField(field)) {
1605
1605
  if (this.firstSegment.type === 'project') {
1606
- throw new Error(`Aggregate Fields cannot be used in PROJECT - '${field.fieldDef.name}'`);
1606
+ throw new Error(`Aggregate Fields cannot be used in select - '${field.fieldDef.name}'`);
1607
1607
  }
1608
1608
  }
1609
1609
  // } else if (field instanceof QueryStruct) {
package/dist/tags.js CHANGED
@@ -272,20 +272,27 @@ function getBuildOn(ctx) {
272
272
  }
273
273
  return new Tag();
274
274
  }
275
- function parsePath(buildOn, path) {
276
- let writeInto = buildOn.getProperties();
275
+ /**
276
+ * When chasing a path reference, the two interesting gestures are to
277
+ * find the path-ed tag so it can be extended, or to find the path tag
278
+ * so it can be deleted. This returns the parent and the final tag
279
+ * so that the caller can delete the tag with delete parent.tagName
280
+ * or assign to it with parent[tagName] = new_value
281
+ */
282
+ function pathToAccess(buildOn, path) {
283
+ let parentPropertyObject = buildOn.getProperties();
277
284
  for (const p of path.slice(0, path.length - 1)) {
278
285
  let next;
279
- if (writeInto[p] === undefined) {
286
+ if (parentPropertyObject[p] === undefined) {
280
287
  next = new Tag({});
281
- writeInto[p] = next;
288
+ parentPropertyObject[p] = next;
282
289
  }
283
290
  else {
284
- next = Tag.tagFrom(writeInto[p]);
291
+ next = Tag.tagFrom(parentPropertyObject[p]);
285
292
  }
286
- writeInto = next.getProperties();
293
+ parentPropertyObject = next.getProperties();
287
294
  }
288
- return [path[path.length - 1], writeInto];
295
+ return [path[path.length - 1], parentPropertyObject];
289
296
  }
290
297
  function getString(ctx) {
291
298
  if (ctx.SQ_STRING() || ctx.DQ_STRING()) {
@@ -399,7 +406,7 @@ class TaglineParser extends tree_1.AbstractParseTreeVisitor {
399
406
  visitTagEq(ctx) {
400
407
  const buildOn = getBuildOn(ctx);
401
408
  const name = this.getPropName(ctx.propName());
402
- const [writeKey, writeInto] = parsePath(buildOn, name);
409
+ const [writeKey, writeInto] = pathToAccess(buildOn, name);
403
410
  const eq = this.visit(ctx.eqValue());
404
411
  const propCx = ctx.properties();
405
412
  if (propCx) {
@@ -422,7 +429,7 @@ class TaglineParser extends tree_1.AbstractParseTreeVisitor {
422
429
  visitTagReplaceProperties(ctx) {
423
430
  const buildOn = getBuildOn(ctx);
424
431
  const name = this.getPropName(ctx.propName());
425
- const [writeKey, writeInto] = parsePath(buildOn, name);
432
+ const [writeKey, writeInto] = pathToAccess(buildOn, name);
426
433
  const propCx = ctx.properties();
427
434
  const props = this.visitProperties(propCx);
428
435
  if (ctx.DOTTY() === undefined) {
@@ -438,11 +445,11 @@ class TaglineParser extends tree_1.AbstractParseTreeVisitor {
438
445
  visitTagUpdateProperties(ctx) {
439
446
  const buildOn = getBuildOn(ctx);
440
447
  const name = this.getPropName(ctx.propName());
441
- const [writeKey, writeInto] = parsePath(buildOn, name);
448
+ const [writeKey, writeInto] = pathToAccess(buildOn, name);
442
449
  const propCx = ctx.properties();
443
450
  propCx['buildOn'] = Tag.tagFrom(writeInto[writeKey]);
444
451
  const props = this.visitProperties(propCx);
445
- const thisObj = writeInto[writeKey] || {};
452
+ const thisObj = writeInto[writeKey] || new Tag({});
446
453
  const properties = { ...thisObj.properties, ...props.dict };
447
454
  writeInto[writeKey] = { ...thisObj, properties };
448
455
  return buildOn;
@@ -450,12 +457,12 @@ class TaglineParser extends tree_1.AbstractParseTreeVisitor {
450
457
  visitTagDef(ctx) {
451
458
  const buildOn = getBuildOn(ctx);
452
459
  const path = this.getPropName(ctx.propName());
453
- const [writeKey, writeInto] = parsePath(buildOn, path);
460
+ const [writeKey, writeInto] = pathToAccess(buildOn, path);
454
461
  if (ctx.MINUS()) {
455
462
  delete writeInto[writeKey];
456
463
  }
457
464
  else {
458
- writeInto[writeKey] = {};
465
+ writeInto[writeKey] = new Tag({});
459
466
  }
460
467
  return buildOn;
461
468
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.94",
3
+ "version": "0.0.95-dev231024220124",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",