@malloydata/malloy 0.0.22-dev230117232218 → 0.0.22-dev230119001546

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.
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.TurtleHeadedPipe = exports.ExistingQuery = exports.Index = exports.Turtles = exports.Limit = exports.Ordering = exports.OrderBy = exports.WildcardFieldReference = exports.RenameField = exports.PrimaryKey = exports.Document = exports.QOPDesc = exports.Aggregate = exports.GroupBy = exports.FieldListEdit = exports.ProjectStatement = exports.isFieldCollectionMember = exports.FieldReferences = exports.FieldReference = exports.FieldName = exports.Renames = exports.Dimensions = exports.Measures = exports.DeclareFields = exports.Filter = exports.FilterElement = exports.ExploreDesc = exports.isExploreProperty = exports.isQueryProperty = exports.Joins = exports.ExpressionJoin = exports.KeyJoin = exports.Join = exports.QuerySource = exports.SQLSource = exports.NamedSource = exports.ModelEntryReference = exports.IsValueBlock = exports.TableSource = exports.RefinedExplore = exports.DefineSourceList = exports.DefineExplore = exports.isDocStatement = exports.RunList = exports.Mallobj = exports.isExploreField = exports.Unimplemented = exports.ListOf = exports.MalloyElement = exports.ErrorFactory = void 0;
27
- exports.SampleProperty = exports.SQLStatement = exports.SQLString = exports.ConstantParameter = exports.HasParameter = exports.ImportStatement = exports.JSONStructDef = exports.JSONElement = exports.Top = exports.AnonymousQuery = exports.DefineQuery = exports.DefineQueryList = exports.isQueryElement = exports.Nests = exports.isNestedQuery = exports.NestDefinition = exports.NestRefinement = exports.NestReference = exports.TurtleDecl = exports.FullQuery = void 0;
27
+ exports.Range = exports.ExprTime = exports.ExprDuration = exports.TypeMistmatch = exports.ExprGranularTime = exports.ExprCompare = exports.BinaryBoolean = exports.applyBinary = exports.FieldDeclaration = exports.ConstantSubExpression = exports.ExpressionDef = exports.SampleProperty = exports.SQLStatement = exports.SQLString = exports.ConstantParameter = exports.HasParameter = exports.ImportStatement = exports.JSONStructDef = exports.JSONElement = exports.Top = exports.AnonymousQuery = exports.DefineQuery = exports.DefineQueryList = exports.isQueryElement = exports.Nests = exports.isNestedQuery = exports.NestDefinition = exports.NestRefinement = exports.NestReference = exports.TurtleDecl = exports.FullQuery = void 0;
28
28
  /*
29
29
  * Copyright 2023 Google LLC
30
30
  *
@@ -49,13 +49,15 @@ exports.SampleProperty = exports.SQLStatement = exports.SQLString = exports.Cons
49
49
  */
50
50
  const lodash_1 = require("lodash");
51
51
  const model = __importStar(require("../../model/malloy_types"));
52
+ const malloy_types_1 = require("../../model/malloy_types");
52
53
  const malloy_query_1 = require("../../model/malloy_query");
53
54
  const field_space_1 = require("../field-space");
54
- const index_1 = require("./index");
55
+ const ast_types_1 = require("./ast-types");
55
56
  const space_field_1 = require("../space-field");
56
57
  const sql_block_1 = require("../../model/sql_block");
57
58
  const util_1 = require("util");
58
59
  const time_utils_1 = require("./time-utils");
