@malloydata/malloy 0.0.56-dev230710182711 → 0.0.56-dev230710205111

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.
@@ -52,9 +52,10 @@ describe('model statements', () => {
52
52
  });
53
53
  expect(m).translationToFailWith('a1 is not a connection');
54
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')`");
55
+ test('table function is deprecated', () => {
56
+ expect(`##! m4warnings
57
+ source: testA is table('conn:aTable')
58
+ `).toTranslateWithWarnings("`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
58
59
  });
59
60
  });
60
61
  describe('source:', () => {
@@ -88,13 +89,12 @@ describe('model statements', () => {
88
89
  });
89
90
  });
90
91
  describe('query:', () => {
91
- // Delete this when ENABLE_M4_WARNINGS is converted to an annotation
92
92
  test('anonymous query', () => {
93
93
  expect((0, test_translator_1.markSource) `query: ${"table('aTable') -> { group_by: astr }"}`).toTranslate();
94
94
  });
95
- // Unskip this when ENABLE_M4_WARNINGS is converted to an annotation
96
- test.skip('anonymous query', () => {
97
- expect((0, test_translator_1.markSource) `query: ${"table('aTable') -> { group_by: astr }"}`).toTranslateWithWarnings('Anonymous `query:` statements are deprecated, use `run:` instead');
95
+ test('anonymous query', () => {
96
+ expect((0, test_translator_1.markSource) `##! m4warnings
97
+ query: ${"conn.table('aTable') -> { group_by: astr }"}`).toTranslateWithWarnings('Anonymous `query:` statements are deprecated, use `run:` instead');
98
98
  });
99
99
  test('run query', () => expect("run: table('aTable') -> { group_by: astr }").toTranslate());
100
100
  test('run query ref', () => expect(`
@@ -1018,16 +1018,18 @@ describe('qops', () => {
1018
1018
  expect('query: a -> { extend: { dimension: x is 1; dimension: y is 2; measure: c is count() }; group_by: x, y; aggregate: c }').toTranslate();
1019
1019
  });
1020
1020
  });
1021
- // TODO ENABLE_M4_WARNINGS: unskip when we have an M4 warning flag
1022
- describe.skip('declare/query join warnings', () => {
1021
+ describe('declare/query join warnings', () => {
1023
1022
  test('declare warning in query', () => {
1024
- expect((0, test_translator_1.markSource) `query: a -> { declare: ${'x is 1'}; group_by: x, y }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
1023
+ expect((0, test_translator_1.markSource) `##! m4warnings
1024
+ run: a -> { ${'declare: x is 1'}; group_by: x }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
1025
1025
  });
1026
1026
  test('declare warning in source', () => {
1027
- expect((0, test_translator_1.markSource) `source: a2 is a { declare: ${'x is 1'} }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
1027
+ expect((0, test_translator_1.markSource) `##! m4warnings
1028
+ source: a2 is a extend { ${'declare: x is 1'} }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
1028
1029
  });
1029
1030
  test('joins in query', () => {
1030
- expect((0, test_translator_1.markSource) `query: 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.');
1031
+ expect((0, test_translator_1.markSource) `##! m4warnings
1032
+ 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.');
1031
1033
  });
1032
1034
  });
1033
1035
  test('refine query with extended source', () => {
@@ -1593,18 +1595,18 @@ describe('error handling', () => {
1593
1595
  `).translationToFailWith("Cannot redefine 'airports'");
1594
1596
  });
1595
1597
  test('query from undefined source', () => {
1596
- expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).translationToFailWith("Undefined source 'x'");
1598
+ expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).translationToFailWith("Undefined query or source 'x'");
1597
1599
  });
1598
1600
  test('query with expression from undefined source', () => {
1599
1601
  // Regression check: Once upon a time this died with an exception even
1600
1602
  // when "query: x->{ group_by: y}" (above) generated the correct error.
1601
- expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).translationToFailWith("Undefined source 'x'");
1603
+ expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).translationToFailWith("Undefined query or source 'x'");
1602
1604
  });
1603
1605
  test('join reference before definition', () => {
1604
1606
  expect((0, test_translator_1.markSource) `
1605
1607
  source: newAB is a { join_one: newB is ${'bb'} on astring }
1606
1608
  source: newB is b
1607
- `).translationToFailWith("Undefined source 'bb'");
1609
+ `).translationToFailWith("Undefined query or source 'bb'");
1608
1610
  });
1609
1611
  test('non-rename rename', () => {
1610
1612
  expect('source: na is a { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
@@ -2895,9 +2897,9 @@ describe('sql expressions', () => {
2895
2897
  expect(m).toTranslate();
2896
2898
  }
2897
2899
  });
2898
- // TODO unskip when ENABLE_M4_WARNINGS is turned into an annotation
2899
- test.skip('sql statement deprecation warning', () => {
2900
- const m = new test_translator_1.TestTranslator('sql: bad_sql is {select: """SELECT 1 as one"""}');
2900
+ test('sql statement deprecation warning', () => {
2901
+ const m = new test_translator_1.TestTranslator(`##! m4warnings
2902
+ sql: bad_sql is {select: """SELECT 1 as one"""}`);
2901
2903
  const req = m.translate().compileSQL;
2902
2904
  if (req) {
2903
2905
  m.update({
@@ -2906,5 +2908,252 @@ describe('sql expressions', () => {
2906
2908
  }
2907
2909
  expect(m).toTranslateWithWarnings('`sql:` statement is deprecated, use `connection_name.sql(...)` instead');
2908
2910
  });
2911
+ test('reference to sql expression in run', () => {
2912
+ const m = new test_translator_1.TestTranslator(`
2913
+ run: bigquery.sql("""select 1 as one""")
2914
+ `);
2915
+ expect(m).toParse();
2916
+ const compileSql = m.translate().compileSQL;
2917
+ expect(compileSql).toBeDefined();
2918
+ if (compileSql) {
2919
+ m.update({
2920
+ compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
2921
+ });
2922
+ expect(m).toTranslate();
2923
+ }
2924
+ });
2925
+ test('reference to sql expression in query def', () => {
2926
+ const m = new test_translator_1.TestTranslator(`
2927
+ query: q is bigquery.sql("""select 1 as one""")
2928
+ `);
2929
+ expect(m).toParse();
2930
+ const compileSql = m.translate().compileSQL;
2931
+ expect(compileSql).toBeDefined();
2932
+ if (compileSql) {
2933
+ m.update({
2934
+ compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
2935
+ });
2936
+ expect(m).toTranslate();
2937
+ }
2938
+ });
2939
+ test('reference to sql expression in anonymous query', () => {
2940
+ const m = new test_translator_1.TestTranslator(`
2941
+ query: bigquery.sql("""select 1 as one""")
2942
+ `);
2943
+ expect(m).toParse();
2944
+ const compileSql = m.translate().compileSQL;
2945
+ expect(compileSql).toBeDefined();
2946
+ if (compileSql) {
2947
+ m.update({
2948
+ compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
2949
+ });
2950
+ expect(m).toTranslate();
2951
+ }
2952
+ });
2953
+ // TODO this is not possible to implement yet unless we
2954
+ // can distinguish between the generated IR of `conn.sql(...)`
2955
+ // and `conn.sql(...) -> { project: * }`.
2956
+ test.skip('cannot refine a SQL query', () => {
2957
+ const m = new test_translator_1.TestTranslator(`
2958
+ query: q1 is bigquery.sql("""select 1 as one""")
2959
+ run: q1 refine { where: 1 = 1 }
2960
+ `);
2961
+ expect(m).toParse();
2962
+ const compileSql = m.translate().compileSQL;
2963
+ expect(compileSql).toBeDefined();
2964
+ if (compileSql) {
2965
+ m.update({
2966
+ compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
2967
+ });
2968
+ expect(m).translationToFailWith('Cannot refine a SQL query');
2969
+ }
2970
+ });
2971
+ });
2972
+ describe('extend and refine', () => {
2973
+ describe('extend and refine, new syntax', () => {
2974
+ test('query name with query refinements', () => {
2975
+ expect(`
2976
+ query: q is a -> { group_by: ai }
2977
+ run: q refine { group_by: ai2 is ai }
2978
+ `).toTranslate();
2979
+ });
2980
+ test('query name with source refinements', () => {
2981
+ expect(`
2982
+ query: q is a -> { group_by: ai }
2983
+ source: s is q extend { dimension: ai_2 is ai + ai }
2984
+ `).toTranslate();
2985
+ });
2986
+ test('source name with query refinements', () => {
2987
+ expect('run: a refine { group_by: ai }').translationToFailWith("Illegal reference to 'a', query expected");
2988
+ });
2989
+ test('source name with source refinements', () => {
2990
+ expect('source: s is a extend { dimension: ai_2 is ai + ai }').toTranslate();
2991
+ });
2992
+ test('query with source refinements', () => {
2993
+ expect('source: s is a -> { group_by: ai } extend { dimension: ai_2 is ai + ai }').toTranslate();
2994
+ });
2995
+ test('query with extension then new stage', () => {
2996
+ expect('run: a { dimension: x is 1 } -> { group_by: x }').toTranslate();
2997
+ });
2998
+ });
2999
+ describe('extend and refine, old syntax', () => {
3000
+ test('query name with query refinements', () => {
3001
+ expect(`
3002
+ query: q is a -> { group_by: ai }
3003
+ run: q { group_by: ai2 is ai }
3004
+ `).toTranslate();
3005
+ });
3006
+ test('query name with source refinements', () => {
3007
+ expect(`
3008
+ query: q is a -> { group_by: ai }
3009
+ source: s is q { dimension: ai_2 is ai + ai }
3010
+ `).toTranslate();
3011
+ });
3012
+ test('source name with query refinements', () => {
3013
+ expect('run: a { group_by: one }').translationToFailWith("Illegal reference to 'a', query expected");
3014
+ });
3015
+ test('source name with source refinements', () => {
3016
+ expect('source: s is a { dimension: ai_2 is ai + ai }').toTranslate();
3017
+ });
3018
+ test('source name with ambiguous refinements', () => {
3019
+ // Ambiguous refinements are assumed to be source extensions
3020
+ expect('run: a { join_one: b on b.ai = ai } -> { project: b.* }').toTranslate();
3021
+ expect('run: a { where: 1 = 1 } -> { project: * }').toTranslate();
3022
+ expect('run: a { declare: three is 3 } -> { project: * }').toTranslate();
3023
+ expect('source: s is a { join_one: b on b.ai = ai }').toTranslate();
3024
+ expect('source: s is a { where: 1 = 1 }').toTranslate();
3025
+ expect('source: s is a { declare: three is 3 }').toTranslate();
3026
+ });
3027
+ test('query name with ambiguous refinements', () => {
3028
+ // Here we implicitly convert the query into a source.
3029
+ expect(`
3030
+ query: q is a -> { group_by: ai }
3031
+ run: q { join_one: b on 1 = 1 } -> { project: b.* }
3032
+ `).toTranslate();
3033
+ expect(`
3034
+ query: q is a -> { group_by: ai }
3035
+ run: q { where: 1 = 1 } -> { project: * }
3036
+ `).toTranslate();
3037
+ expect(`
3038
+ query: q is a -> { group_by: ai }
3039
+ run: q { declare: three is 3 } -> { project: * }
3040
+ `).toTranslate();
3041
+ // Without the new stage we automatically recognize this as a query refinement
3042
+ expect(`
3043
+ query: q is a -> { group_by: ai }
3044
+ run: q { join_one: b on 1 = 1 }
3045
+ `).toTranslate();
3046
+ expect(`
3047
+ query: q is a -> { group_by: ai }
3048
+ run: q { where: 1 = 1 }
3049
+ `).toTranslate();
3050
+ expect(`
3051
+ query: q is a -> { group_by: ai }
3052
+ run: q { declare: three is 3 }
3053
+ `).toTranslate();
3054
+ // Can also just add from() to use as a source
3055
+ // TODO add a warning when you use from() -- "`from()` is deprecated; to apply source extensions to a query, use `extend`"
3056
+ expect(`
3057
+ query: q is a -> { group_by: ai }
3058
+ source: s is from(q) { join_one: b on b.ai = ai }
3059
+ `).toTranslate();
3060
+ // Can also add an arrow to clarify that it's a query
3061
+ // Of course, number 1 and 3 are actually useless because you're defining
3062
+ // something and then not using it...
3063
+ expect(`
3064
+ query: q is a -> { group_by: ai }
3065
+ run: -> q { join_one: b on b.ai = ai } -> { project: * }
3066
+ `).toTranslate();
3067
+ expect(`
3068
+ query: q is a -> { group_by: ai }
3069
+ run: -> q { where: 1 = 1 } -> { project: * }
3070
+ `).toTranslate();
3071
+ expect(`
3072
+ query: q is a -> { group_by: ai }
3073
+ run: -> q { declare: three is 3 } -> { project: * }
3074
+ `).toTranslate();
3075
+ // Alternatively, fix 1 and 3 by actually using the declared thing
3076
+ expect(`
3077
+ query: q is a -> { group_by: ai }
3078
+ run: q { join_one: b on b.ai = ai; group_by: ai2 is b.ai }
3079
+ `).toTranslate();
3080
+ expect(`
3081
+ query: q is a -> { group_by: ai }
3082
+ run: q { declare: three is 3; group_by: three }
3083
+ `).toTranslate();
3084
+ });
3085
+ });
3086
+ describe('extend and refine fallout', () => {
3087
+ test('syntactically valid to run a source, but still illegal', () => {
3088
+ expect('run: a').translationToFailWith("Illegal reference to 'a', query expected");
3089
+ });
3090
+ test('syntactically valid to refine a source, but illegal', () => {
3091
+ expect('run: a { group_by: ai } ').translationToFailWith("Illegal reference to 'a', query expected");
3092
+ expect('run: a refine { group_by: ai } ').translationToFailWith("Illegal reference to 'a', query expected");
3093
+ });
3094
+ });
3095
+ describe('turtles', () => {
3096
+ test('explicit refine in turtle works', () => {
3097
+ expect(`source: c is a extend {
3098
+ query: x is { project: * }
3099
+ query: y is x refine { limit: 1 }
3100
+ }`).toTranslate();
3101
+ });
3102
+ test('implicit refine in turtle works', () => {
3103
+ expect(`source: c is a extend {
3104
+ query: x is { project: * }
3105
+ query: y is x { limit: 1 }
3106
+ }`).toTranslate();
3107
+ });
3108
+ });
3109
+ describe('nests', () => {
3110
+ test('explicit refine in nest', () => {
3111
+ expect(`source: c is a extend {
3112
+ query: x is { project: * }
3113
+ }
3114
+
3115
+ run: c -> {
3116
+ nest: x refine { limit: 1 }
3117
+ }`).toTranslate();
3118
+ });
3119
+ test('implicit refine in nest', () => {
3120
+ expect(`source: c is a extend {
3121
+ query: x is { project: * }
3122
+ }
3123
+
3124
+ run: c -> {
3125
+ nest: x { limit: 1 }
3126
+ }`).toTranslate();
3127
+ });
3128
+ });
3129
+ describe('m4 warnings', () => {
3130
+ test('implicit refine in nest', () => {
3131
+ expect(`##! m4warnings
3132
+ source: c is a extend {
3133
+ query: x is { project: * }
3134
+ }
3135
+
3136
+ run: c -> {
3137
+ nest: x { limit: 1 }
3138
+ }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `refine` operator.');
3139
+ });
3140
+ test('implicit refine in turtle works', () => {
3141
+ expect(`##! m4warnings
3142
+ source: c is a extend {
3143
+ query: x is { project: * }
3144
+ query: y is x { limit: 1 }
3145
+ }`).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `refine` operator.');
3146
+ });
3147
+ test('implicit query refinement', () => {
3148
+ expect(`##! m4warnings
3149
+ query: q is a -> { group_by: ai }
3150
+ run: q { group_by: three is 3 }
3151
+ `).toTranslateWithWarnings('Implicit query refinement is deprecated, use the `refine` operator.');
3152
+ });
3153
+ test('implicit source extension', () => {
3154
+ expect(`##! m4warnings
3155
+ source: s is a { dimension: ai_2 is ai + ai }`).toTranslateWithWarnings('Implicit source extension is deprecated, use the `extend` operator.');
3156
+ });
3157
+ });
2909
3158
  });
