@malloydata/malloy 0.0.119-dev240118211537 → 0.0.119-dev240118215411

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.
@@ -60,6 +60,7 @@ const simple_numeric_functions_1 = require("./simple_numeric_functions");
60
60
  const avg_moving_1 = require("./avg_moving");
61
61
  const function_map_1 = require("./function_map");
62
62
  const coalesce_1 = require("./coalesce");
63
+ const sql_1 = require("./sql");
63
64
  /**
64
65
  * This is a function map containing default implementations of all Malloy
65
66
  * built-in functions. These don't work in all dialects, but are a good starting
@@ -131,5 +132,10 @@ exports.FUNCTIONS.add('min_window', sum_min_max_window_1.fnMinWindow);
131
132
  exports.FUNCTIONS.add('max_window', sum_min_max_window_1.fnMaxWindow);
132
133
  exports.FUNCTIONS.add('sum_window', sum_min_max_window_1.fnSumWindow);
133
134
  exports.FUNCTIONS.add('avg_moving', avg_moving_1.fnAvgRolling);
135
+ exports.FUNCTIONS.add('sql_number', sql_1.fnSqlNumber);
136
+ exports.FUNCTIONS.add('sql_string', sql_1.fnSqlString);
137
+ exports.FUNCTIONS.add('sql_date', sql_1.fnSqlDate);
138
+ exports.FUNCTIONS.add('sql_timestamp', sql_1.fnSqlTimestamp);
139
+ exports.FUNCTIONS.add('sql_boolean', sql_1.fnSqlBoolean);
134
140
  exports.FUNCTIONS.seal();
135
141
  //# sourceMappingURL=all_functions.js.map
@@ -0,0 +1,6 @@
1
+ import { DialectFunctionOverloadDef } from './util';
2
+ export declare function fnSqlNumber(): DialectFunctionOverloadDef[];
3
+ export declare function fnSqlString(): DialectFunctionOverloadDef[];
4
+ export declare function fnSqlDate(): DialectFunctionOverloadDef[];
5
+ export declare function fnSqlTimestamp(): DialectFunctionOverloadDef[];
6
+ export declare function fnSqlBoolean(): DialectFunctionOverloadDef[];
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2023 Google LLC
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining
6
+ * a copy of this software and associated documentation files
7
+ * (the "Software"), to deal in the Software without restriction,
8
+ * including without limitation the rights to use, copy, modify, merge,
9
+ * publish, distribute, sublicense, and/or sell copies of the Software,
10
+ * and to permit persons to whom the Software is furnished to do so,
11
+ * subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be
14
+ * included in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.fnSqlBoolean = exports.fnSqlTimestamp = exports.fnSqlDate = exports.fnSqlString = exports.fnSqlNumber = void 0;
26
+ const util_1 = require("./util");
27
+ function fnSqlNumber() {
28
+ const value = (0, util_1.makeParam)('value', (0, util_1.literal)((0, util_1.maxScalar)('string')));
29
+ return [
30
+ (0, util_1.overload)((0, util_1.minScalar)('number'), [value.param], [{ type: 'sql-string', e: [value.arg] }]),
31
+ ];
32
+ }
33
+ exports.fnSqlNumber = fnSqlNumber;
34
+ function fnSqlString() {
35
+ const value = (0, util_1.makeParam)('value', (0, util_1.literal)((0, util_1.maxScalar)('string')));
36
+ return [
37
+ (0, util_1.overload)((0, util_1.minScalar)('string'), [value.param], [{ type: 'sql-string', e: [value.arg] }]),
38
+ ];
39
+ }
40
+ exports.fnSqlString = fnSqlString;
41
+ function fnSqlDate() {
42
+ const value = (0, util_1.makeParam)('value', (0, util_1.literal)((0, util_1.maxScalar)('string')));
43
+ return [
44
+ (0, util_1.overload)((0, util_1.minScalar)('date'), [value.param], [{ type: 'sql-string', e: [value.arg] }]),
45
+ ];
46
+ }
47
+ exports.fnSqlDate = fnSqlDate;
48
+ function fnSqlTimestamp() {
49
+ const value = (0, util_1.makeParam)('value', (0, util_1.literal)((0, util_1.maxScalar)('string')));
50
+ return [
51
+ (0, util_1.overload)((0, util_1.minScalar)('timestamp'), [value.param], [{ type: 'sql-string', e: [value.arg] }]),
52
+ ];
53
+ }
54
+ exports.fnSqlTimestamp = fnSqlTimestamp;
55
+ function fnSqlBoolean() {
56
+ const value = (0, util_1.makeParam)('value', (0, util_1.literal)((0, util_1.maxScalar)('string')));
57
+ return [
58
+ (0, util_1.overload)((0, util_1.minScalar)('boolean'), [value.param], [{ type: 'sql-string', e: [value.arg] }]),
59
+ ];
60
+ }
61
+ exports.fnSqlBoolean = fnSqlBoolean;
62
+ //# sourceMappingURL=sql.js.map
@@ -216,6 +216,7 @@ function getJoinUsage(fs, expr) {
216
216
  };
217
217
  (0, utils_1.exprWalk)(expr, frag => {
218
218
  if (typeof frag !== 'string') {
219
+ // TODO make this work for field references inside sql_* functions
219
220
  if (frag.type === 'field') {
220
221
  const def = lookup(fs, frag.path);
221
222
  if (def.def.type !== 'struct' && def.def.type !== 'turtle') {
@@ -161,7 +161,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
161
161
  .join(', ')}) with source`);
162
162
  return (0, ast_utils_1.errorFor)('cannot call with source');
163
163
  }
164
- const funcCall = [
164
+ let funcCall = [
165
165
  {
166
166
  type: 'function_call',
167
167
  overload,
@@ -170,6 +170,50 @@ class ExprFunc extends expression_def_1.ExpressionDef {
170
170
  structPath,
171
171
  },
172
172
  ];
173
+ if ([
174
+ 'sql_number',
175
+ 'sql_string',
176
+ 'sql_date',
177
+ 'sql_timestamp',
178
+ 'sql_boolean',
179
+ ].includes(func.name)) {
180
+ if (!this.inExperiment('sql_functions', true)) {
181
+ return (0, ast_utils_1.errorFor)(`Cannot use sql_function \`${func.name}\`; use \`sql_functions\` experiment to enable this behavior`);
182
+ }
183
+ const str = argExprs[0].value;
184
+ if (str.length !== 1 ||
185
+ typeof str[0] === 'string' ||
186
+ str[0].type !== 'dialect' ||
187
+ str[0].function !== 'stringLiteral') {
188
+ this.log(`Invalid string literal for \`${func.name}\``);
189
+ }
190
+ else {
191
+ const literal = str[0].literal;
192
+ const parts = parseSQLInterpolation(literal);
193
+ const unsupportedInterpolations = parts
194
+ .filter(part => part.type === 'interpolation' && part.name.includes('.'))
195
+ .map(unsupportedPart => unsupportedPart.type === 'interpolation'
196
+ ? `\${${unsupportedPart.name}}`
197
+ : `\${${unsupportedPart.value}}`);
198
+ if (unsupportedInterpolations.length > 0) {
199
+ const unsupportedInterpolationMsg = unsupportedInterpolations.length === 1
200
+ ? `'.' paths are not yet supported in sql interpolations, found ${unsupportedInterpolations.at(0)}`
201
+ : `'.' paths are not yet supported in sql interpolations, found [${unsupportedInterpolations.join(', ')}]`;
202
+ this.log(unsupportedInterpolationMsg);
203
+ return (0, ast_utils_1.errorFor)(`${unsupportedInterpolationMsg}. See LookML \${...} documentation at https://cloud.google.com/looker/docs/reference/param-field-sql#sql_for_dimensions`);
204
+ }
205
+ funcCall = [
206
+ {
207
+ type: 'sql-string',
208
+ e: parts.map(part => part.type === 'string'
209
+ ? part.value
210
+ : part.name === 'TABLE'
211
+ ? { type: 'source-reference' }
212
+ : { type: 'field-reference', path: part.name }),
213
+ },
214
+ ];
215
+ }
216
+ }
173
217
  if (type.dataType === 'any') {
174
218
  this.log(`Invalid return type ${type.dataType} for function '${this.name}'`);
175
219
  return (0, ast_utils_1.errorFor)('invalid return type');
@@ -285,4 +329,31 @@ function findOverload(func, args) {
285
329
  }
286
330
  }
287
331
  }
332
+ function parseSQLInterpolation(template) {
333
+ const parts = [];
334
+ let remaining = template;
335
+ while (remaining.length) {
336
+ const nextInterp = remaining.indexOf('${');
337
+ if (nextInterp === -1) {
338
+ parts.push({ type: 'string', value: remaining });
339
+ break;
340
+ }
341
+ else {
342
+ const interpEnd = remaining.slice(nextInterp).indexOf('}');
343
+ if (interpEnd === -1) {
344
+ parts.push({ type: 'string', value: remaining });
345
+ break;
346
+ }
347
+ if (nextInterp > 0) {
348
+ parts.push({ type: 'string', value: remaining.slice(0, nextInterp) });
349
+ }
350
+ parts.push({
351
+ type: 'interpolation',
352
+ name: remaining.slice(nextInterp + 2, interpEnd + nextInterp),
353
+ });
354
+ remaining = remaining.slice(interpEnd + nextInterp + 1);
355
+ }
356
+ }
357
+ return parts;
358
+ }
288
359
  //# sourceMappingURL=expr-func.js.map
@@ -1,5 +1,5 @@
1
1
  import { Dialect, DialectFieldList } from '../dialect';
2
- import { AggregateFragment, AggregateFunctionType, CompiledQuery, DialectFragment, Expr, FieldDef, FieldFragment, FieldRef, Filtered, FilterExpression, FilterFragment, FunctionCallFragment, FunctionOverloadDef, JoinRelationship, ModelDef, OrderBy, OutputFieldFragment, Parameter, ParameterFragment, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SpreadFragment, SQLExpressionFragment, StructDef, StructRef, TurtleDef, UngroupFragment } from './malloy_types';
2
+ import { AggregateFragment, AggregateFunctionType, CompiledQuery, DialectFragment, Expr, FieldDef, FieldFragment, FieldRef, FieldReferenceFragment, Filtered, FilterExpression, FilterFragment, FunctionCallFragment, FunctionOverloadDef, JoinRelationship, ModelDef, OrderBy, OutputFieldFragment, Parameter, ParameterFragment, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SourceReferenceFragment, SpreadFragment, SQLExpressionFragment, SqlStringFragment, StructDef, StructRef, TurtleDef, UngroupFragment } from './malloy_types';
3
3
  import { Connection } from '../runtime_types';
4
4
  import { AndChain } from './utils';
5
5
  import { QueryInfo } from '../dialect/dialect';
@@ -83,6 +83,9 @@ declare class QueryField extends QueryNode {
83
83
  generateAvgFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: AggregateFragment, state: GenerateState): string;
84
84
  generateCountFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: AggregateFragment, state: GenerateState): string;
85
85
  generateDialect(resultSet: FieldInstanceResult, context: QueryStruct, expr: DialectFragment, state: GenerateState): string;
86
+ generateFieldReference(resultSet: FieldInstanceResult, context: QueryStruct, expr: FieldReferenceFragment, state: GenerateState): string;
87
+ generateSqlString(resultSet: FieldInstanceResult, context: QueryStruct, expr: SqlStringFragment, state: GenerateState): string;
88
+ generateSourceReference(resultSet: FieldInstanceResult, context: QueryStruct, expr: SourceReferenceFragment): string;
86
89
  getAnalyticPartitions(resultStruct: FieldInstanceResult): string;
87
90
  generateAnalyticFragment(resultStruct: FieldInstanceResult, context: QueryStruct, expr: Expr, overload: FunctionOverloadDef, state: GenerateState, args: Expr[]): string;
88
91
  generateExpressionFromExpr(resultSet: FieldInstanceResult, context: QueryStruct, e: Expr, state?: GenerateState): string;
@@ -479,6 +479,24 @@ class QueryField extends QueryNode {
479
479
  generateDialect(resultSet, context, expr, state) {
480
480
  return this.generateExpressionFromExpr(resultSet, context, context.dialect.dialectExpr(resultSet.getQueryInfo(), expr), state);
481
481
  }
482
+ generateFieldReference(resultSet, context, expr, state) {
483
+ return this.generateFieldFragment(resultSet, context, { type: 'field', path: expr.path }, state);
484
+ }
485
+ generateSqlString(resultSet, context, expr, state) {
486
+ return expr.e
487
+ .map(part => typeof part === 'string'
488
+ ? part
489
+ : this.generateExpressionFromExpr(resultSet, context, [part], state))
490
+ .join('');
491
+ }
492
+ generateSourceReference(resultSet, context, expr) {
493
+ if (expr.path === undefined) {
494
+ return context.getSQLIdentifier();
495
+ }
496
+ else {
497
+ return context.getFieldByName(expr.path).getIdentifier();
498
+ }
499
+ }
482
500
  getAnalyticPartitions(resultStruct) {
483
501
  const ret = [];
484
502
  let p = resultStruct.parent;
@@ -643,6 +661,15 @@ class QueryField extends QueryNode {
643
661
  else if (expr.type === 'dialect') {
644
662
  s += this.generateDialect(resultSet, context, expr, state);
645
663
  }
664
+ else if (expr.type === 'sql-string') {
665
+ s += this.generateSqlString(resultSet, context, expr, state);
666
+ }
667
+ else if (expr.type === 'source-reference') {
668
+ s += this.generateSourceReference(resultSet, context, expr);
669
+ }
670
+ else if (expr.type === 'field-reference') {
671
+ s += this.generateFieldReference(resultSet, context, expr, state);
672
+ }
646
673
  else {
647
674
  throw new Error(`Internal Error: Unknown expression fragment ${JSON.stringify(expr, undefined, 2)}`);
648
675
  }
@@ -151,6 +151,21 @@ export interface FieldFragment {
151
151
  path: string;
152
152
  }
153
153
  export declare function isFieldFragment(f: Fragment): f is FieldFragment;
154
+ export interface FieldReferenceFragment {
155
+ type: 'field-reference';
156
+ path: string;
157
+ }
158
+ export declare function isFieldReferenceFragment(f: Fragment): f is FieldReferenceFragment;
159
+ export interface SqlStringFragment {
160
+ type: 'sql-string';
161
+ e: Expr;
162
+ }
163
+ export declare function isSqlStringFragment(f: Fragment): f is SqlStringFragment;
164
+ export interface SourceReferenceFragment {
165
+ type: 'source-reference';
166
+ path?: string;
167
+ }
168
+ export declare function isSourceReferenceFragment(f: Fragment): f is SourceReferenceFragment;
154
169
  export interface ParameterFragment {
155
170
  type: 'parameter';
156
171
  path: string;
@@ -234,7 +249,7 @@ export interface NumberLiteralFragment extends DialectFragmentBase {
234
249
  literal: string;
235
250
  }
236
251
  export type DialectFragment = DivFragment | TimeLiteralFragment | NowFragment | TimeDeltaFragment | TimeDiffFragment | TimeTruncFragment | TypecastFragment | TimeExtractFragment | StringLiteralFragment | RegexpLiteralFragment | NumberLiteralFragment | RegexpMatchFragment;
237
- export type Fragment = string | ApplyFragment | ApplyValueFragment | FieldFragment | ParameterFragment | FilterFragment | OutputFieldFragment | AggregateFragment | UngroupFragment | DialectFragment | FunctionParameterFragment | FunctionCallFragment | SQLExpressionFragment | SpreadFragment;
252
+ export type Fragment = string | ApplyFragment | ApplyValueFragment | FieldFragment | FieldReferenceFragment | SourceReferenceFragment | SqlStringFragment | ParameterFragment | FilterFragment | OutputFieldFragment | AggregateFragment | UngroupFragment | DialectFragment | FunctionParameterFragment | FunctionCallFragment | SQLExpressionFragment | SpreadFragment;
238
253
  export type Expr = Fragment[];
239
254
  export interface TypedValue {
240
255
  value: Expr;
@@ -22,8 +22,8 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.isMatrixOperation = exports.isIndexSegment = exports.isRawSegment = exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isPartialSegment = exports.isReduceSegment = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.FieldIsIntrinsic = exports.isCastType = exports.isAtomicFieldType = exports.isTimeFieldType = exports.hasExpression = exports.maxOfExpressionTypes = exports.maxExpressionType = exports.isExpressionTypeLEQ = exports.expressionIsAnalytic = exports.expressionIsCalculation = exports.expressionInvolvesAggregate = exports.expressionIsUngroupedAggregate = exports.expressionIsAggregate = exports.expressionIsScalar = exports.mkExpr = exports.isApplyFragment = exports.isApplyValue = exports.isParameterFragment = exports.isFieldFragment = exports.isSpreadFragment = exports.isSQLExpressionFragment = exports.isFunctionCallFragment = exports.isFunctionParameterFragment = exports.isUngroupFragment = exports.isAsymmetricFragment = exports.isAggregateFragment = exports.isDialectFragment = exports.isFilterFragment = exports.isOutputFieldFragment = exports.isFilteredAliasedName = exports.paramHasValue = exports.isConditionParameter = exports.isValueParameter = void 0;
26
- exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = exports.isMeasureLike = exports.getPhysicalFields = exports.getDimensions = exports.isPhysical = exports.isDimensional = exports.isAtomicField = exports.isTurtleDef = exports.getIdentifier = exports.isFieldStructDef = exports.isFieldTimeBased = exports.isFieldTypeDef = exports.isSQLBlockStruct = exports.mergeEvalSpaces = exports.isSQLFragment = exports.isJoinOn = void 0;
25
+ exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isPartialSegment = exports.isReduceSegment = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.FieldIsIntrinsic = exports.isCastType = exports.isAtomicFieldType = exports.isTimeFieldType = exports.hasExpression = exports.maxOfExpressionTypes = exports.maxExpressionType = exports.isExpressionTypeLEQ = exports.expressionIsAnalytic = exports.expressionIsCalculation = exports.expressionInvolvesAggregate = exports.expressionIsUngroupedAggregate = exports.expressionIsAggregate = exports.expressionIsScalar = exports.mkExpr = exports.isApplyFragment = exports.isApplyValue = exports.isParameterFragment = exports.isSourceReferenceFragment = exports.isSqlStringFragment = exports.isFieldReferenceFragment = exports.isFieldFragment = exports.isSpreadFragment = exports.isSQLExpressionFragment = exports.isFunctionCallFragment = exports.isFunctionParameterFragment = exports.isUngroupFragment = exports.isAsymmetricFragment = exports.isAggregateFragment = exports.isDialectFragment = exports.isFilterFragment = exports.isOutputFieldFragment = exports.isFilteredAliasedName = exports.paramHasValue = exports.isConditionParameter = exports.isValueParameter = void 0;
26
+ exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = exports.isMeasureLike = exports.getPhysicalFields = exports.getDimensions = exports.isPhysical = exports.isDimensional = exports.isAtomicField = exports.isTurtleDef = exports.getIdentifier = exports.isFieldStructDef = exports.isFieldTimeBased = exports.isFieldTypeDef = exports.isSQLBlockStruct = exports.mergeEvalSpaces = exports.isSQLFragment = exports.isJoinOn = exports.isMatrixOperation = exports.isIndexSegment = exports.isRawSegment = void 0;
27
27
  function isValueParameter(p) {
28
28
  return p.value !== undefined;
29
29
  }
@@ -90,6 +90,18 @@ function isFieldFragment(f) {
90
90
  return (f === null || f === void 0 ? void 0 : f.type) === 'field';
91
91
  }
92
92
  exports.isFieldFragment = isFieldFragment;
93
+ function isFieldReferenceFragment(f) {
94
+ return (f === null || f === void 0 ? void 0 : f.type) === 'field-reference';
95
+ }
96
+ exports.isFieldReferenceFragment = isFieldReferenceFragment;
97
+ function isSqlStringFragment(f) {
98
+ return (f === null || f === void 0 ? void 0 : f.type) === 'sql-string';
99
+ }
100
+ exports.isSqlStringFragment = isSqlStringFragment;
101
+ function isSourceReferenceFragment(f) {
102
+ return (f === null || f === void 0 ? void 0 : f.type) === 'source-reference';
103
+ }
104
+ exports.isSourceReferenceFragment = isSourceReferenceFragment;
93
105
  function isParameterFragment(f) {
94
106
  return (f === null || f === void 0 ? void 0 : f.type) === 'parameter';
95
107
  }
@@ -126,6 +126,11 @@ function exprMap(expr, func) {
126
126
  case 'parameter':
127
127
  case 'outputField':
128
128
  return fragment;
129
+ case 'sql-string':
130
+ return {
131
+ ...fragment,
132
+ e: exprMap(fragment.e, func),
133
+ };
129
134
  case 'function_call':
130
135
  return {
131
136
  ...fragment,
@@ -232,6 +237,11 @@ function exprWalk(expr, func) {
232
237
  ...fragment,
233
238
  args: fragment.args.map(arg => exprWalk(arg, func)),
234
239
  };
240
+ case 'sql-string':
241
+ return {
242
+ ...fragment,
243
+ e: exprWalk(fragment.e, func),
244
+ };
235
245
  case 'filterExpression':
236
246
  return {
237
247
  ...fragment,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.119-dev240118211537",
3
+ "version": "0.0.119-dev240118215411",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",