@malloydata/malloy 0.0.38-dev230602001052 → 0.0.38-dev230606045241

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.
@@ -364,13 +364,13 @@ ${(0, utils_1.indent)(sql)}
364
364
  }
365
365
  sqlMeasureTime(from, to, units) {
366
366
  const measureMap = {
367
- microsecond: { use: 'microsecond', ratio: 1 },
368
- millisecond: { use: 'microsecond', ratio: 1000 },
369
- second: { use: 'millisecond', ratio: 1000 },
370
- minute: { use: 'second', ratio: 60 },
371
- hour: { use: 'minute', ratio: 60 },
372
- day: { use: 'hour', ratio: 24 },
373
- week: { use: 'day', ratio: 7 },
367
+ 'microsecond': { use: 'microsecond', ratio: 1 },
368
+ 'millisecond': { use: 'microsecond', ratio: 1000 },
369
+ 'second': { use: 'millisecond', ratio: 1000 },
370
+ 'minute': { use: 'second', ratio: 60 },
371
+ 'hour': { use: 'minute', ratio: 60 },
372
+ 'day': { use: 'hour', ratio: 24 },
373
+ 'week': { use: 'day', ratio: 7 },
374
374
  };
375
375
  let lVal = from.value;
376
376
  let rVal = to.value;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { QueryDataRow, Fragment, StructDef, StructRelationship, NamedStructDefs, MalloyQueryData, AtomicFieldType as AtomicFieldTypeInner, DateUnit, ExtractUnit, TimestampUnit, TimeFieldType, QueryData, FieldTypeDef, Expr, DialectFragment, TimeValue, FilterExpression, SQLBlock, FieldDef, FilteredAliasedName, PipeSegment, QueryFieldDef, TurtleDef, SearchValueMapResult, SearchIndexResult, ModelDef, Query, NamedQuery, NamedModelObject, ExpressionType, DocumentLocation, DocumentRange, DocumentPosition, } from './model';
1
+ export type { QueryDataRow, Fragment, StructDef, StructRelationship, NamedStructDefs, MalloyQueryData, AtomicFieldType as AtomicFieldTypeInner, DateUnit, ExtractUnit, TimestampUnit, TimeFieldType, QueryData, FieldTypeDef, Expr, DialectFragment, TimeValue, FilterExpression, SQLBlock, FieldDef, FilteredAliasedName, PipeSegment, QueryFieldDef, TurtleDef, SearchValueMapResult, SearchIndexResult, ModelDef, Query, QueryRunStats, NamedQuery, NamedModelObject, ExpressionType, DocumentLocation, DocumentRange, DocumentPosition, } from './model';
2
2
  export { Segment, isFilteredAliasedName, flattenQuery, expressionIsCalculation, } from './model';
3
3
  export { HighlightType, MalloyTranslator, } from './lang';
4
4
  export type { LogMessage, TranslateResponse } from './lang';
@@ -29,6 +29,9 @@ const fragtype_utils_1 = require("../fragtype-utils");
29
29
  const expression_def_1 = require("../types/expression-def");
30
30
  const malloy_element_1 = require("../types/malloy-element");
31
31
  const utils_1 = require("./utils");
32
+ function typeCoalesce(ev1, ev2) {
33
+ return ev1 === undefined || ev1.dataType === 'null' ? ev2 : ev1;
34
+ }
32
35
  class Pick extends expression_def_1.ExpressionDef {
33
36
  constructor(choices, elsePick) {
34
37
  super({ choices });
@@ -65,21 +68,17 @@ class Pick extends expression_def_1.ExpressionDef {
65
68
  ? choice.pick.getExpression(fs)
66
69
  : expr.getExpression(fs);
67
70
  anyExpressionType = (0, malloy_types_1.maxExpressionType)(anyExpressionType, (0, malloy_types_1.maxExpressionType)(whenExpr.expressionType, thenExpr.expressionType));
68
- if (returnType) {
69
- if (!fragtype_utils_1.FT.typeEq(returnType, thenExpr, true)) {
70
- const whenType = fragtype_utils_1.FT.inspect(thenExpr);
71
- this.log(`pick type '${whenType}', expected '${returnType.dataType}'`);
72
- return (0, ast_utils_1.errorFor)('pick when type');
73
- }
74
- }
75
- else {
76
- returnType = thenExpr;
71
+ if (returnType && !fragtype_utils_1.FT.typeEq(returnType, thenExpr, true)) {
72
+ const whenType = fragtype_utils_1.FT.inspect(thenExpr);
73
+ this.log(`pick type '${whenType}', expected '${returnType.dataType}'`);
74
+ return (0, ast_utils_1.errorFor)('pick when type');
77
75
  }
76
+ returnType = typeCoalesce(returnType, thenExpr);
78
77
  caseValue.push(' WHEN ', ...whenExpr.value, ' THEN ', ...thenExpr.value);
79
78
  }
80
79
  const elsePart = this.elsePick || expr;
81
80
  const elseVal = elsePart.getExpression(fs);
82
- returnType || (returnType = elseVal);
81
+ returnType = typeCoalesce(returnType, elseVal);
83
82
  if (!fragtype_utils_1.FT.typeEq(returnType, elseVal, true)) {
84
83
  const errSrc = this.elsePick ? 'else' : 'pick default';
85
84
  this.log(`${errSrc} type '${fragtype_utils_1.FT.inspect(elseVal)}', expected '${returnType.dataType}'`);
@@ -112,24 +111,26 @@ class Pick extends expression_def_1.ExpressionDef {
112
111
  when: c.when.getExpression(fs),
113
112
  });
114
113
  }
115
- const returnType = choiceValues[0].pick;
114
+ let returnType;
116
115
  const caseValue = ['CASE'];
117
- let anyExpressionType = returnType.expressionType;
116
+ let anyExpressionType = 'scalar';
118
117
  for (const aChoice of choiceValues) {
119
118
  if (!fragtype_utils_1.FT.typeEq(aChoice.when, fragtype_utils_1.FT.boolT)) {
120
119
  this.log(`when expression must be boolean, not '${fragtype_utils_1.FT.inspect(aChoice.when)}`);
121
120
  return (0, ast_utils_1.errorFor)('pick when type');
122
121
  }
123
- if (!fragtype_utils_1.FT.typeEq(returnType, aChoice.pick, true)) {
122
+ if (returnType && !fragtype_utils_1.FT.typeEq(returnType, aChoice.pick, true)) {
124
123
  const whenType = fragtype_utils_1.FT.inspect(aChoice.pick);
125
124
  this.log(`pick type '${whenType}', expected '${returnType.dataType}'`);
126
125
  return (0, ast_utils_1.errorFor)('pick value type');
127
126
  }
127
+ returnType = typeCoalesce(returnType, aChoice.pick);
128
128
  anyExpressionType = (0, malloy_types_1.maxExpressionType)(anyExpressionType, (0, malloy_types_1.maxExpressionType)(aChoice.pick.expressionType, aChoice.when.expressionType));
129
129
  caseValue.push(' WHEN ', ...aChoice.when.value, ' THEN ', ...aChoice.pick.value);
130
130
  }
131
131
  const defVal = this.elsePick.getExpression(fs);
132
132
  anyExpressionType = (0, malloy_types_1.maxExpressionType)(anyExpressionType, defVal.expressionType);
133
+ returnType = typeCoalesce(returnType, defVal);
133
134
  if (!fragtype_utils_1.FT.typeEq(returnType, defVal, true)) {
134
135
  this.elsePick.log(`else type '${fragtype_utils_1.FT.inspect(defVal)}', expected '${returnType.dataType}'`);
135
136
  return (0, ast_utils_1.errorFor)('pick value type mismatch');
@@ -37,6 +37,14 @@ function exprOK(s) {
37
37
  return undefined;
38
38
  };
39
39
  }
40
+ function exprType(s, t) {
41
+ return () => {
42
+ const expr = new test_translator_1.BetaExpression(s);
43
+ expect(expr).modelParsed();
44
+ expect(expr.generated().dataType).toBe(t);
45
+ return undefined;
46
+ };
47
+ }
40
48
  function modelOK(s) {
41
49
  return () => {
42
50
  const m = new test_translator_1.TestTranslator(s);
@@ -720,6 +728,9 @@ describe('expressions', () => {
720
728
  test('filtering', exprOK(`
721
729
  astr ? pick 'missing value' when NULL
722
730
  `));
731
+ test('null branch with else', exprType("astr ? pick null when = '42' else 3", 'number'));
732
+ test('null branch no else', exprType("astr ? pick null when = '42'", 'string'));
733
+ test('null branch no apply', exprType('pick null when 1 = 1 else 3', 'number'));
723
734
  test('tiering', exprOK(`
724
735
  ai ?
725
736
  pick 1 when < 10
package/dist/malloy.d.ts CHANGED
@@ -121,6 +121,14 @@ export declare class Malloy {
121
121
  sqlStruct: SQLBlockStructDef;
122
122
  options?: RunSQLOptions;
123
123
  }): AsyncIterableIterator<DataRecord>;
124
+ static estimateQueryCost(params: {
125
+ connections: LookupConnection<Connection>;
126
+ preparedResult: PreparedResult;
127
+ }): Promise<QueryRunStats>;
128
+ static estimateQueryCost(params: {
129
+ connections: LookupConnection<Connection>;
130
+ sqlStruct: SQLBlockStructDef;
131
+ }): Promise<QueryRunStats>;
124
132
  }
125
133
  /**
126
134
  * A Malloy error, which may contain log messages produced during compilation.
@@ -930,6 +938,12 @@ export declare class QueryMaterializer extends FluentState<PreparedQuery> {
930
938
  * @return A promise of the `PreparedQuery`.
931
939
  */
932
940
  getPreparedQuery(): Promise<PreparedQuery>;
941
+ /**
942
+ * Estimates the cost of this loaded `Query`.
943
+ *
944
+ * @return The estimated cost of running this loaded query.
945
+ */
946
+ estimateQueryCost(): Promise<QueryRunStats>;
933
947
  }
934
948
  /**
935
949
  * An object representing the task of loading a `PreparedResult`, capable of
@@ -986,6 +1000,7 @@ export declare class SQLBlockMaterializer extends FluentState<SQLBlockStructDef>
986
1000
  * @return A promise to the SQL string.
987
1001
  */
988
1002
  getSQL(): Promise<string>;
1003
+ estimateQueryCost(): Promise<QueryRunStats>;
989
1004
  }
990
1005
  /**
991
1006
  * An object representing the task of loading an `Explore`, capable of
package/dist/malloy.js CHANGED
@@ -281,6 +281,23 @@ class Malloy {
281
281
  index += 1;
282
282
  }
283
283
  }
284
+ static async estimateQueryCost({ connections, preparedResult, sqlStruct, }) {
285
+ const sqlBlock = sqlStruct === null || sqlStruct === void 0 ? void 0 : sqlStruct.structSource.sqlBlock;
286
+ if (!connections) {
287
+ throw new Error('Internal Error: Connection or LookupConnection<Connection> must be provided.');
288
+ }
289
+ const connectionName = (sqlBlock === null || sqlBlock === void 0 ? void 0 : sqlBlock.connection) || (preparedResult === null || preparedResult === void 0 ? void 0 : preparedResult.connectionName);
290
+ const connection = await connections.lookupConnection(connectionName);
291
+ if (sqlBlock) {
292
+ return await connection.estimateQueryCost(sqlBlock === null || sqlBlock === void 0 ? void 0 : sqlBlock.selectStr);
293
+ }
294
+ else if (preparedResult) {
295
+ return await connection.estimateQueryCost(preparedResult === null || preparedResult === void 0 ? void 0 : preparedResult.sql);
296
+ }
297
+ else {
298
+ throw new Error('Internal error: sqlStruct or preparedResult must be provided.');
299
+ }
300
+ }
284
301
  }
285
302
  exports.Malloy = Malloy;
286
303
  /**
@@ -1878,6 +1895,16 @@ class QueryMaterializer extends FluentState {
1878
1895
  getPreparedQuery() {
1879
1896
  return this.materialize();
1880
1897
  }
1898
+ /**
1899
+ * Estimates the cost of this loaded `Query`.
1900
+ *
1901
+ * @return The estimated cost of running this loaded query.
1902
+ */
1903
+ async estimateQueryCost() {
1904
+ const connections = this.runtime.connections;
1905
+ const preparedResult = await this.getPreparedResult();
1906
+ return Malloy.estimateQueryCost({ connections, preparedResult });
1907
+ }
1881
1908
  }
1882
1909
  exports.QueryMaterializer = QueryMaterializer;
1883
1910
  /**
@@ -1967,6 +1994,11 @@ class SQLBlockMaterializer extends FluentState {
1967
1994
  const sqlStruct = await this.getSQLBlock();
1968
1995
  return sqlStruct.structSource.sqlBlock.selectStr;
1969
1996
  }
1997
+ async estimateQueryCost() {
1998
+ const connections = this.runtime.connections;
1999
+ const sqlStruct = await this.getSQLBlock();
2000
+ return Malloy.estimateQueryCost({ connections, sqlStruct });
2001
+ }
1970
2002
  }
1971
2003
  exports.SQLBlockMaterializer = SQLBlockMaterializer;
1972
2004
  /**
@@ -1,5 +1,5 @@
1
1
  import { RunSQLOptions } from './run_sql_options';
2
- import { MalloyQueryData, QueryDataRow, SQLBlock, StructDef } from './model/malloy_types';
2
+ import { MalloyQueryData, QueryDataRow, QueryRunStats, SQLBlock, StructDef } from './model/malloy_types';
3
3
  /**
4
4
  * The contents of a Malloy query document.
5
5
  */
@@ -77,6 +77,7 @@ export interface Connection extends InfoConnection {
77
77
  canPersist(): this is PersistSQLResults;
78
78
  canStream(): this is StreamingConnection;
79
79
  close(): Promise<void>;
80
+ estimateQueryCost(sqlCommand: string): Promise<QueryRunStats>;
80
81
  }
81
82
  export interface TestableConnection extends Connection {
82
83
  test(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.38-dev230602001052",
3
+ "version": "0.0.38-dev230606045241",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",