60
+ const apply_expr_1 = require("./apply-expr");
59
61
  /*
60
62
  ** For times when there is a code generation error but your function needs
61
63
  ** to return some kind of object to type properly, the ErrorFactory is
@@ -357,7 +359,7 @@ function getStructFieldDef(s, fn) {
357
359
  return s.fields.find((fld) => (fld.as || fld.name) === fn);
358
360
  }
359
361
  function isFieldDecl(f) {
360
- return (f instanceof index_1.FieldDeclaration ||
362
+ return (f instanceof FieldDeclaration ||
361
363
  f instanceof Join ||
362
364
  f instanceof TurtleDecl ||
363
365
  f instanceof Turtles);
@@ -861,7 +863,7 @@ class ExpressionJoin extends Join {
861
863
  }
862
864
  const joinRel = inStruct.structRelationship;
863
865
  if (model.isJoinOn(joinRel)) {
864
- joinRel.onExpression = (0, index_1.compressExpr)(exprX.value);
866
+ joinRel.onExpression = (0, ast_types_1.compressExpr)(exprX.value);
865
867
  }
866
868
  }
867
869
  structDef() {
@@ -940,7 +942,7 @@ class FilterElement extends MalloyElement {
940
942
  }
941
943
  const exprCond = {
942
944
  code: this.exprSrc,
943
- expression: (0, index_1.compressExpr)(exprVal.value),
945
+ expression: (0, ast_types_1.compressExpr)(exprVal.value),
944
946
  expressionType: exprVal.expressionType,
945
947
  };
946
948
  return exprCond;
@@ -1066,7 +1068,7 @@ exports.FieldReferences = FieldReferences;
1066
1068
  function isFieldCollectionMember(el) {
1067
1069
  return (el instanceof FieldReference ||
1068
1070
  el instanceof WildcardFieldReference ||
1069
- el instanceof index_1.FieldDeclaration);
1071
+ el instanceof FieldDeclaration);
1070
1072
  }
1071
1073
  exports.isFieldCollectionMember = isFieldCollectionMember;
1072
1074
  class ProjectStatement extends ListOf {
@@ -1088,7 +1090,7 @@ class GroupBy extends ListOf {
1088
1090
  constructor(members) {
1089
1091
  super("groupBy", members);
1090
1092
  for (const el of members) {
1091
- if (el instanceof index_1.FieldDeclaration) {
1093
+ if (el instanceof FieldDeclaration) {
1092
1094
  el.isMeasure = false;
1093
1095
  }
1094
1096
  }
@@ -1099,7 +1101,7 @@ class Aggregate extends ListOf {
1099
1101
  constructor(members) {
1100
1102
  super("aggregate", members);
1101
1103
  for (const el of members) {
1102
- if (el instanceof index_1.FieldDeclaration) {
1104
+ if (el instanceof FieldDeclaration) {
1103
1105
  el.isMeasure = true;
1104
1106
  }
1105
1107
  }
@@ -1933,7 +1935,7 @@ class Top extends MalloyElement {
1933
1935
  if (model.expressionIsAggregate(byExpr.expressionType)) {
1934
1936
  this.log("top by expression must be an aggregate");
1935
1937
  }
1936
- return { by: "expression", e: (0, index_1.compressExpr)(byExpr.value) };
1938
+ return { by: "expression", e: (0, ast_types_1.compressExpr)(byExpr.value) };
1937
1939
  }
1938
1940
  }
1939
1941
  return undefined;
@@ -2183,4 +2185,674 @@ class SampleProperty extends MalloyElement {
2183
2185
  }
2184
2186
  }
2185
2187
  exports.SampleProperty = SampleProperty;
2188
+ /**
2189
+ * Root node for any element in an expression. These essentially
2190
+ * create a sub-tree in the larger AST. Expression nodes know
2191
+ * how to write themselves as SQL (or rather, generate the
2192
+ * template for SQL required by the query writer)
2193
+ */
2194
+ class ExpressionDef extends MalloyElement {
2195
+ constructor() {
2196
+ super(...arguments);
2197
+ this.legalChildTypes = ast_types_1.FT.anyAtomicT;
2198
+ }
2199
+ granular() {
2200
+ return false;
2201
+ }
2202
+ /**
2203
+ * Some operators want to give the right hand value a chance to
2204
+ * rewrite itself. This requests a translation for a rewrite,
2205
+ * or returns undefined if that request should be denied.
2206
+ * @param fs FieldSpace
2207
+ * @returns Translated expression or undefined
2208
+ */
2209
+ requestExpression(fs) {
2210
+ return this.getExpression(fs);
2211
+ }
2212
+ defaultFieldName() {
2213
+ return undefined;
2214
+ }
2215
+ /**
2216
+ * Check an expression for type compatibility
2217
+ * @param _eNode currently unused, will be used to get error location
2218
+ * @param eVal ...list of expressions that must match legalChildTypes
2219
+ */
2220
+ typeCheck(eNode, eVal) {
2221
+ if (!ast_types_1.FT.in(eVal, this.legalChildTypes)) {
2222
+ eNode.log(`'${this.elementType}' Can't use type ${ast_types_1.FT.inspect(eVal)}`);
2223
+ return false;
2224
+ }
2225
+ return true;
2226
+ }
2227
+ /**
2228
+ * This is the operation which makes partial comparison and value trees work
2229
+ * The default implemention merely constructs LEFT OP RIGHT, but specialized
2230
+ * nodes like alternation trees or or partial comparison can control how
2231
+ * the appplication gets generated
2232
+ * @param fs The symbol table
2233
+ * @param op The operator being applied
2234
+ * @param expr The "other" (besdies 'this') value
2235
+ * @returns The translated expression
2236
+ */
2237
+ apply(fs, op, left) {
2238
+ return applyBinary(fs, left, op, this);
2239
+ }
2240
+ }
2241
+ exports.ExpressionDef = ExpressionDef;
2242
+ class ConstantSubExpression extends ExpressionDef {
2243
+ constructor(expr) {
2244
+ super({ expr });
2245
+ this.expr = expr;
2246
+ this.elementType = "constantExpression";
2247
+ }
2248
+ getExpression(_fs) {
2249
+ return this.constantValue();
2250
+ }
2251
+ get constantFs() {
2252
+ if (!this.cfs) {
2253
+ this.cfs = new ConstantFieldSpace();
2254
+ }
2255
+ return this.cfs;
2256
+ }
2257
+ constantValue() {
2258
+ return this.expr.getExpression(this.constantFs);
2259
+ }
2260
+ constantCondition(type) {
2261
+ const compareAndContrast = new ExprCompare(new DollarReference(type), "=", this.expr);
2262
+ const application = compareAndContrast.getExpression(this.constantFs);
2263
+ return { ...application, value: (0, ast_types_1.compressExpr)(application.value) };
2264
+ }
2265
+ apply(fs, op, expr) {
2266
+ return this.expr.apply(fs, op, expr);
2267
+ }
2268
+ requestExpression(fs) {
2269
+ return this.expr.requestExpression(fs);
2270
+ }
2271
+ }
2272
+ exports.ConstantSubExpression = ConstantSubExpression;
2273
+ class DollarReference extends ExpressionDef {
2274
+ constructor(refType) {
2275
+ super();
2276
+ this.refType = refType;
2277
+ this.elementType = "$";
2278
+ }
2279
+ getExpression(_fs) {
2280
+ return {
2281
+ dataType: this.refType,
2282
+ value: [{ type: "applyVal" }],
2283
+ expressionType: "scalar",
2284
+ };
2285
+ }
2286
+ }
2287
+ class ConstantFieldSpace {
2288
+ constructor() {
2289
+ this.type = "fieldSpace";
2290
+ }
2291
+ structDef() {
2292
+ throw new Error("ConstantFieldSpace cannot generate a structDef");
2293
+ }
2294
+ emptyStructDef() {
2295
+ throw new Error("ConstantFieldSpace cannot generate a structDef");
2296
+ }
2297
+ lookup(_name) {
2298
+ return {
2299
+ error: "Only constants allowed in parameter expressions",
2300
+ found: undefined,
2301
+ };
2302
+ }
2303
+ dialectObj() {
2304
+ return undefined;
2305
+ }
2306
+ whenComplete(step) {
2307
+ step();
2308
+ }
2309
+ }
2310
+ class FieldDeclaration extends MalloyElement {
2311
+ constructor(expr, defineName, exprSrc) {
2312
+ super({ expr });
2313
+ this.expr = expr;
2314
+ this.defineName = defineName;
2315
+ this.exprSrc = exprSrc;
2316
+ this.elementType = "fieldDeclaration";
2317
+ }
2318
+ fieldDef(fs, exprName) {
2319
+ /*
2320
+ * In an explore we cannot reference the thing we are defining, you need
2321
+ * to use rename. In a query, the output space is a new thing, and expressions
2322
+ * can reference the outer value in order to make a value with the new name,
2323
+ * and it feels wrong that this is HERE and not somehow in the QueryOperation.
2324
+ * For now, this stops the stack overflow, and passes all tests, but I think
2325
+ * a refactor of QueryFieldSpace might someday be the place where this should
2326
+ * happen.
2327
+ */
2328
+ return this.queryFieldDef(new field_space_1.DefSpace(fs, this), exprName);
2329
+ }
2330
+ queryFieldDef(exprFS, exprName) {
2331
+ let exprValue;
2332
+ try {
2333
+ exprValue = this.expr.getExpression(exprFS);
2334
+ }
2335
+ catch (error) {
2336
+ this.log(`Cannot define '${exprName}', ${error.message}`);
2337
+ return {
2338
+ name: `error_defining_${exprName}`,
2339
+ type: "string",
2340
+ };
2341
+ }
2342
+ const compressValue = (0, ast_types_1.compressExpr)(exprValue.value);
2343
+ const retType = exprValue.dataType;
2344
+ if ((0, malloy_types_1.isAtomicFieldType)(retType)) {
2345
+ const template = {
2346
+ name: exprName,
2347
+ type: retType,
2348
+ location: this.location,
2349
+ };
2350
+ if (compressValue.length > 0) {
2351
+ template.e = compressValue;
2352
+ }
2353
+ if (exprValue.expressionType) {
2354
+ template.expressionType = exprValue.expressionType;
2355
+ }
2356
+ if (this.exprSrc) {
2357
+ template.code = this.exprSrc;
2358
+ }
2359
+ // TODO this should work for dates too
2360
+ if ((0, ast_types_1.isGranularResult)(exprValue) && template.type === "timestamp") {
2361
+ template.timeframe = exprValue.timeframe;
2362
+ }
2363
+ return template;
2364
+ }
2365
+ const circularDef = exprFS instanceof field_space_1.DefSpace && exprFS.foundCircle;
2366
+ if (!circularDef) {
2367
+ if (exprValue.dataType == "unknown") {
2368
+ this.log(`Cannot define '${exprName}', value has unknown type`);
2369
+ }
2370
+ else {
2371
+ const badType = ast_types_1.FT.inspect(exprValue);
2372
+ this.log(`Cannot define '${exprName}', unexpected type: ${badType}`);
2373
+ }
2374
+ }
2375
+ return {
2376
+ name: `error_defining_${exprName}`,
2377
+ type: "string",
2378
+ };
2379
+ }
2380
+ }
2381
+ exports.FieldDeclaration = FieldDeclaration;
2382
+ /**
2383
+ * All of the magic of malloy expressions eventually flows to here,
2384
+ * where an operator is applied to two values. Depending on the
2385
+ * operator and value types this may involve transformations of
2386
+ * the values or even the operator.
2387
+ * @param fs FieldSpace for the symbols
2388
+ * @param left Left value
2389
+ * @param op The operator
2390
+ * @param right Right Value
2391
+ * @returns ExprValue of the expression
2392
+ */
2393
+ function applyBinary(fs, left, op, right) {
2394
+ if ((0, ast_types_1.isEquality)(op)) {
2395
+ return equality(fs, left, op, right);
2396
+ }
2397
+ if ((0, ast_types_1.isComparison)(op)) {
2398
+ return compare(fs, left, op, right);
2399
+ }
2400
+ if (oneOf(op, "+", "-")) {
2401
+ return delta(fs, left, op, right);
2402
+ }
2403
+ if (oneOf(op, "*", "%")) {
2404
+ return numeric(fs, left, op, right);
2405
+ }
2406
+ if (oneOf(op, "/")) {
2407
+ const num = left.getExpression(fs);
2408
+ const denom = right.getExpression(fs);
2409
+ if (num.dataType != "number") {
2410
+ left.log("Numerator for division must be a number");
2411
+ }
2412
+ else if (denom.dataType != "number") {
2413
+ right.log("Denominator for division must be a number");
2414
+ }
2415
+ else {
2416
+ const div = {
2417
+ type: "dialect",
2418
+ function: "div",
2419
+ numerator: num.value,
2420
+ denominator: denom.value,
2421
+ };
2422
+ return {
2423
+ dataType: "number",
2424
+ expressionType: (0, malloy_types_1.maxExpressionType)(num.expressionType, denom.expressionType),
2425
+ value: [div],
2426
+ };
2427
+ }
2428
+ return (0, ast_types_1.errorFor)("divide type mismatch");
2429
+ }
2430
+ left.log(`Canot use ${op} operator here`);
2431
+ return (0, ast_types_1.errorFor)("applybinary bad operator");
2432
+ }
2433
+ exports.applyBinary = applyBinary;
2434
+ class BinaryBoolean extends ExpressionDef {
2435
+ constructor(left, op, right) {
2436
+ super({ left, right });
2437
+ this.left = left;
2438
+ this.op = op;
2439
+ this.right = right;
2440
+ this.elementType = "abstract boolean binary";
2441
+ this.legalChildTypes = [ast_types_1.FT.boolT];
2442
+ }
2443
+ getExpression(fs) {
2444
+ const left = this.left.getExpression(fs);
2445
+ const right = this.right.getExpression(fs);
2446
+ if (this.typeCheck(this.left, left) && this.typeCheck(this.right, right)) {
2447
+ return {
2448
+ dataType: "boolean",
2449
+ expressionType: (0, malloy_types_1.maxExpressionType)(left.expressionType, right.expressionType),
2450
+ value: (0, ast_types_1.compose)(left.value, this.op, right.value),
2451
+ };
2452
+ }
2453
+ return (0, ast_types_1.errorFor)("logial required boolean");
2454
+ }
2455
+ }
2456
+ exports.BinaryBoolean = BinaryBoolean;
2457
+ class ExprCompare extends BinaryBoolean {
2458
+ constructor(left, op, right) {
2459
+ super(left, op, right);
2460
+ this.elementType = "a<=>b";
2461
+ this.legalChildTypes = compareTypes[op];
2462
+ }
2463
+ getExpression(fs) {
2464
+ if (!this.right.granular()) {
2465
+ const rhs = this.right.requestExpression(fs);
2466
+ if (rhs && (0, ast_types_1.isGranularResult)(rhs)) {
2467
+ const newRight = new ExprGranularTime(this.right, rhs.timeframe, false);
2468
+ return newRight.apply(fs, this.op, this.left);
2469
+ }
2470
+ }
2471
+ return this.right.apply(fs, this.op, this.left);
2472
+ }
2473
+ }
2474
+ exports.ExprCompare = ExprCompare;
2475
+ function equality(fs, left, op, right) {
2476
+ const lhs = left.getExpression(fs);
2477
+ const rhs = right.getExpression(fs);
2478
+ let value = timeCompare(lhs, op, rhs) || (0, ast_types_1.compose)(lhs.value, op, rhs.value);
2479
+ if (lhs.dataType != "unknown" && rhs.dataType != "unknown") {
2480
+ switch (op) {
2481
+ case "~":
2482
+ case "!~": {
2483
+ if (lhs.dataType === "string" && rhs.dataType === "string") {
2484
+ value = (0, ast_types_1.compose)(lhs.value, "LIKE", rhs.value);
2485
+ }
2486
+ else {
2487
+ const regexCmp = regexEqual(lhs, rhs);
2488
+ if (regexCmp === undefined) {
2489
+ throw new TypeMistmatch("Incompatible types for match('~') operator");
2490
+ }
2491
+ value = regexCmp;
2492
+ }
2493
+ value = (0, apply_expr_1.nullsafeNot)(value, op);
2494
+ break;
2495
+ }
2496
+ case "=":
2497
+ case "!=": {
2498
+ const nullCmp = nullCompare(lhs, op, rhs);
2499
+ if (nullCmp) {
2500
+ value = nullCmp;
2501
+ }
2502
+ else {
2503
+ value = (0, apply_expr_1.nullsafeNot)(regexEqual(lhs, rhs) || (0, ast_types_1.compose)(lhs.value, "=", rhs.value), op);
2504
+ }
2505
+ break;
2506
+ }
2507
+ }
2508
+ }
2509
+ return {
2510
+ dataType: "boolean",
2511
+ expressionType: (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType),
2512
+ value,
2513
+ };
2514
+ }
2515
+ function compare(fs, left, op, right) {
2516
+ const lhs = left.getExpression(fs);
2517
+ const rhs = right.getExpression(fs);
2518
+ const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
2519
+ const value = timeCompare(lhs, op, rhs) || (0, ast_types_1.compose)(lhs.value, op, rhs.value);
2520
+ return {
2521
+ dataType: "boolean",
2522
+ expressionType,
2523
+ value: value,
2524
+ };
2525
+ }
2526
+ function oneOf(op, ...operators) {
2527
+ return operators.includes(op);
2528
+ }
2529
+ function delta(fs, left, op, right) {
2530
+ const lhs = left.getExpression(fs);
2531
+ const rhs = right.getExpression(fs);
2532
+ if ((0, malloy_types_1.isTimeFieldType)(lhs.dataType)) {
2533
+ let duration = right;
2534
+ if (rhs.dataType !== "duration") {
2535
+ if ((0, ast_types_1.isGranularResult)(lhs)) {
2536
+ duration = new ExprDuration(right, lhs.timeframe);
2537
+ }
2538
+ else if (lhs.dataType === "date") {
2539
+ duration = new ExprDuration(right, "day");
2540
+ }
2541
+ else {
2542
+ left.log(`Can not offset time by '${rhs.dataType}'`);
2543
+ return (0, ast_types_1.errorFor)(`time plus ${rhs.dataType}`);
2544
+ }
2545
+ }
2546
+ return duration.apply(fs, op, left);
2547
+ }
2548
+ return numeric(fs, left, op, right);
2549
+ }
2550
+ function numeric(fs, left, op, right) {
2551
+ const lhs = left.getExpression(fs);
2552
+ const rhs = right.getExpression(fs);
2553
+ const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
2554
+ if (allAre("number", lhs, rhs)) {
2555
+ return {
2556
+ dataType: "number",
2557
+ expressionType,
2558
+ value: (0, ast_types_1.compose)(lhs.value, op, rhs.value),
2559
+ };
2560
+ }
2561
+ left.log(`Non numeric('${lhs.dataType},${rhs.dataType}') value with '${op}'`);
2562
+ return (0, ast_types_1.errorFor)("numbers required");
2563
+ }
2564
+ const compareTypes = {
2565
+ "~": [ast_types_1.FT.stringT],
2566
+ "!~": [ast_types_1.FT.stringT],
2567
+ "<": [ast_types_1.FT.numberT, ast_types_1.FT.stringT, ast_types_1.FT.dateT, ast_types_1.FT.timestampT],
2568
+ "<=": [ast_types_1.FT.numberT, ast_types_1.FT.stringT, ast_types_1.FT.dateT, ast_types_1.FT.timestampT],
2569
+ "=": [ast_types_1.FT.numberT, ast_types_1.FT.stringT, ast_types_1.FT.dateT, ast_types_1.FT.timestampT],
2570
+ "!=": [ast_types_1.FT.numberT, ast_types_1.FT.stringT, ast_types_1.FT.dateT, ast_types_1.FT.timestampT],
2571
+ ">=": [ast_types_1.FT.numberT, ast_types_1.FT.stringT, ast_types_1.FT.dateT, ast_types_1.FT.timestampT],
2572
+ ">": [ast_types_1.FT.numberT, ast_types_1.FT.stringT, ast_types_1.FT.dateT, ast_types_1.FT.timestampT],
2573
+ };
2574
+ /**
2575
+ * GranularTime is a moment in time which ALSO has a "granularity"
2576
+ * commonly this are created by applying ".datePart" to an expression
2577
+ * 1) They have a value, which is the moment in time
2578
+ * 2) When used in a comparison, they act like a range, for the
2579
+ * duration of 1 unit of granularity
2580
+ */
2581
+ class ExprGranularTime extends ExpressionDef {
2582
+ constructor(expr, units, truncate) {
2583
+ super({ expr });
2584
+ this.expr = expr;
2585
+ this.units = units;
2586
+ this.truncate = truncate;
2587
+ this.elementType = "granularTime";
2588
+ this.legalChildTypes = [ast_types_1.FT.timestampT, ast_types_1.FT.dateT];
2589
+ }
2590
+ granular() {
2591
+ return true;
2592
+ }
2593
+ getExpression(fs) {
2594
+ const timeframe = this.units;
2595
+ const exprVal = this.expr.getExpression(fs);
2596
+ if ((0, malloy_types_1.isTimeFieldType)(exprVal.dataType)) {
2597
+ const tsVal = {
2598
+ ...exprVal,
2599
+ dataType: exprVal.dataType,
2600
+ timeframe: timeframe,
2601
+ };
2602
+ if (this.truncate) {
2603
+ tsVal.value = [
2604
+ {
2605
+ type: "dialect",
2606
+ function: "trunc",
2607
+ expr: { value: exprVal.value, valueType: exprVal.dataType },
2608
+ units: timeframe,
2609
+ },
2610
+ ];
2611
+ }
2612
+ return tsVal;
2613
+ }
2614
+ this.log(`Cannot do time truncation on type '${exprVal.dataType}'`);
2615
+ return (0, ast_types_1.errorFor)(`granularity typecheck`);
2616
+ }
2617
+ apply(fs, op, left) {
2618
+ const rangeType = this.getExpression(fs).dataType;
2619
+ const _valueType = left.getExpression(fs).dataType;
2620
+ const granularityType = (0, malloy_types_1.isDateUnit)(this.units) ? "date" : "timestamp";
2621
+ if (rangeType == "date" && granularityType == "date") {
2622
+ return this.dateRange(fs, op, left);
2623
+ }
2624
+ return this.timestampRange(fs, op, left);
2625
+ /*
2626
+ write tests for each of these cases ....
2627
+
2628
+ vt rt gt use
2629
+ dt dt dt dateRange
2630
+ dt dt ts == or timeStampRange
2631
+ dt ts dt timestampRange
2632
+ dt ts ts timeStampRange
2633
+
2634
+ ts ts ts timestampRange
2635
+ ts ts dt timestampRange
2636
+ ts dt ts timestampRange
2637
+ ts dt dt either
2638
+
2639
+ */
2640
+ }
2641
+ timestampRange(fs, op, expr) {
2642
+ const begin = this.getExpression(fs);
2643
+ const beginTime = ExprTime.fromValue("timestamp", begin);
2644
+ const endTime = new ExprTime("timestamp", (0, time_utils_1.timeOffset)("timestamp", begin.value, "+", (0, malloy_types_1.mkExpr) `1`, this.units), begin.expressionType);
2645
+ const range = new Range(beginTime, endTime);
2646
+ return range.apply(fs, op, expr);
2647
+ }
2648
+ dateRange(fs, op, expr) {
2649
+ const begin = this.getExpression(fs);
2650
+ const beginTime = new ExprTime("date", begin.value, begin.expressionType);
2651
+ const endAt = (0, time_utils_1.timeOffset)("date", begin.value, "+", ["1"], this.units);
2652
+ const end = new ExprTime("date", endAt, begin.expressionType);
2653
+ const range = new Range(beginTime, end);
2654
+ return range.apply(fs, op, expr);
2655
+ }
2656
+ }
2657
+ exports.ExprGranularTime = ExprGranularTime;
2658
+ function timeCompare(lhs, op, rhs) {
2659
+ if ((0, malloy_types_1.isTimeFieldType)(lhs.dataType) && (0, malloy_types_1.isTimeFieldType)(rhs.dataType)) {
2660
+ if (lhs.dataType !== rhs.dataType) {
2661
+ let lval = lhs.value;
2662
+ let rval = rhs.value;
2663
+ if (lhs.dataType === "timestamp") {
2664
+ lval = (0, time_utils_1.castTimestampToDate)(lval);
2665
+ }
2666
+ else {
2667
+ rval = (0, time_utils_1.castTimestampToDate)(rval);
2668
+ }
2669
+ return (0, ast_types_1.compose)(lval, op, rval);
2670
+ }
2671
+ }
2672
+ return undefined;
2673
+ }
2674
+ function regexEqual(left, right) {
2675
+ if (left.dataType === "string") {
2676
+ if (right.dataType === "regular expression") {
2677
+ return [
2678
+ {
2679
+ type: "dialect",
2680
+ function: "regexpMatch",
2681
+ expr: left.value,
2682
+ regexp: right.value[0].replace(/^r'/, "'"),
2683
+ },
2684
+ ];
2685
+ }
2686
+ }
2687
+ else if (right.dataType === "string") {
2688
+ if (left.dataType === "regular expression") {
2689
+ return [
2690
+ {
2691
+ type: "dialect",
2692
+ function: "regexpMatch",
2693
+ expr: right.value,
2694
+ regexp: left.value[0].replace(/^r'/, "'"),
2695
+ },
2696
+ ];
2697
+ }
2698
+ }
2699
+ return undefined;
2700
+ }
2701
+ class TypeMistmatch extends Error {
2702
+ }
2703
+ exports.TypeMistmatch = TypeMistmatch;
2704
+ function nullCompare(left, op, right) {
2705
+ const not = op === "!=" || op === "!~";
2706
+ if (left.dataType === "null" || right.dataType === "null") {
2707
+ const maybeNot = not ? " NOT" : "";
2708
+ if (left.dataType !== "null") {
2709
+ return [...left.value, ` IS${maybeNot} NULL`];
2710
+ }
2711
+ if (right.dataType !== "null") {
2712
+ return [...right.value, `IS${maybeNot} NULL`];
2713
+ }
2714
+ return [not ? "false" : "true"];
2715
+ }
2716
+ return undefined;
2717
+ }
2718
+ class ExprDuration extends ExpressionDef {
2719
+ constructor(n, timeframe) {
2720
+ super({ n });
2721
+ this.n = n;
2722
+ this.timeframe = timeframe;
2723
+ this.elementType = "duration";
2724
+ this.legalChildTypes = [ast_types_1.FT.timestampT, ast_types_1.FT.dateT];
2725
+ }
2726
+ apply(fs, op, left) {
2727
+ const lhs = left.getExpression(fs);
2728
+ this.typeCheck(this, lhs);
2729
+ if ((0, malloy_types_1.isTimeFieldType)(lhs.dataType) && (op === "+" || op === "-")) {
2730
+ const num = this.n.getExpression(fs);
2731
+ if (!ast_types_1.FT.typeEq(num, ast_types_1.FT.numberT)) {
2732
+ this.log(`Duration units needs number not '${num.dataType}`);
2733
+ return (0, ast_types_1.errorFor)("illegal unit expression");
2734
+ }
2735
+ let resultGranularity;
2736
+ // Only allow the output of this to be granular if the
2737
+ // granularities match, this is still an area where
2738
+ // more thought is required.
2739
+ if ((0, ast_types_1.isGranularResult)(lhs) && lhs.timeframe == this.timeframe) {
2740
+ resultGranularity = lhs.timeframe;
2741
+ }
2742
+ if (lhs.dataType === "timestamp") {
2743
+ const result = (0, time_utils_1.timeOffset)("timestamp", lhs.value, op, num.value, this.timeframe);
2744
+ return (0, time_utils_1.timeResult)({
2745
+ dataType: "timestamp",
2746
+ expressionType: (0, malloy_types_1.maxExpressionType)(lhs.expressionType, num.expressionType),
2747
+ value: result,
2748
+ }, resultGranularity);
2749
+ }
2750
+ return (0, time_utils_1.timeResult)({
2751
+ dataType: "date",
2752
+ expressionType: (0, malloy_types_1.maxExpressionType)(lhs.expressionType, num.expressionType),
2753
+ value: (0, time_utils_1.timeOffset)("date", lhs.value, op, num.value, this.timeframe),
2754
+ }, resultGranularity);
2755
+ }
2756
+ return super.apply(fs, op, left);
2757
+ }
2758
+ getExpression(_fs) {
2759
+ return {
2760
+ dataType: "duration",
2761
+ expressionType: "scalar",
2762
+ value: ["__ERROR_DURATION_IS_NOT_A_VALUE__"],
2763
+ };
2764
+ }
2765
+ }
2766
+ exports.ExprDuration = ExprDuration;
2767
+ function allAre(oneType, ...values) {
2768
+ for (const v of values) {
2769
+ if (v.dataType !== oneType) {
2770
+ return false;
2771
+ }
2772
+ }
2773
+ return true;
2774
+ }
2775
+ class ExprTime extends ExpressionDef {
2776
+ constructor(timeType, value, expressionType = "scalar") {
2777
+ super();
2778
+ this.elementType = "timestampOrDate";
2779
+ this.elementType = timeType;
2780
+ this.translationValue = {
2781
+ dataType: timeType,
2782
+ expressionType,
2783
+ value: typeof value === "string" ? [value] : value,
2784
+ };
2785
+ }
2786
+ getExpression(_fs) {
2787
+ return this.translationValue;
2788
+ }
2789
+ static fromValue(timeType, expr) {
2790
+ let value = expr.value;
2791
+ if (timeType != expr.dataType) {
2792
+ const toTs = {
2793
+ type: "dialect",
2794
+ function: "cast",
2795
+ safe: false,
2796
+ dstType: timeType,
2797
+ expr: expr.value,
2798
+ };
2799
+ if ((0, malloy_types_1.isTimeFieldType)(expr.dataType)) {
2800
+ toTs.srcType = expr.dataType;
2801
+ }
2802
+ value = (0, ast_types_1.compressExpr)([toTs]);
2803
+ }
2804
+ return new ExprTime(timeType, value, expr.expressionType);
2805
+ }
2806
+ }
2807
+ exports.ExprTime = ExprTime;
2808
+ class Range extends ExpressionDef {
2809
+ constructor(first, last) {
2810
+ super({ first, last });
2811
+ this.first = first;
2812
+ this.last = last;
2813
+ this.elementType = "range";
2814
+ }
2815
+ apply(fs, op, expr) {
2816
+ switch (op) {
2817
+ case "=":
2818
+ case "!=": {
2819
+ const op1 = op === "=" ? ">=" : "<";
2820
+ const op2 = op === "=" ? "and" : "or";
2821
+ const op3 = op === "=" ? "<" : ">=";
2822
+ const fromValue = this.first.apply(fs, op1, expr);
2823
+ const toValue = this.last.apply(fs, op3, expr);
2824
+ return {
2825
+ dataType: "boolean",
2826
+ expressionType: (0, malloy_types_1.maxExpressionType)(fromValue.expressionType, toValue.expressionType),
2827
+ value: (0, ast_types_1.compose)(fromValue.value, op2, toValue.value),
2828
+ };
2829
+ }
2830
+ /**
2831
+ * This is a little surprising, but is actually how you comapre a
2832
+ * value to a range ...
2833
+ *
2834
+ * val > begin to end val >= end
2835
+ * val >= begin to end val >= begin
2836
+ * val < begin to end val < begin
2837
+ * val <= begin to end val < end
2838
+ */
2839
+ case ">":
2840
+ return this.last.apply(fs, ">=", expr);
2841
+ case ">=":
2842
+ return this.first.apply(fs, ">=", expr);
2843
+ case "<":
2844
+ return this.first.apply(fs, "<", expr);
2845
+ case "<=":
2846
+ return this.last.apply(fs, "<", expr);
2847
+ }
2848
+ throw new Error("mysterious error in range computation");
2849
+ }
2850
+ requestExpression(_fs) {
2851
+ return undefined;
2852
+ }
2853
+ getExpression(_fs) {
2854
+ return (0, ast_types_1.errorFor)("a range is not a value");
2855
+ }
2856
+ }
2857
+ exports.Range = Range;
2186
2858
  //# sourceMappingURL=ast-main.js.map