2910
3159
  //# sourceMappingURL=parse.spec.js.map
package/dist/tags.d.ts CHANGED
@@ -18,4 +18,5 @@ export declare class Tags {
18
18
  getMalloyTags(): MalloyTags;
19
19
  getTagList(): string[];
20
20
  }
21
+ export declare function parseTagProperties(src: string, tagProp: MalloyTagProperties): MalloyTagProperties | undefined;
21
22
  export {};
package/dist/tags.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.Tags = void 0;
25
+ exports.parseTagProperties = exports.Tags = void 0;
26
26
  function isDocTag(a) {
27
27
  return (a === null || a === void 0 ? void 0 : a.docString) !== undefined;
28
28
  }
@@ -79,6 +79,12 @@ function parseTag(src, tagProp) {
79
79
  if (!propMatch) {
80
80
  return;
81
81
  }
82
+ const newProps = parseTagProperties(src.slice(propMatch[0].length), tagProp);
83
+ if (newProps) {
84
+ return { properties: newProps };
85
+ }
86
+ }
87
+ function parseTagProperties(src, tagProp) {
82
88
  /*
83
89
  * I went back and forth if the grammar for these annotations should be
84
90
  * in the parser or the lexer. Eventually the fact that the lexer ate
@@ -86,7 +92,7 @@ function parseTag(src, tagProp) {
86
92
  *
87
93
  * Seems wrong to be writing a parser though.
88
94
  */
89
- const tokens = tokenize(src.slice(propMatch[0].length));
95
+ const tokens = tokenize(src);
90
96
  let tn = 0;
91
97
  const lastToken = tokens.length - 1;
92
98
  while (tn <= lastToken) {
@@ -119,8 +125,9 @@ function parseTag(src, tagProp) {
119
125
  }
120
126
  tn += 1;
121
127
  }
122
- return { properties: tagProp };
128
+ return tagProp;
123
129
  }
130
+ exports.parseTagProperties = parseTagProperties;
124
131
  function tokenize(src) {
125
132
  const parts = [];
126
133
  src = src.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.56-dev230710182711",
3
+ "version": "0.0.56-dev230710205111",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",