@malloydata/malloy 0.0.247-dev250321153536 → 0.0.247-dev250321211927

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.
@@ -15,7 +15,6 @@ class ExprFilterExpression extends expression_def_1.ExpressionDef {
15
15
  super();
16
16
  this.filterText = filterText;
17
17
  this.elementType = 'filter expression literal';
18
- // mtoy todo parse the filter and reflect errors into the error stream
19
18
  }
20
19
  getExpression() {
21
20
  return this.loggedErrorExpr('filter-expression-type', 'Filter expression illegal here');
@@ -49,9 +48,14 @@ class ExprFilterExpression extends expression_def_1.ExpressionDef {
49
48
  return left.loggedErrorExpr('filter-expression-type', `Cannot use filter expressions with type '${matchExpr.type}'`);
50
49
  }
51
50
  if (fParse.log.length > 0) {
52
- for (const _err of fParse.log) {
53
- // mtoy todo actuall get error and report correct position and error type
54
- return this.loggedErrorExpr('filter-expression-type', `Filter parse error: ${fParse.log[0].message}`);
51
+ for (const err of fParse.log) {
52
+ // Current parser only ever returns one error, it doesn't recover
53
+ // Theoretically possible to user the startIndex to point to the
54
+ // error inside the filter string, but I don't know if this is an
55
+ // f' string or an f''' string, and I think the startOffset is from
56
+ // the first non blank space in the filter (it was at one point)
57
+ // so for now, we just flag the entire filter source token.
58
+ return this.loggedErrorExpr('filter-expression-error', `Filter parse error: ${err.message}`);
55
59
  }
56
60
  }
57
61
  if (!fParse.parsed) {
@@ -364,6 +364,7 @@ type MessageParameterTypes = {
364
364
  'wildcard-include-rename': string;
365
365
  'literal-string-newline': string;
366
366
  'filter-expression-type': string;
367
+ 'filter-expression-error': string;
367
368
  'invalid-malloy-query-document': string;
368
369
  };
369
370
  export declare const MESSAGE_FORMATTERS: PartialErrorCodeMessageMap;
@@ -5,5 +5,5 @@ export declare const FilterCompilers: {
5
5
  numberCompile(nc: NumberFilter, x: string, d: Dialect): string;
6
6
  booleanCompile(bc: BooleanFilter, x: string, _d: Dialect): string;
7
7
  stringCompile(sc: StringFilter, x: string, d: Dialect): string;
8
- temporalCompile(tc: TemporalFilter, x: string, d: Dialect): string;
8
+ temporalCompile(tc: TemporalFilter, x: string, d: Dialect, t: 'date' | 'timestamp'): string;
9
9
  };
@@ -47,7 +47,7 @@ exports.FilterCompilers = {
47
47
  return exports.FilterCompilers.booleanCompile(c, x, d);
48
48
  }
49
49
  else if ((t === 'date' || t === 'timestamp') && (0, malloy_filter_1.isTemporalFilter)(c)) {
50
- return exports.FilterCompilers.temporalCompile(c, x, d);
50
+ return exports.FilterCompilers.temporalCompile(c, x, d, t);
51
51
  }
52
52
  throw new Error('INTERNAL ERROR: No filter compiler for ' + t);
53
53
  },
@@ -167,9 +167,9 @@ exports.FilterCompilers = {
167
167
  case ',': {
168
168
  /*
169
169
  * Basic formula over all members
170
- * ALL INCLUDED THINGS OR TOGETHER AND ALL EXCLUDED THINGS ANDED TOGETHER
171
- *
172
- * mtoy todo write some tests to see if AND clauses are includes or excludes
170
+ * (ALL INCLUDED THINGS OR TOGETHER)
171
+ * AND
172
+ * (ALL EXCLUDED THINGS ANDED TOGETHER)
173
173
  */
174
174
  const includes = [];
175
175
  const excludes = [];
@@ -243,8 +243,8 @@ exports.FilterCompilers = {
243
243
  }
244
244
  },
245
245
  // mtoy todo figure out what to do about dates
246
- temporalCompile(tc, x, d) {
247
- const c = new filter_temporal_compiler_1.TemporalFilterCompiler(x, d);
246
+ temporalCompile(tc, x, d, t) {
247
+ const c = new filter_temporal_compiler_1.TemporalFilterCompiler(x, d, t);
248
248
  return c.compile(tc);
249
249
  },
250
250
  };
@@ -11,6 +11,7 @@ export declare class TemporalFilterCompiler {
11
11
  readonly timetype: 'timestamp' | 'date';
12
12
  readonly d: Dialect;
13
13
  constructor(expr: string, dialect: Dialect, timetype?: 'timestamp' | 'date');
14
+ time(timeSQL: string): string;
14
15
  compile(tc: TemporalFilter): string;
15
16
  private expandLiteral;
16
17
  private literalNode;
@@ -27,16 +27,37 @@ class TemporalFilterCompiler {
27
27
  this.timetype = timetype;
28
28
  this.d = dialect;
29
29
  }
30
+ time(timeSQL) {
31
+ if (this.timetype === 'timestamp') {
32
+ return timeSQL;
33
+ }
34
+ return this.d.sqlCast({}, {
35
+ node: 'cast',
36
+ e: {
37
+ node: 'genericSQLExpr',
38
+ src: ['', timeSQL],
39
+ kids: { args: [] },
40
+ sql: timeSQL,
41
+ },
42
+ srcType: { type: 'timestamp' },
43
+ dstType: { type: 'date' },
44
+ safe: false,
45
+ });
46
+ }
30
47
  compile(tc) {
31
48
  const x = this.expr;
32
49
  switch (tc.operator) {
33
50
  case 'after':
34
- return `${x} ${tc.not ? '<' : '>='} ${this.moment(tc.after).end}`;
51
+ return `${x} ${tc.not ? '<' : '>='} ${this.time(this.moment(tc.after).end)}`;
35
52
  case 'before':
36
- return `${x} ${tc.not ? '>=' : '<'} ${this.moment(tc.before).begin}`;
53
+ return `${x} ${tc.not ? '>=' : '<'} ${this.time(this.moment(tc.before).begin.sql)}`;
37
54
  case 'in': {
38
- // mtoy todo in now
39
55
  const m = this.moment(tc.in);
56
+ if (m.begin.sql === m.end) {
57
+ return tc.not
58
+ ? `${x} != ${this.time(m.end)} OR ${x} IS NULL`
59
+ : `${x} = ${this.time(m.end)}`;
60
+ }
40
61
  return this.isIn(tc.not, m.begin.sql, m.end);
41
62
  }
42
63
  case 'for': {
@@ -57,7 +78,7 @@ class TemporalFilterCompiler {
57
78
  case 'to': {
58
79
  const firstMoment = this.moment(tc.fromMoment);
59
80
  const lastMoment = this.moment(tc.toMoment);
60
- return this.isIn(tc.not, firstMoment.begin.sql, lastMoment.end);
81
+ return this.isIn(tc.not, firstMoment.begin.sql, lastMoment.begin.sql);
61
82
  }
62
83
  case 'last': {
63
84
  const thisUnit = this.nowDot(tc.units);
@@ -221,7 +242,6 @@ class TemporalFilterCompiler {
221
242
  }
222
243
  moment(m) {
223
244
  switch (m.moment) {
224
- // mtoy todo moments which have no duration should have somethign in the interface?
225
245
  case 'now': {
226
246
  const now = this.nowExpr();
227
247
  return { begin: now, end: now.sql };
@@ -230,20 +250,14 @@ class TemporalFilterCompiler {
230
250
  return this.expandLiteral(m);
231
251
  case 'ago':
232
252
  case 'from_now': {
233
- // mtoy todo just pretending all units work, they don't
234
- const nowTruncExpr = {
235
- node: 'trunc',
236
- e: this.nowExpr(),
237
- units: m.units,
238
- };
239
- nowTruncExpr.sql = this.d.sqlTruncExpr({}, nowTruncExpr);
253
+ const nowTruncExpr = this.nowDot(m.units);
240
254
  const nowTrunc = (0, malloy_types_1.mkTemporal)(nowTruncExpr, 'timestamp');
241
255
  const beginExpr = this.delta(nowTrunc, m.moment === 'ago' ? '-' : '+', m.n, m.units);
242
256
  // Now the end is one unit after that .. either n-1 units ago or n+1 units from now
243
257
  if (m.moment === 'ago' && m.n === '1') {
244
258
  return { begin: beginExpr, end: nowTruncExpr.sql };
245
259
  }
246
- const oneDifferent = Number(m.n) + m.moment === 'ago' ? -1 : 1;
260
+ const oneDifferent = Number(m.n) + (m.moment === 'ago' ? -1 : 1);
247
261
  const endExpr = {
248
262
  ...beginExpr,
249
263
  kids: { base: nowTrunc, delta: this.n(oneDifferent.toString()) },
@@ -318,6 +332,8 @@ class TemporalFilterCompiler {
318
332
  begOp = '<';
319
333
  endOp = '>=';
320
334
  }
335
+ begin = this.time(begin);
336
+ end = this.time(end);
321
337
  return `${this.expr} ${begOp} ${begin} ${joinOp} ${this.expr} ${endOp} ${end}`;
322
338
  }
323
339
  }
@@ -893,8 +893,7 @@ class QueryField extends QueryNode {
893
893
  return filter_compilers_1.FilterCompilers.compile(expr.dataType, expr.filter, expr.e.sql || '', this.parent.dialect);
894
894
  }
895
895
  }
896
- // mtoy todo no throw
897
- throw new Error(`Internal Error: Filter Compiler Undefined (FCU) ${expr.dataType}`);
896
+ throw new Error(`Internal Error: Filter Compiler Undefined Type '${expr.dataType}'`);
898
897
  default:
899
898
  throw new Error(`Internal Error: Unknown expression node '${expr.node}' ${JSON.stringify(expr, undefined, 2)}`);
900
899
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.247-dev250321153536",
3
+ "version": "0.0.247-dev250321211927",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -41,9 +41,9 @@
41
41
  "generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
42
42
  },
43
43
  "dependencies": {
44
- "@malloydata/malloy-filter": "^0.0.247-dev250321153536",
45
- "@malloydata/malloy-interfaces": "^0.0.247-dev250321153536",
46
- "@malloydata/malloy-tag": "^0.0.247-dev250321153536",
44
+ "@malloydata/malloy-filter": "^0.0.247-dev250321211927",
45
+ "@malloydata/malloy-interfaces": "^0.0.247-dev250321211927",
46
+ "@malloydata/malloy-tag": "^0.0.247-dev250321211927",
47
47
  "antlr4ts": "^0.5.0-alpha.4",
48
48
  "assert": "^2.0.0",
49
49
  "jest-diff": "^29.6.2",