@graphenedata/cli 0.0.6 → 0.0.7

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.
package/cli.ts CHANGED
@@ -5,18 +5,17 @@ import {printDiagnostics, printTable} from './printer.ts'
5
5
  import {analyze, getDiagnostics, loadWorkspace, toSql, type Query} from '../lang/core.ts'
6
6
  import fs from 'fs-extra'
7
7
  import path from 'path'
8
- import {loadConfig} from '../lang/config.ts'
8
+ import dotenv from 'dotenv'
9
+ import {config, loadConfig} from '../lang/config.ts'
9
10
  import {runServeInBackground, stopGrapheneIfRunning} from './background.ts'
10
11
  import {check} from './check.ts'
11
- import {runQuery} from './connections/index.ts'
12
+ import {getConnection, runQuery} from './connections/index.ts'
12
13
  import {loginPkce} from './auth.ts'
13
14
 
15
+ dotenv.config({quiet: true})
14
16
  const program = new Command()
15
17
 
16
- program
17
- .name('graphene')
18
- .description('Graphene CLI')
19
- .version('1.0.0')
18
+ program.name('graphene').description('Graphene CLI').version('1.0.0')
20
19
 
21
20
  program.hook('preAction', async () => {
22
21
  if (process.env.CLI_DELAY) { // useful if you want to attach a debugger
@@ -51,6 +50,38 @@ program
51
50
  printTable(res.rows)
52
51
  })
53
52
 
53
+ program.command('schema')
54
+ .description('Inspect database tables or describe a table')
55
+ .argument('[schema | table]', 'Optional schema or table name to describe')
56
+ .action(async (tableArg: string) => {
57
+ let connection = await getConnection()
58
+ let datasets = await connection.listDatasets()
59
+
60
+ // if there's no arg and more than one dataset, just list the datasets
61
+ if (!tableArg && datasets.length > 1) {
62
+ return console.log(`Datasets available:\n${datasets.join('\n')}`)
63
+ }
64
+
65
+ // figure out if you're wanting to list tables in a schema/dataset
66
+ let dsToList: string | null = null
67
+ if (datasets.includes(tableArg)) dsToList = tableArg // you gave the name of a dataset
68
+ else if (!tableArg && datasets.length == 1) dsToList = datasets[0] // only one dataset, and no args
69
+ else if (!tableArg && config.namespace) dsToList = config.namespace // default namespace configured
70
+ else if (!tableArg && config.dialect == 'duckdb') dsToList = '<default>'
71
+
72
+ if (dsToList) {
73
+ let tables = await connection.listTables(dsToList)
74
+ return console.log(`Tables${dsToList ? ` in ${dsToList}` : ''}:\n${tables.join('\n')}`)
75
+ }
76
+
77
+ // otherwise, assume you're wanting to see tables
78
+ let cols = await connection.describeTable(tableArg)
79
+ if (!cols.length) return console.log(`Table ${tableArg} not found`)
80
+ console.log(`table ${tableArg} (`)
81
+ cols.forEach(col => console.log(` ${col.name} ${col.dataType}`))
82
+ console.log(')')
83
+ })
84
+
54
85
  program
55
86
  .command('serve')
56
87
  .description('Run the local server')
package/dist/cli/cli.js CHANGED
@@ -172,7 +172,7 @@ var init_config = __esm({
172
172
  }
173
173
  });
174
174
 
175
- // ../lang/functions.ts
175
+ // ../lang/functionDefs.ts
176
176
  import { DUCKDB_DIALECT_FUNCTIONS, GlobalNameSpace, DialectNameSpace, getDialect } from "@graphenedata/malloy";
177
177
  function findOverloads(name, dialect) {
178
178
  if (!dialectNamespaces.has(dialect)) {
@@ -183,8 +183,8 @@ function findOverloads(name, dialect) {
183
183
  return res?.entry ? res.entry.overloads : [];
184
184
  }
185
185
  var globalNamespace, dialectNamespaces, BIGQUERY_DIALECT_FUNCTIONS;
186
- var init_functions = __esm({
187
- "../lang/functions.ts"() {
186
+ var init_functionDefs = __esm({
187
+ "../lang/functionDefs.ts"() {
188
188
  "use strict";
189
189
  globalNamespace = new GlobalNameSpace();
190
190
  dialectNamespaces = /* @__PURE__ */ new Map();
@@ -254,8 +254,9 @@ var init_functions = __esm({
254
254
  impl: { function: "TIMESTAMP_DIFF" }
255
255
  },
256
256
  "date_trunc": {
257
- takes: { "date": "timestamp", "unit": { sql_native: "kw" } },
258
- returns: "timestamp",
257
+ generic: { "T": ["date", "timestamp"] },
258
+ takes: { "date": { generic: "T" }, "unit": { sql_native: "kw" } },
259
+ returns: { generic: "T" },
259
260
  impl: { sql: "DATE_TRUNC(${date}, ${unit})" }
260
261
  },
261
262
  "current_date": {
@@ -315,6 +316,112 @@ var init_functions = __esm({
315
316
  }
316
317
  });
317
318
 
319
+ // ../lang/functions.ts
320
+ function analyzeFunctionCall(expr, scope) {
321
+ let rawName = txt(expr.getChild("Identifier")).toLowerCase();
322
+ let argNodes = expr.getChildren("Expression");
323
+ let name = rawName;
324
+ let overload = findOverloads(name, config.dialect).find((o) => {
325
+ return o.params.length == argNodes.length || !!o.params.find((p) => p.isVariadic);
326
+ });
327
+ let args = argNodes.map((node, idx) => {
328
+ let firstType = overload?.params[idx]?.allowedTypes[0];
329
+ if (firstType?.type === "sql native" && firstType?.rawType === "kw") {
330
+ return { node: "genericSQLExpr", kids: { args: [] }, type: "sql native", src: [txt(node)], isAgg: false };
331
+ } else {
332
+ let argExpr = analyzeExpression(node, scope);
333
+ let allowed = overload?.params[idx]?.allowedTypes.map((at) => at.type);
334
+ if (allowed) checkTypes(argExpr, allowed, node);
335
+ return argExpr;
336
+ }
337
+ });
338
+ let type = overload?.returnType.type;
339
+ if (type == "generic") type = args[0]?.type || "string";
340
+ if (type && !isSupportedType(type)) {
341
+ return diag(expr, `Unsupported function return type ${type} from function ${name}`, errExpr);
342
+ }
343
+ let structPaths = /* @__PURE__ */ new Set();
344
+ args.forEach((a) => walkExpression(a, (e) => {
345
+ if (e.node != "field") return;
346
+ structPaths.add(e.path.slice(0, -1).join(".") || scope.table.name);
347
+ }));
348
+ let ret;
349
+ let percentileMatch = /^p(\d+)$/.exec(rawName);
350
+ if (["count", "min", "max", "avg", "sum"].includes(name.toLowerCase())) {
351
+ let type2 = "number", typeDef;
352
+ if (["min", "max", "avg"].includes(name.toLowerCase())) {
353
+ type2 = args[0].type;
354
+ typeDef = args[0].typeDef;
355
+ }
356
+ ret = { node: "aggregate", function: name, e: args[0], type: type2, typeDef, isAgg: true };
357
+ } else if (percentileMatch) {
358
+ ret = analyzePercentile(expr, scope, percentileMatch[1], argNodes);
359
+ } else if (overload && type) {
360
+ ret = {
361
+ node: "function_call",
362
+ type,
363
+ name,
364
+ overload,
365
+ expressionType: overload.returnType.expressionType || "scalar",
366
+ kids: { args },
367
+ isAgg: overload.returnType.expressionType == "aggregate" || args.some((a) => a.isAgg)
368
+ };
369
+ } else {
370
+ return diag(expr, `Unknown function: ${name}`, errExpr);
371
+ }
372
+ if (structPaths.size > 1 && (ret.node == "aggregate" || ret.expressionType == "aggregate")) {
373
+ return diag(expr, "Graphene only supports a single table within aggregates. This one has: " + Array.from(structPaths).join(", "), errExpr);
374
+ }
375
+ let foriegnPaths = Array.from(structPaths).filter((p) => p != scope.table.name);
376
+ if (foriegnPaths.length > 0) ret.structPath = foriegnPaths[0].split(".");
377
+ return ret;
378
+ }
379
+ function isSupportedType(value) {
380
+ let supported = ["string", "number", "boolean", "date", "timestamp", "json", "sql native", "error", "array", "record", "null", "generic", "interval"];
381
+ return supported.includes(value);
382
+ }
383
+ function analyzePercentile(callNode, scope, digits, argNodes) {
384
+ let frac = Number(`0.${digits}`);
385
+ if (Number(digits) == 100) return diag(callNode, "p100 is not allowed", errExpr);
386
+ if (Number(digits) == 0) return diag(callNode, "p0 is not allowed", errExpr);
387
+ if (config.dialect == "bigquery" && frac > 0.99) return diag(callNode, "BigQuery only supports up to p99", errExpr);
388
+ let valueExpr = analyzeExpression(argNodes[0], scope);
389
+ checkTypes(valueExpr, ["number"], argNodes[0]);
390
+ let src;
391
+ switch (config.dialect) {
392
+ case "duckdb":
393
+ src = ["quantile_cont(", `, ${frac})`];
394
+ break;
395
+ case "bigquery": {
396
+ src = ["approx_quantiles(", `, 100)[OFFSET(${Math.round(frac * 10)})]`];
397
+ break;
398
+ }
399
+ case "snowflake":
400
+ src = [`PERCENTILE_CONT(${frac}) WITHIN GROUP (ORDER BY `, ")"];
401
+ break;
402
+ default:
403
+ return diag(callNode, `Percentile functions are not supported for dialect ${config.dialect}`, errExpr);
404
+ }
405
+ return {
406
+ node: "genericSQLExpr",
407
+ kids: { args: [valueExpr] },
408
+ src,
409
+ type: "number",
410
+ isAgg: true
411
+ };
412
+ }
413
+ var errExpr;
414
+ var init_functions = __esm({
415
+ "../lang/functions.ts"() {
416
+ "use strict";
417
+ init_config();
418
+ init_functionDefs();
419
+ init_util();
420
+ init_analyze();
421
+ errExpr = { node: "error", type: "error" };
422
+ }
423
+ });
424
+
318
425
  // ../lang/temporalLiterals.ts
319
426
  function parseTemporalLiteral(value, expected) {
320
427
  let raw = (value ?? "").trim();
@@ -598,9 +705,9 @@ function findTables(fi) {
598
705
  fi.tables = [];
599
706
  let nodes = tn.getChildren("TableStatement").concat(tn.getChildren("ViewStatement"));
600
707
  for (let syntaxNode of nodes) {
601
- let name = txt(syntaxNode.getChild("Identifier"));
708
+ let name = txt(syntaxNode.getChild("Ref"));
602
709
  if (Object.values(FILE_MAP).find((f) => f.tables.find((t) => t.name == name))) {
603
- diag(syntaxNode.getChild("Identifier"), `Table "${name}" is already defined`);
710
+ diag(syntaxNode.getChild("Ref"), `Table "${name}" is already defined`);
604
711
  }
605
712
  let table2 = makeTable(name, syntaxNode.getChild("QueryStatement") ? "query_source" : "table");
606
713
  table2.metadata = extractLeadingMetadata(syntaxNode);
@@ -623,11 +730,10 @@ function addColumnField(table2, node) {
623
730
  }
624
731
  let type = convertDataType(txt(node.getChild("DataType")));
625
732
  if (!type) diag(node, `Unsupported data type: ${txt(node.getChild("DataType"))}`);
626
- let field = { name, type, metadata: extractLeadingMetadata(node) };
627
- return addFieldToTable(table2, field, node);
733
+ addFieldToTable(table2, { name, type, metadata: extractLeadingMetadata(node) }, node);
628
734
  }
629
735
  function addJoinField(table2, node) {
630
- let nameNode = node.getChild("Alias") || node.getChild("Identifier");
736
+ let nameNode = node.getChild("Alias") || node.getChild("Ref").getChildren("Identifier").pop();
631
737
  return addFieldToTable(table2, { name: txt(nameNode) }, node);
632
738
  }
633
739
  function addComputedField(table2, node) {
@@ -636,19 +742,17 @@ function addComputedField(table2, node) {
636
742
  }
637
743
  function addFieldToTable(table2, field, node) {
638
744
  if (table2.fields.find((f) => f.name == field.name)) {
639
- diag(node, `Table already has a field called "${field.name}"`);
640
- return null;
745
+ return diag(node, `Table already has a field called "${field.name}"`);
641
746
  }
642
747
  table2.fields.push(field);
643
748
  FIELD_NODE_MAP.set(field, node);
644
- return field;
645
749
  }
646
750
  function applyExtends(fi) {
647
751
  fi.tree.topNode.getChildren("ExtendStatement").forEach((node) => {
648
- let tableName = txt(node.getChild("Identifier"));
752
+ let tableName = txt(node.getChild("Ref"));
649
753
  let target = lookupTable(tableName, node);
650
754
  if (!target) {
651
- return diag(node.getChild("Identifier") || node, `Cannot extend unknown table "${tableName}"`);
755
+ return diag(node.getChild("Ref") || node, `Cannot extend unknown table "${tableName}"`);
652
756
  }
653
757
  node.getChildren("JoinDef").forEach((jn) => addJoinField(target, jn));
654
758
  node.getChildren("ComputedDef").forEach((cn) => addComputedField(target, cn));
@@ -675,14 +779,14 @@ function analyzeField(field, table2) {
675
779
  analysisQueue.add(field);
676
780
  if (node.name == "JoinDef") {
677
781
  field = field;
678
- let target = lookupTable(txt(node.getChild("Identifier")), node);
782
+ let target = lookupTable(txt(node.getChild("Ref")), node);
679
783
  if (!target) return diag(node, "Unknown table to join");
680
784
  if (target.type == "query_source") analyzeTable(target);
681
785
  let joinTypeStr = txt(node.getChild("JoinType")).replace(/\s+/g, " ");
682
786
  let jt = { "join many": "many", "join one": "one" }[joinTypeStr];
683
787
  if (!jt) return diag(node, "Unknown join type");
684
788
  Object.assign(field, target, { name: field.name, join: jt });
685
- field.onExpression = analyzeExpression(node.getChild("Expression"), { table: table2, outputFields: [] });
789
+ field.onExpression = analyzeExpression(node.getChild("BinaryExpression"), { table: table2, outputFields: [] });
686
790
  }
687
791
  if (node.name == "ComputedDef") {
688
792
  let e = analyzeExpression(node.getChild("Expression"), { table: table2, outputFields: [] });
@@ -711,7 +815,7 @@ function analyzeQuery(queryNode) {
711
815
  TABLE_NODE_MAP.set(scope.table, froms[0].getChild("SubqueryExpression"));
712
816
  analyzeTable(scope.table);
713
817
  } else {
714
- baseTableName = txt(froms[0].getChild("Identifier"));
818
+ baseTableName = txt(froms[0].getChild("Ref"));
715
819
  scope.table = lookupTable(baseTableName, froms[0]);
716
820
  if (!scope.table) return diag(froms[0], `could not find table "${baseTableName}"`);
717
821
  NODE_ENTITY_MAP.set(froms[0], { entityType: "table", table: scope.table });
@@ -799,7 +903,7 @@ function analyzeQuery(queryNode) {
799
903
  }
800
904
  function analyzeExpression(expr, scope) {
801
905
  if (expr.type.isError) {
802
- return diag(expr, "Invalid expression", errExpr);
906
+ return diag(expr, "Invalid expression", errExpr2);
803
907
  }
804
908
  switch (expr.name) {
805
909
  case "Number":
@@ -814,7 +918,7 @@ function analyzeExpression(expr, scope) {
814
918
  return { node: "parameter", path: [txt(expr).slice(1)], type: "string" };
815
919
  case "Ref": {
816
920
  let field = lookupField(expr, scope);
817
- if (!field) return errExpr;
921
+ if (!field) return errExpr2;
818
922
  let type = field.type || "unknown";
819
923
  let typeInfo = { type };
820
924
  if (type === "date" || type === "timestamp") typeInfo.typeDef = { type };
@@ -828,9 +932,9 @@ function analyzeExpression(expr, scope) {
828
932
  let extractExprNode = expr.getChild("Expression");
829
933
  let e = analyzeExpression(extractExprNode, scope);
830
934
  checkTypes(e, ["date", "timestamp"], extractExprNode);
831
- if (!isTemporalType(e.type) || !e.typeDef) return diag(expr, "Expression must be a date or timestamp", errExpr);
935
+ if (!isTemporalType(e.type) || !e.typeDef) return diag(expr, "Expression must be a date or timestamp", errExpr2);
832
936
  let units = txt(expr.getChild("ExtractUnit")).replace(/^['"]|['"]$/g, "").toLowerCase();
833
- if (!isExtractUnit(units)) return diag(expr, "Not a valid unit to extract", errExpr);
937
+ if (!isExtractUnit(units)) return diag(expr, "Not a valid unit to extract", errExpr2);
834
938
  return { node: "extract", type: "number", units, e, isAgg: false };
835
939
  }
836
940
  case "FunctionCall":
@@ -863,6 +967,7 @@ function analyzeExpression(expr, scope) {
863
967
  checkTypes(right2, ["number"], expr.lastChild);
864
968
  }
865
969
  if (op == "<" || op == "<=" || op == ">" || op == ">=" || op == "=" || op == "!=" || op == "<>") {
970
+ if (op == "<>") op = "!=";
866
971
  ensureSameType(left2, expr.firstChild, right2, expr.lastChild);
867
972
  type = "boolean";
868
973
  }
@@ -884,7 +989,7 @@ function analyzeExpression(expr, scope) {
884
989
  if (opTxt === "not") return { node: "not", e: child, type: "boolean", isAgg: child.isAgg };
885
990
  if (opTxt === "-") return { node: "unary-", e: child, type: child.type, isAgg: child.isAgg };
886
991
  if (opTxt === "+") return { node: "()", e: child, type: child.type, isAgg: child.isAgg };
887
- return diag(expr, `Unknown unary operator: ${opTxt}`, errExpr);
992
+ return diag(expr, `Unknown unary operator: ${opTxt}`, errExpr2);
888
993
  }
889
994
  case "CaseExpression": {
890
995
  let caseValue = expr.getChild("Expression");
@@ -919,83 +1024,28 @@ function analyzeExpression(expr, scope) {
919
1024
  }
920
1025
  case "SubqueryExpression":
921
1026
  default:
922
- return diag(expr, `Unsupported expression "${expr.name}": ${txt(expr)}`, errExpr);
1027
+ return diag(expr, `Unsupported expression "${expr.name}": ${txt(expr)}`, errExpr2);
923
1028
  }
924
1029
  }
925
- function analyzeFunctionCall(expr, scope) {
926
- let name = txt(expr.getChild("Identifier")).toLowerCase();
927
- let argNodes = expr.getChildren("Expression");
928
- let overload = findOverloads(name, config.dialect).find((o) => {
929
- return o.params.length == argNodes.length || !!o.params.find((p) => p.isVariadic);
930
- });
931
- let args = argNodes.map((node, idx) => {
932
- let firstType = overload?.params[idx]?.allowedTypes[0];
933
- if (firstType?.type === "sql native" && firstType?.rawType === "kw") {
934
- return { node: "genericSQLExpr", kids: { args: [] }, type: "sql native", src: [txt(node)], isAgg: false };
935
- } else {
936
- let argExpr = analyzeExpression(node, scope);
937
- let allowed = overload?.params[idx]?.allowedTypes.map((at) => at.type);
938
- if (allowed) checkTypes(argExpr, allowed, node);
939
- return argExpr;
940
- }
941
- });
942
- let type = overload?.returnType.type;
943
- if (type == "generic") type = args[0]?.type || "string";
944
- if (type && !isSupportedType(type)) {
945
- return diag(expr, `Unsupported function return type ${type} from function ${name}`, errExpr);
946
- }
947
- let structPaths = /* @__PURE__ */ new Set();
948
- args.forEach((a) => walkExpression(a, (e) => {
949
- if (e.node != "field") return;
950
- structPaths.add(e.path.slice(0, -1).join(".") || scope.table.name);
951
- }));
952
- let ret;
953
- if (["count", "min", "max", "avg", "sum"].includes(name.toLowerCase())) {
954
- let type2 = "number", typeDef;
955
- if (["min", "max", "avg"].includes(name.toLowerCase())) {
956
- type2 = args[0].type;
957
- typeDef = args[0].typeDef;
958
- }
959
- ret = { node: "aggregate", function: name, e: args[0], type: type2, typeDef, isAgg: true };
960
- } else if (overload && type) {
961
- ret = {
962
- node: "function_call",
963
- type,
964
- name,
965
- overload,
966
- expressionType: overload.returnType.expressionType || "scalar",
967
- kids: { args },
968
- isAgg: overload.returnType.expressionType == "aggregate" || args.some((a) => a.isAgg)
969
- };
970
- } else {
971
- return diag(expr, `Unknown function: ${name}`, errExpr);
972
- }
973
- if (structPaths.size > 1 && (ret.node == "aggregate" || ret.expressionType == "aggregate")) {
974
- return diag(expr, "Graphene only supports a single table within aggregates. This one has: " + Array.from(structPaths).join(", "), errExpr);
975
- }
976
- let foriegnPaths = Array.from(structPaths).filter((p) => p != scope.table.name);
977
- if (foriegnPaths.length > 0) ret.structPath = foriegnPaths[0].split(".");
978
- return ret;
979
- }
980
1030
  function analyzeTimeExpression(op, left2, right2, node) {
981
- if (left2.type !== "date" && left2.type !== "timestamp") return diag(node, "Expected left side to be a date or timestamp", errExpr);
1031
+ if (left2.type !== "date" && left2.type !== "timestamp") return diag(node, "Expected left side to be a date or timestamp", errExpr2);
982
1032
  let units = left2.type === "timestamp" ? "second" : "day";
983
1033
  if (right2.node == "stringLiteral") {
984
1034
  units = parseTemporal(right2);
985
1035
  if (right2.node == "stringLiteral") {
986
- return diag(node, "Could not parse interval", errExpr);
1036
+ return diag(node, "Could not parse interval", errExpr2);
987
1037
  }
988
1038
  }
989
1039
  if (right2.type == "date" || right2.type == "timestamp") {
990
- if (op !== "-") return diag(node, "Only subtraction between dates is supported", errExpr);
991
- if (right2.type !== left2.type) return diag(node, `Expected right side to be a ${left2.type}`, errExpr);
1040
+ if (op !== "-") return diag(node, "Only subtraction between dates is supported", errExpr2);
1041
+ if (right2.type !== left2.type) return diag(node, `Expected right side to be a ${left2.type}`, errExpr2);
992
1042
  return { node: "timeDiff", kids: { left: left2, right: right2 }, units, type: "interval", isAgg: false };
993
1043
  }
994
1044
  if (right2.type == "interval") {
995
1045
  let typeDef = { type: left2.type };
996
1046
  return { node: "delta", kids: { base: left2, delta: right2 }, op, units, type: left2.type, typeDef, isAgg: false };
997
1047
  }
998
- return diag(node, "Expected right side to be a date or interval", errExpr);
1048
+ return diag(node, "Expected right side to be a date or interval", errExpr2);
999
1049
  }
1000
1050
  function ensureSameType(left2, leftNode, right2, rightNode) {
1001
1051
  if (left2.type === "error" || right2.type === "error") return;
@@ -1021,10 +1071,6 @@ function checkTypes(expr, expected, node) {
1021
1071
  return Object.assign(expr, { node: "numberLiteral", literal: parsed.quantity.toString(), type: "interval", intervalUnit: parsed.unit });
1022
1072
  } else diag(node, `Expected types: ${expected.join(", ")}`);
1023
1073
  }
1024
- function isSupportedType(value) {
1025
- let supported = ["string", "number", "boolean", "date", "timestamp", "json", "sql native", "error", "array", "record", "null", "generic", "interval"];
1026
- return supported.includes(value);
1027
- }
1028
1074
  function lookupField(expr, scope) {
1029
1075
  let pathNodes = expr.getChildren("Identifier");
1030
1076
  let fieldNode = pathNodes.pop();
@@ -1124,6 +1170,8 @@ function convertDataType(dataType) {
1124
1170
  return "number";
1125
1171
  case "FLOAT64":
1126
1172
  return "number";
1173
+ case "BOOL":
1174
+ return "boolean";
1127
1175
  case "BOOLEAN":
1128
1176
  return "boolean";
1129
1177
  case "DATE":
@@ -1156,7 +1204,7 @@ function convertDataType(dataType) {
1156
1204
  return null;
1157
1205
  }
1158
1206
  }
1159
- var FILE_MAP, diagnostics, TABLE_NODE_MAP, FIELD_NODE_MAP, NODE_ENTITY_MAP, analysisQueue, errExpr;
1207
+ var FILE_MAP, diagnostics, TABLE_NODE_MAP, FIELD_NODE_MAP, NODE_ENTITY_MAP, analysisQueue, errExpr2;
1160
1208
  var init_analyze = __esm({
1161
1209
  "../lang/analyze.ts"() {
1162
1210
  "use strict";
@@ -1172,7 +1220,7 @@ var init_analyze = __esm({
1172
1220
  FIELD_NODE_MAP = /* @__PURE__ */ new WeakMap();
1173
1221
  NODE_ENTITY_MAP = new NodeWeakMap();
1174
1222
  analysisQueue = /* @__PURE__ */ new Set();
1175
- errExpr = { node: "error", type: "error" };
1223
+ errExpr2 = { node: "error", type: "error" };
1176
1224
  }
1177
1225
  });
1178
1226
 
@@ -1182,10 +1230,10 @@ var init_parser_terms = __esm({
1182
1230
  "../lang/parser.terms.js"() {
1183
1231
  "use strict";
1184
1232
  table = 6;
1185
- primary_key = 11;
1186
- join = 15;
1187
- as = 21;
1188
- on = 24;
1233
+ primary_key = 12;
1234
+ join = 16;
1235
+ as = 22;
1236
+ on = 25;
1189
1237
  or = 31;
1190
1238
  and = 34;
1191
1239
  like = 43;
@@ -1266,24 +1314,24 @@ var init_parser = __esm({
1266
1314
  "../lang/parser.js"() {
1267
1315
  "use strict";
1268
1316
  init_tokens();
1269
- spec_Identifier = { __proto__: null, table: 12, primary_key: 22, join: 30, one: 34, many: 38, as: 42, on: 48, or: 62, and: 68, like: 86, not: 90, in: 110, from: 120, inner: 132, left: 136, right: 140, full: 144, cross: 148, select: 154, distinct: 158, where: 168, group: 174, by: 178, having: 184, order: 190, asc: 198, desc: 202, limit: 208, offset: 212, is: 218, null: 222, case: 232, when: 238, then: 242, else: 248, end: 252, exists: 258, true: 266, false: 270, count: 280, extract: 286, extend: 306 };
1317
+ spec_Identifier = { __proto__: null, table: 12, primary_key: 24, join: 32, one: 36, many: 40, as: 44, on: 50, or: 62, and: 68, like: 86, not: 90, in: 110, from: 120, inner: 132, left: 136, right: 140, full: 144, cross: 148, select: 154, distinct: 158, where: 168, group: 174, by: 178, having: 184, order: 190, asc: 198, desc: 202, limit: 208, offset: 212, is: 218, null: 222, case: 232, when: 238, then: 242, else: 248, end: 252, exists: 258, true: 266, false: 270, count: 280, extract: 286, extend: 306 };
1270
1318
  parser = LRParser.deserialize({
1271
1319
  version: 14,
1272
- states: "G`QYQPOOOOQO'#C`'#C`OOQO'#Di'#DiOwQPO'#DhOOQO'#Dz'#DzO#TQPO'#DyOOQO'#ER'#EROOQO'#EU'#EUO#[QPO'#ETOOQO'#EZ'#EZOOQO'#E^'#E^O#[QPO'#E]OOQO'#Eg'#EgO#aQPO'#EfOOQO'#Fp'#FpO#}QPO'#DgO$[QPO'#C_OOQO'#Fj'#FjO$aQPO'#FiO$fQPO'#FlQYQPOOQ%ZQPO'#FlO%`QPO'#EQO%`QPO'#EYO&aQPO'#DkO#fQPO'#DmO'nQPO'#DlOOQO'#GP'#GPO)aQPO,5:SO+hQPO'#CvO-pQPO'#CvOOQO'#DY'#DYO-xQPO'#DmOOQO'#D|'#D|OOQO'#EP'#EPOOQO'#EO'#EOO.cQPO,5:eO!PQPO,5:eO0eQPO'#EOOOQO'#En'#EnOOQO'#Eq'#EqOOQO'#Es'#EsO1_QPO'#ErOOQO'#FQ'#FQO1fQPO'#FPOOQO'#FU'#FUOOQO'#FW'#FWOOQO'#FT'#FTOOQO'#FY'#FYOOQO'#GT'#GTOOQO'#F]'#F]O1kQPO'#F[OOQO'#GS'#GSOOQO'#F`'#F`OOQO'#GR'#GROOQO'#GU'#GUOOQO'#F}'#F}O%`QPO'#EpO1pQPO'#F_OOQO'#EW'#EWO!PQPO,5:oO1uQPO,5:wO1}QPO,5;QOOQO-E9n-E9nO2rQPO,58yO2zQPO,5<TOOQO,5<W,5<WOOQO-E9j-E9jO3PQPO'#CvO3UQPO,5:lO3xQPO,5:tOOQO'#Cp'#CpOOQO'#Cr'#CrOOQO,5:V,5:VO4lQPO,5:VO4qQPO,5:XOOQO,5:W,5:WO4lQPO,5:WOOQO'#Cj'#CjOOQO'#Do'#DoOOQO'#Dq'#DqOOQO'#Ds'#DsOOQO'#Du'#DuOOQO'#Dw'#DwOwQPO'#DnO4vQPO'#DnOOQO'#Fq'#FqO4{QPO1G/nOOQO,5<Y,5<YO5oQPO,5;uO5vQPO,59bOOQO-E9l-E9lOOQO,5:k,5:kO9[QPO,5<OO9cQPO1G0PO:VQPO1G0PO:VQPO1G0POOQO'#Cz'#CzOOQO'#C}'#C}OOQO'#DW'#DWOOQO'#DP'#DPO:zQPO,59}OOQO'#D['#D[OOQO'#D_'#D_OOQO'#Dd'#DdO;SQPO,59}OOQO'#El'#ElO;XQPO,5;VO%`QPO,59hO%`QPO,59hO%`QPO,59hO%`QPO,59hO%`QPO,59hOOQO,5:j,5:jO4lQPO,5:jO;aQPO,5;^OOQO'#Ev'#EvOOQO'#Ft'#FtO;hQPO,5;^O%`QPO'#EuO#fQPO,5;kO;sQPO,5;vOOQO,5;[,5;[O<QQPO,5;yO<YQPO1G0ZO<}QPO'#E`O=xQPO1G0cOOQO'#Ei'#EiO>mQPO1G0lO>rQPO1G.eO?sQPO1G1nO?xQPO1G1oOOQO1G/q1G/qOOQO1G/s1G/sOOQO1G/r1G/rO@yQPO,5:YOwQPO,5:YOOQO-E9o-E9oOBQQPO1G1aOOQO1G1a1G1aPB[QPO'#FnOOQO1G1j1G1jOOQO,5<^,5<^OBaQPO7+%kOOQO-E9p-E9pOCTQPO7+%kOOQO,59k,59kOCxQPO1G/iO-xQPO1G/iOOQO1G0q1G0qO;[QPO1G0qOG]QPO1G/SOGdQPO1G/SOJvQPO1G/SOKQQPO1G/SOOQO1G/S1G/SOOQO1G0U1G0UO;hQPO1G0xOOQO-E9r-E9rOOQO'#E{'#E{OOQO'#E}'#E}OOQO1G0x1G0xO;nQPO1G0xO%`QPO'#EzOK[QPO,5;aOKcQPO1G1VOKhQPO1G1bOOQO1G1b1G1bOKoQPO1G1bO%`QPO1G1bOOQO'#Fb'#FbOKtQPO1G1eOKyQPO7+%uOLmQPO7+%uOOQO'#Eb'#EbOOQO'#Ed'#EdOOQO,5:z,5:zONSQPO7+%}ON^QPO7+%}OOQO7+&W7+&WONeQPO'#CrONoQPO'#CiONwQPO'#ChO/WQPO'#CxOOQO'#F|'#F|OOQO'#Fz'#FzON|QPO'#FmO!!TQPO7+$PO!![QPO'#CxO#fQPO7+'YONhQPO'#CrOOQO'#GW'#GWO!!aQPO'#FuO!#hQPO7+'ZOOQO'#Cs'#CsO%`QPO1G/tO!#oQPO1G/tO!$vQPO7+&{O!%OQPO7+&{OOQO7+&{7+&{P!PQPO'#FrO!%VQPO<<IVO-xQPO7+%TO!%yQPO'#DfO!&TQPO7+%TOOQO7+&]7+&]OOQO7+&d7+&dO;nQPO7+&dO!&YQPO,5;fOOQO'#Ex'#ExO%`QPO1G0{OOQO7+&q7+&qOOQO7+&|7+&|O!&aQPO7+&|O%`QPO7+'PO!&hQPO<<IaOOQO,5<_,5<_O!'[QPO<<IiOOQO-E9q-E9qOOQO'#Cd'#CdO!(SQPO,58}OOQO'#Cl'#ClOOQO'#Cn'#CnOOQO,59T,59TO!)^QPO,59SO:zQPO,5<PO!)fQPO,5<PO;XQPO,5<QO%`QPO,59eO%`QPO,59eO%`QPO,59eO%`QPO,59eO%`QPO,59eO4lQPO,59dOOQO,5<X,5<XOOQO-E9k-E9kOOQO<<Gk<<GkO%`QPO,59dO!)kQPO<<JtOOQO,5<a,5<aOOQO-E9s-E9sOOQO<<Ju<<JuO!)pQPO7+%`O%`QPO7+%`OOQO-E9m-E9mO!*vQPO<<JgOOQO<<Jg<<JgO!*}QPO,5<ZO!+XQPO<<HoO!+^QPO,5:QO!+fQPO,5:QOOQO<<Ho<<HoOOQO<<JO<<JOO!+mQPO7+&gOOQO<<Jh<<JhO!+zQPO<<JkP1uQPO'#FsOOQO'#Cf'#CfOOQO'#Ce'#CeOOQO1G.i1G.iO!,RQPO1G.nO4lQPO1G.nO!,WQPO1G1kO-xQPO1G1kOOQO1G1l1G1lO;[QPO1G1lO!-gQPO1G/PO!-nQPO1G/PO!.|QPO1G/PO!/WQPO1G/POOQO1G/P1G/POOQO1G/O1G/OO!/bQPO1G/OOOQOAN@`AN@`O!0bQPO<<HzP%`QPO'#FoOOQOAN@RAN@ROOQOAN>ZAN>ZO!1hQPO1G/lOOQOAN@VAN@VO!1oQPO'#CvO!2|QPO'#CuOOQO7+$Y7+$YO!)aQPO7+$YO-xQPO7+'VO!3RQPO7+'VOOQO7+'W7+'WO!,RQPO,59aO!,RQPO<<GtO!3WQPO<<JqOOQO<<Jq<<JqOOQO1G.{1G.{OOQOAN=`AN=`OOQOAN@]AN@]",
1273
- stateData: "!3b~O$lOSPOS~OUPO!^QO!oSO!vUO!yVO#OXO#RYO#[[O$_aO~OThO$miO~OTmO}oO!PxO!QxO!SrO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO$mpO~O!qqO~P!PO!{!]O~O#T!`O~O!^QO!oSO!vUO!yVO#OXO#RYO#[[O~O$j!ZX$y!ZX$t!ZX~P#fOT!bO~OT!cO~O$y!dOU$`X!^$`X!o$`X!v$`X!y$`X#O$`X#R$`X#[$`X$_$`X$j$`X~O$y!dO~OTmO}oO!PxO!QxO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO$mpO~OT!jOe!iO_!_X!^!_X!d!_X!f!_X!h!_X!j!_X!l!_X!o!_X!v!_X!y!_X#O!_X#R!_X#[!_X$j!_X$y!_X$t!_Xh!_X~OT!jOe!iO_!`X!^!`X!d!`X!f!`X!h!`X!j!`X!l!`X!o!`X!v!`X!y!`X#O!`X#R!`X#[!`X$j!`X$y!`X$t!`Xh!`X~O_!pO!d!qO!f!rO!h!sO!j!tO!l!uO~O!^![a!o![a!v![a!y![a#O![a#R![a#[![a$j![a$y![a$t![a~P({O$m!{O$o!zOejXkjXojXrjXtjXujXvjXwjXxjXyjX{jX}jX!PjX!QjX!SjX!TjX!UjX!XjX#ajX~OTjX!^jX!ojX!vjX!yjX#OjX#RjX#[jX$jjX$rjX$yjX$tjX#kjX#mjX#rjX_jX!djX!fjX!hjX!jjX!ljX#pjX#TjX#cjX#hjX#ujX#vjX#yjX#{jX$QjX$TjX$VjX~P*TOT!|O!S#OO~O!^QO!oSO!vUO!yVO#OXO#RYO#[[O~P%`O$r#QO!^!ma!o!ma!v!ma!y!ma#O!ma#R!ma#[!ma$j!ma$y!ma$t!ma~Oe!iOk#WOo#TOr#UOt#WOu#WOv#WOw#WOx#WOy#WO{#VO}oO!P#YO!Q#YO!S#ZO!T#ZO!U#ZO!X#[O#a#^O~OT!jO!^!rX!o!rX!v!rX!y!rX#O!rX#R!rX#[!rX$j!rX$r!rX$y!rX$t!rX~P/WO#k#hO~P%`O$m#lO~O$m#mO~O$m#oO~OT#qO#T#qO~O#^#sO!^#Ya!o#Ya!v#Ya!y#Ya#O#Ya#R#Ya#[#Ya$j#Ya$y#Ya$t#Ya~Oe!iO$m#uO~O$m#wO~OT!|O~O!^!ta!o!ta!v!ta!y!ta#O!ta#R!ta#[!ta$j!ta$y!ta$t!ta~P/ZO!^!|a!o!|a!v!|a!y!|a#O!|a#R!|a#[!|a$j!|a$y!|a$t!|a~P/ZOT!jO~O$t#yO~O_!pO~O!^![i!o![i!v![i!y![i#O![i#R![i#[![i$j![i$y![i$t![i~P({O$t$PO~P%`O$o!zOTjaejakjaojarjatjaujavjawjaxjayja{ja}ja!Pja!Qja!Sja!Tja!Uja!Xja!^ja!oja!vja!yja#Oja#Rja#[ja#aja$jja$rja$yja$tja#kja#mja#rja_ja!dja!fja!hja!jja!lja#pja#Tja#cja#hja#uja#vja#yja#{ja$Qja$Tja$Vja~O$t$RO~P/ZO!^!mi!o!mi!v!mi!y!mi#O!mi#R!mi#[!mi$j!mi$y!mi$t!mi~P!PO$r$TO!^!mi!o!mi!v!mi!y!mi#O!mi#R!mi#[!mi$j!mi$y!mi$t!mi~O{#VO!X#[O~O$m$YO~O}oO#cwO~O#k#hO~P/ZO#k#hO#p$eO#r$fO~O!S$nO!qqO$t$mO~P%`OT$pO#v$pO~O$r$rO!^!wi!o!wi!v!wi!y!wi#O!wi#R!wi#[!wi$j!wi$y!wi$t!wi~O#V$tO#X$uO!^#SX!o#SX!v#SX!y#SX#O#SX#R#SX#[#SX$j#SX$r#SX$y#SX$t#SX~O$r$wO!^#Pi!o#Pi!v#Pi!y#Pi#O#Pi#R#Pi#[#Pi$j#Pi$y#Pi$t#Pi~O#T$yO~OT$zO_!pO}oO!PxO!QxO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO~O$m%TO~OT%UO_!pO}oO!PxO!QxO#T!RO#cwO#hyO#u{O#v!RO#y}O#{!OO$Q!SO$T!VO$V!WO~Oh%YO_!ba!^!ba!d!ba!f!ba!h!ba!j!ba!l!ba!o!ba!v!ba!y!ba#O!ba#R!ba#[!ba$j!ba$y!ba$t!ba~O$r%^O$t%_O~P/ZO$o!zO~O!^!mq!o!mq!v!mq!y!mq#O!mq#R!mq#[!mq$j!mq$y!mq$t!mq~P!PO$r%aO!^!mq!o!mq!v!mq!y!mq#O!mq#R!mq#[!mq$j!mq$y!mq$t!mq~O$m%bO~Ok#WOt#WOu#WOv#WOw#WOx#WOy#WO{#VO}oO!P#YO!Q#YO!S#ZO!T#ZO!U#ZO!X#[O#a#^OTpiepiopi!^pi!opi!vpi!ypi#Opi#Rpi#[pi$jpi$rpi$ypi$tpi#kpi#mpi#rpi_pi!dpi!fpi!hpi!jpi!lpi#ppi#Tpi#cpi#hpi#upi#vpi#ypi#{pi$Qpi$Tpi$Vpi~Or#UO~PC}Orpi~PC}O!S#ZO!T#ZO!U#ZOTpiepikpiopirpitpiupivpiwpixpiypi{pi}pi!Xpi!^pi!opi!vpi!ypi#Opi#Rpi#[pi#api$jpi$rpi$ypi$tpi#kpi#mpi#rpi_pi!dpi!fpi!hpi!jpi!lpi#ppi#Tpi#cpi#hpi#upi#vpi#ypi#{pi$Qpi$Tpi$Vpi~O!P#YO!Q#YO~PGkO!Ppi!Qpi~PGkO#m%iO~P/ZO$t%kO~O$t%lO~P/ZO$t%lO~O!^QO~O!^!wq!o!wq!v!wq!y!wq#O!wq#R!wq#[!wq$j!wq$y!wq$t!wq~P!PO$r%oO!^!wq!o!wq!v!wq!y!wq#O!wq#R!wq#[!wq$j!wq$y!wq$t!wq~O!^#Pq!o#Pq!v#Pq!y#Pq#O#Pq#R#Pq#[#Pq$j#Pq$y#Pq$t#Pq~OT#qO#T#qO~PMbO$r%qO~PMbOT%sO$ZfX~P*TOa%uOc%vO~OT%xO~O$r&SOT$aX_$aX}$aX!P$aX!Q$aX#T$aX#c$aX#h$aX#u$aX#v$aX#y$aX#{$aX$Q$aX$T$aX$V$aX$t$aX~O$t&UO~P>rO$Z&VO~O$r&XOT$iX_$iX}$iX!P$iX!Q$iX#T$iX#c$iX#h$iX#u$iX#v$iX#y$iX#{$iX$Q$iX$T$iX$V$iX$t$iX~O$t&ZO~P?xOh%YO_!bi!^!bi!d!bi!f!bi!h!bi!j!bi!l!bi!o!bi!v!bi!y!bi#O!bi#R!bi#[!bi$j!bi$y!bi$t!bi~O$r&_O$t&`O~O$t&`O~P%`O!^!my!o!my!v!my!y!my#O!my#R!my#[!my$j!my$y!my$t!my~P!PO$r&dO$t!YX~P/ZO$t&eO~O#r#na~P/ZO$t&hO~P/ZO!^!wy!o!wy!v!wy!y!wy#O!wy#R!wy#[!wy$j!wy$y!wy$t!wy~P!POT#qO#T#qO!^#Py!o#Py!v#Py!y#Py#O#Py#R#Py#[#Py$j#Py$y#Py$t#Py~OZ&kOTVa_Va}Va!PVa!QVa#TVa#cVa#hVa#uVa#vVa#yVa#{Va$QVa$TVa$VVa$rVa$tVa~Oe!iOh%YO~O$m&qO~O$t&{O~O_!bq!^!bq!d!bq!f!bq!h!bq!j!bq!l!bq!o!bq!v!bq!y!bq#O!bq#R!bq#[!bq$j!bq$y!bq$t!bq~P/ZO$t'OO~P%`O$r$ca$t$ca~P/ZO$t'PO~O$r'QO$t!Ya~O$t!Ya~P%`O#k#iq#p#iq#r#iq~P/ZO$t'RO~P/ZOT'SO~O$m'WO~Ok#WOt#WOu#WOv#WOw#WOx#WOy#WO{#VO}oO!P#YO!Q#YO!S#ZO!T#ZO!U#ZO!X#[O#a#^Oemiomi~Or#UO~P!,]Ormi~P!,]O!S#ZO!T#ZO!U#ZOemikmiomirmitmiumivmiwmixmiymi{mi}mi!Xmi#ami~O!P#YO!Q#YO~P!-uO!Pmi!Qmi~P!-uOTli_li#Tli#cli#hli#uli#vli#yli#{li$Qli$Tli$Vli$rli$tli~P/ZO_!by!^!by!d!by!f!by!h!by!j!by!l!by!o!by!v!by!y!by#O!by#R!by#[!by$j!by$y!by$t!by~P/ZO$t!Yi~P%`O$o!zOkjXTjX_jX}jX!PjX!QjX#TjX#cjX#hjX#ujX#vjX#yjX#{jX$QjX$TjX$VjX$rjX$tjX~Ok'ZO~O$t'^O~O$t'aO~OP!T!S!Q~",
1274
- goto: "Hi${PPP$|%QPP%U%Y%]%`P%c%k%qP&OP&OP&RP&e'TP'a'gP%c(q(wP)_*^P*vPPPPPP+bP,OP-sPP.aPPP)_/PP/q/}0i0vP1W1W1]2a2eP2eP2eP2eP2eP0i2iP2vP2|3_0i3jP0i3wP4UP0i4[P0i4iP4vP5OP5OP0i5RP5`P)_5cP5}P7^8a7^9dP:g:mP:sP:v:|P;QP7^;[PP<_=bP=bP<_>e>e?hP7^@kPAnP1b(q(qP$|$|AqPAuA{BRC_CiCxDOD^DdDnPPPPDtPDxEOPGVPG`7^>e)_PHeTcOdT`OdT%P#u%RR%t$zR&m%tR&l%tS%P#u%RT%V#w%XX$|#u#w%R%XS!vl!yQ#|!wX${#u#w%R%XR%w${Q!lhQ!ojQ#fvQ#v!bQ&R$}R&o%xQ!khQ!njQ#evQ#x!lQ#z!oQ$b#fW%S#u#w%R%XQ&y&RR'V&oQ%Z#{Q&]%[Q&n%xR'['VQ'U&nR'`'[#Q!UTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WS'T&n'[R'_'ZX%O#u#w%R%Xr#`v!g!h#P#g$O$j$l%c%h%m&[&a&g&i&z&|R%|$}!y!YTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m$T$Y$i$o$r%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'Wv#av!g!h#P#g$O$]$j$l%c%h%m&[&a&g&i&t&z&|R%}$}z#bv!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|R&O$}|#Wv!g!h#P#g$O$]$^$j$l$}%c%h%m&[&a&g&i&t&u&z&|T$W#X%y#QxTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'Wz#Xv!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|Q$[#_Q%y$}R&s%{!O#cv!g!h#P#g$O$]$^$_$j$l%c%h%m&[&a&g&i&t&u&v&z&|R&P$}!S#dv!g!h#P#g$O$]$^$_$`$j$l%c%h%m&[&a&g&i&t&u&v&w&z&|R&Q$}z#]v!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|Q$X#XQ%z$}R&p%yQ%d$YQ&b%bQ'X&qR']'WSeOdS!mipQ$k#lQ%d$YQ&W%TQ&b%bQ'X&qR']'Wg^O_dip#l$Y%T%b&q'WfRO_dip#l$Y%T%b&q'WR%n$qVkR!v#|UjR!v#|!y!XTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m$T$Y$i$o$r%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WT!xl!yT!wl!ygTO_dip#l$Y%T%b&q'WQuTR$o#mQtTQ#SuQ#p!^]$S#Q$T$r%`%a%ocsTu!^#Q$T$r%`%a%ogfO_dip#l$Y%T%b&q'WgWO_dip#l$Y%T%b&q'WQ!^WR!_ZggO_dip#l$Y%T%b&q'WgZO_dip#l$Y%T%b&q'WQ#r!_V%p$w%q&jR$v#qg]O_dip#l$Y%T%b&q'WR#t!`z#_v!g!h#P#g$O$]$^$j$l%c%h%m&[&a&g&i&t&u&z&|R%{$}#Q!QTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WQ$Z#_Q%e$[Q&r%{R'Y&s#R!WTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!ZTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#RzTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WX#iz#g#j$cX#kz#g#j$cR%j$jQ$h#jR%g$cT$i#j$cQ$g#jS%f$c$hR&f%g#R|Tfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!RTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!PTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!UTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R!TTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'W#R![Tfgpuz!Z!^!{#Q#`#a#b#c#d#k#m#u#w$T$Y$i$o$r%R%X%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WR$q#oTbOdQdOR!edQ%R#uR&T%RbnTu!^#Q$T$r%`%a%o!t!ffgpz!Z!{#`#a#b#c#d#k#m#u#w$Y$i$o%R%X%Z%^%b%j%n%|%}&O&P&Q&V&]&_&d&n&q&}'Q'W'Z'[T!}n!fQ%]$OS&^%]&cR&c%cd_Odip#l$Y%T%b&q'WR!a_Q!ylR#}!yQ#RtU$U#R$V$sQ$V#SR$s#pQ$x#rR%r$xQ#jzQ$c#gT$d#j$cQ%X#wR&Y%XT%Q#u%RX$}#u#w%R%XbvTu!^#Q$T$r%`%a%oQ!gfQ!hgQ#PpQ#gzQ#n!ZQ$O!{Q$]#`Q$^#aQ$_#bQ$`#cQ$a#dQ$j#kQ$l#mW%c$Y%b&q'WQ%h$iQ%m$oQ&[%ZY&a%^&_&d&}'QQ&g%jQ&i%nQ&t%|Q&u%}Q&v&OQ&w&PQ&x&QQ&z&VR&|&]QlRQ#{!vR%[#|!x!YTfgpuz!Z!^!{#Q#`#a#b#c#d#k#m$T$Y$i$o$r%Z%^%`%a%b%j%n%o%|%}&O&P&Q&V&]&_&d&q&}'Q'WX%O#u#w%R%XT%W#w%X",
1275
- nodeNames: "\u26A0 Comment Program TableStatement Kw Identifier table ColumnDef DataType PrimaryKey Kw primary_key JoinDef JoinType Kw join Kw one Kw many Kw as Alias Kw on BinaryExpression Ref = ComputedDef BinaryExpression Kw or BinaryExpression Kw and ComparisonOp != <> < > <= >= Kw like Kw not AddOp + - MulOp * / % InExpression Kw in InValueList QueryStatement FromClause Kw from TableName Subquery SubqueryExpression JoinClause Kw inner Kw left Kw right Kw full Kw cross SelectClause Kw select Kw distinct SelectItem Wildcard WhereClause Kw where GroupByClause Kw group Kw by HavingClause Kw having OrderByClause Kw order OrderItem Number Kw asc Kw desc LimitClause Kw limit Kw offset NullTestExpression Kw is Kw null UnaryExpression UnaryOperator CaseExpression Kw case WhenClause Kw when Kw then ElseClause Kw else Kw end ExistsExpression Kw exists String Boolean Kw true Kw false Null FunctionCall Count Kw count ExtractExpression Kw extract ExtractUnit Param Parenthetical InExpression NullTestExpression : ViewStatement ExtendStatement Kw extend",
1320
+ states: "G`QYQPOOOOQO'#C`'#C`OOQO'#Di'#DiOwQPO'#DhOOQO'#Dz'#DzO#TQPO'#DyOOQO'#ER'#EROOQO'#EU'#EUO#[QPO'#ETOOQO'#EZ'#EZOOQO'#E^'#E^O#[QPO'#E]OOQO'#Eg'#EgO#aQPO'#EfOOQO'#Fp'#FpO#}QPO'#DgO$[QPO'#C_OOQO'#Fj'#FjO$[QPO'#FiO$aQPO'#FlQYQPOOQ%UQPO'#FlO%ZQPO'#EQO%ZQPO'#EYO(hQPO'#CcO(rQPO'#CcO(wQPO'#DkO#fQPO'#DmO*UQPO'#DlOOQO'#GP'#GPO+wQPO,5:SO,kQPO'#CcO-zQPO'#CcOOQO'#DY'#DYO.SQPO'#DmOOQO'#D|'#D|OOQO'#EP'#EPOOQO'#EO'#EOO.mQPO,5:eO!PQPO,5:eO0oQPO'#EOOOQO'#En'#EnOOQO'#Eq'#EqOOQO'#Es'#EsO1iQPO'#ErOOQO'#FQ'#FQO1pQPO'#FPOOQO'#FU'#FUOOQO'#FW'#FWOOQO'#FT'#FTOOQO'#FY'#FYOOQO'#GT'#GTOOQO'#F]'#F]O1uQPO'#F[OOQO'#GS'#GSOOQO'#F`'#F`OOQO'#GR'#GROOQO'#GU'#GUOOQO'#F}'#F}O%ZQPO'#EpO1zQPO'#F_OOQO'#EW'#EWO!PQPO,5:oO2PQPO,5:wO2XQPO,5;QOOQO-E9n-E9nO2|QPO,58yO3UQPO,5<TOOQO,5<W,5<WOOQO-E9j-E9jO3ZQPO,5:lO3}QPO,5:tOOQO,5<X,5<XO4qQPO,58}OOQO-E9k-E9kOOQO'#Cq'#CqOOQO'#Cs'#CsOOQO,5:V,5:VO8]QPO,5:VO8bQPO,5:XOOQO,5:W,5:WO8]QPO,5:WOOQO'#Ck'#CkOOQO'#Do'#DoOOQO'#Dq'#DqOOQO'#Ds'#DsOOQO'#Du'#DuOOQO'#Dw'#DwOwQPO'#DnO8gQPO'#DnOOQO'#Fq'#FqO8lQPO1G/nO9`QPO,5;uOOQO,5:k,5:kO9gQPO,5<OO9nQPO1G0PO:bQPO1G0PO:bQPO1G0POOQO'#Cz'#CzOOQO'#C}'#C}OOQO'#DW'#DWOOQO'#DP'#DPO;VQPO,59}OOQO'#D['#D[OOQO'#D_'#D_OOQO'#Dd'#DdO;_QPO,59}OOQO'#El'#ElO;dQPO,5;VO%ZQPO,59hO%ZQPO,59hO%ZQPO,59hO%ZQPO,59hO%ZQPO,59hOOQO,5:j,5:jO8]QPO,5:jO;lQPO,5;^OOQO'#Ev'#EvOOQO'#Ft'#FtO;sQPO,5;^O%ZQPO'#EuO#fQPO,5;kO<OQPO,5;vOOQO,5;[,5;[O<]QPO,5;yO<eQPO1G0ZO=YQPO'#E`O>TQPO1G0cOOQO'#Ei'#EiO>xQPO1G0lO>}QPO1G.eO@OQPO1G1nO@TQPO1G1oPAUQPO'#FmOOQO1G/q1G/qOOQO1G/s1G/sOOQO1G/r1G/rOAZQPO,5:YOwQPO,5:YOOQO-E9o-E9oOBbQPO1G1aOOQO1G1a1G1aOOQO1G1j1G1jOOQO,5<^,5<^OBlQPO7+%kOOQO-E9p-E9pOC`QPO7+%kOOQO,59k,59kODTQPO1G/iO.SQPO1G/iOOQO1G0q1G0qO;gQPO1G0qOGhQPO1G/SOGoQPO1G/SOKRQPO1G/SOK]QPO1G/SOOQO1G/S1G/SOOQO1G0U1G0UO;sQPO1G0xOOQO-E9r-E9rOOQO'#E{'#E{OOQO'#E}'#E}OOQO1G0x1G0xO;yQPO1G0xO%ZQPO'#EzOKgQPO,5;aOKnQPO1G1VOKsQPO1G1bOOQO1G1b1G1bOKzQPO1G1bO%ZQPO1G1bOOQO'#Fb'#FbOLPQPO1G1eOLUQPO7+%uOLxQPO7+%uOOQO'#Eb'#EbOOQO'#Ed'#EdOOQO,5:z,5:zON_QPO7+%}ONiQPO7+%}OOQO7+&W7+&WO!!WQPO'#CcO!!_QPO'#CjO$[QPO'#CiO/bQPO'#CxOOQO'#F|'#F|OOQO'#F{'#F{O!!gQPO'#FnO!#nQPO7+$PO!#uQPO'#CxO#fQPO7+'YONpQPO'#CcOOQO'#GW'#GWO!#zQPO'#FuO!%RQPO7+'ZOOQO'#Ct'#CtO%ZQPO1G/tO!%YQPO1G/tO!&aQPO7+&{O!&iQPO7+&{OOQO7+&{7+&{P!PQPO'#FrO!&pQPO<<IVO.SQPO7+%TO!'dQPO'#DfO!'nQPO7+%TOOQO7+&]7+&]OOQO7+&d7+&dO;yQPO7+&dO!'sQPO,5;fOOQO'#Ex'#ExO%ZQPO1G0{OOQO7+&q7+&qOOQO7+&|7+&|O!'zQPO7+&|O%ZQPO7+'PO!(RQPO<<IaOOQO,5<_,5<_O!(uQPO<<IiOOQO-E9q-E9qOOQO'#Ce'#CeO!)mQPO,59OOOQO'#Cm'#CmOOQO'#Co'#CoOOQO,59U,59UO!*wQPO,59TO;VQPO,5<PO!+PQPO,5<PO;dQPO,5<QO%ZQPO,59eO%ZQPO,59eO%ZQPO,59eO%ZQPO,59eO%ZQPO,59eO8]QPO,59dOOQO,5<Y,5<YOOQO-E9l-E9lOOQO<<Gk<<GkO%ZQPO,59dO!+UQPO<<JtOOQO,5<a,5<aOOQO-E9s-E9sOOQO<<Ju<<JuO!+ZQPO7+%`O%ZQPO7+%`OOQO-E9m-E9mO!,aQPO<<JgOOQO<<Jg<<JgO!,hQPO,5<ZO!,rQPO<<HoO!,wQPO,5:QO!-PQPO,5:QOOQO<<Ho<<HoOOQO<<JO<<JOO!-WQPO7+&gOOQO<<Jh<<JhO!-eQPO<<JkP2PQPO'#FsOOQO'#Cg'#CgOOQO'#Cf'#CfOOQO1G.j1G.jO$[QPO1G.oO8]QPO1G.oO!-lQPO1G1kO.SQPO1G1kOOQO1G1l1G1lO;gQPO1G1lO!.{QPO1G/PO!/SQPO1G/PO!0bQPO1G/PO!0lQPO1G/POOQO1G/P1G/POOQO1G/O1G/OO!0vQPO1G/OOOQOAN@`AN@`O!1vQPO<<HzP%ZQPO'#FoOOQOAN@RAN@ROOQOAN>ZAN>ZO!2|QPO1G/lOOQOAN@VAN@VO!3TQPO'#CvOOQO7+$Z7+$ZO!*zQPO7+$ZO.SQPO7+'VO!3YQPO7+'VOOQO7+'W7+'WO$[QPO,59bO$[QPO<<GuO!3_QPO<<JqOOQO<<Jq<<JqOOQO1G.|1G.|OOQOAN=aAN=aOOQOAN@]AN@]",
1321
+ stateData: "!3i~O$lOSPOS~OUPO!^QO!oSO!vUO!yVO#OXO#RYO#[[O$_aO~OThO$nkO~OToO}qO!PzO!QzO!StO#T!TO#cyO#h{O#u}O#v!TO#y!PO#{!QO$Q!UO$T!XO$V!YO$nrO~O!qsO~P!PO!{!_O~O#T!bO~O!^QO!oSO!vUO!yVO#OXO#RYO#[[O~O$j!ZX$y!ZX$t!ZX~P#fOThO~O$y!fOU$`X!^$`X!o$`X!v$`X!y$`X#O$`X#R$`X#[$`X$_$`X$j$`X~O$y!fO~OToO}qO!PzO!QzO#T!TO#cyO#h{O#u}O#v!TO#y!PO#{!QO$Q!UO$T!XO$V!YO$nrO~O$m!jOTVX`VXfVX!^VX!dVX!fVX!hVX!jVX!lVX!oVX!vVX!yVX#OVX#RVX#[VX$jVX$yVX$tVXkVX}VX!PVX!QVX#TVX#cVX#hVX#uVX#vVX#yVX#{VX$QVX$TVX$VVX$rVX~O$nVXiVX~P&[OT!kO~OT!nOf!mO`!_X!^!_X!d!_X!f!_X!h!_X!j!_X!l!_X!o!_X!v!_X!y!_X#O!_X#R!_X#[!_X$j!_X$y!_X$t!_Xi!_X~OT!nOf!mO`!`X!^!`X!d!`X!f!`X!h!`X!j!`X!l!`X!o!`X!v!`X!y!`X#O!`X#R!`X#[!`X$j!`X$y!`X$t!`Xi!`X~O`!tO!d!uO!f!vO!h!wO!j!xO!l!yO~O!^![a!o![a!v![a!y![a#O![a#R![a#[![a$j![a$y![a$t![a~P+cO$n#OOoVXrVXtVXuVXvVXwVXxVXyVX{VX!SVX!TVX!UVX!XVX#aVX#kVX#mVX#rVX#pVX~P&[OT!kO!S#PO~O!^QO!oSO!vUO!yVO#OXO#RYO#[[O~P%ZO$r#RO!^!ma!o!ma!v!ma!y!ma#O!ma#R!ma#[!ma$j!ma$y!ma$t!ma~Of!mOk#XOo#UOr#VOt#XOu#XOv#XOw#XOx#XOy#XO{#WO}qO!P#ZO!Q#ZO!S#[O!T#[O!U#[O!X#]O#a#_O~OT!nO!^!rX!o!rX!v!rX!y!rX#O!rX#R!rX#[!rX$j!rX$r!rX$y!rX$t!rX~P/bO#k#iO~P%ZO$n#mO~O$n#nO~O$n#pO~OT#rO#T#rO~O#^#tO!^#Ya!o#Ya!v#Ya!y#Ya#O#Ya#R#Ya#[#Ya$j#Ya$y#Ya$t#Ya~Of!mO$n#vO~O$n#xO~O!^!ta!o!ta!v!ta!y!ta#O!ta#R!ta#[!ta$j!ta$y!ta$t!ta~P/eO!^!|a!o!|a!v!|a!y!|a#O!|a#R!|a#[!|a$j!|a$y!|a$t!|a~P/eO$m!jOTVa`VafVa!^Va!dVa!fVa!hVa!jVa!lVa!oVa!vVa!yVa#OVa#RVa#[Va$jVa$yVakVaoVarVatVauVavVawVaxVayVa{Va}Va!PVa!QVa!SVa!TVa!UVa!XVa#aVa$rVa$nVa$tVa#kVaiVa#mVa#rVa#pVa#TVa#cVa#hVa#uVa#vVa#yVa#{Va$QVa$TVa$VVa~OT!nO~O$t#{O~O`!tO~O!^![i!o![i!v![i!y![i#O![i#R![i#[![i$j![i$y![i$t![i~P+cO$t$RO~P%ZO$t$SO~P/eO!^!mi!o!mi!v!mi!y!mi#O!mi#R!mi#[!mi$j!mi$y!mi$t!mi~P!PO$r$UO!^!mi!o!mi!v!mi!y!mi#O!mi#R!mi#[!mi$j!mi$y!mi$t!mi~O{#WO!X#]O~O$n$ZO~O}qO#cyO~O#k#iO~P/eO#k#iO#p$fO#r$gO~O!S$oO!qsO$t$nO~P%ZOT$qO#v$qO~O$r$sO!^!wi!o!wi!v!wi!y!wi#O!wi#R!wi#[!wi$j!wi$y!wi$t!wi~O#V$uO#X$vO!^#SX!o#SX!v#SX!y#SX#O#SX#R#SX#[#SX$j#SX$r#SX$y#SX$t#SX~O$r$xO!^#Pi!o#Pi!v#Pi!y#Pi#O#Pi#R#Pi#[#Pi$j#Pi$y#Pi$t#Pi~O#T$zO~OT${O`!tO}qO!PzO!QzO#T!TO#cyO#h{O#u}O#v!TO#y!PO#{!QO$Q!UO$T!XO$V!YO~O$n%UO~OT%VO`!tO}qO!PzO!QzO#T!TO#cyO#h{O#u}O#v!TO#y!PO#{!QO$Q!UO$T!XO$V!YO~O$m!jO~Oi%ZO`!ba!^!ba!d!ba!f!ba!h!ba!j!ba!l!ba!o!ba!v!ba!y!ba#O!ba#R!ba#[!ba$j!ba$y!ba$t!ba~O$r%_O$t%`O~P/eO!^!mq!o!mq!v!mq!y!mq#O!mq#R!mq#[!mq$j!mq$y!mq$t!mq~P!PO$r%bO!^!mq!o!mq!v!mq!y!mq#O!mq#R!mq#[!mq$j!mq$y!mq$t!mq~O$n%cO~Ok#XOt#XOu#XOv#XOw#XOx#XOy#XO{#WO}qO!P#ZO!Q#ZO!S#[O!T#[O!U#[O!X#]O#a#_OTpifpiopi!^pi!opi!vpi!ypi#Opi#Rpi#[pi$jpi$rpi$ypi$tpi#kpi#mpi#rpi`pi!dpi!fpi!hpi!jpi!lpi#ppi#Tpi#cpi#hpi#upi#vpi#ypi#{pi$Qpi$Tpi$Vpi~Or#VO~PDYOrpi~PDYO!S#[O!T#[O!U#[OTpifpikpiopirpitpiupivpiwpixpiypi{pi}pi!Xpi!^pi!opi!vpi!ypi#Opi#Rpi#[pi#api$jpi$rpi$ypi$tpi#kpi#mpi#rpi`pi!dpi!fpi!hpi!jpi!lpi#ppi#Tpi#cpi#hpi#upi#vpi#ypi#{pi$Qpi$Tpi$Vpi~O!P#ZO!Q#ZO~PGvO!Ppi!Qpi~PGvO#m%jO~P/eO$t%lO~O$t%mO~P/eO$t%mO~O!^QO~O!^!wq!o!wq!v!wq!y!wq#O!wq#R!wq#[!wq$j!wq$y!wq$t!wq~P!PO$r%pO!^!wq!o!wq!v!wq!y!wq#O!wq#R!wq#[!wq$j!wq$y!wq$t!wq~O!^#Pq!o#Pq!v#Pq!y#Pq#O#Pq#R#Pq#[#Pq$j#Pq$y#Pq$t#Pq~OT#rO#T#rO~PMmO$r%rO~PMmO$m!jO$n#OOfVXkVXoVXrVXtVXuVXvVXwVXxVXyVX{VX}VX!PVX!QVX!SVX!TVX!UVX!XVX#aVX$ZgX~OT%tO~PNpOb%vOd%wO~O$r&TOT$bX`$bX}$bX!P$bX!Q$bX#T$bX#c$bX#h$bX#u$bX#v$bX#y$bX#{$bX$Q$bX$T$bX$V$bX$t$bX~O$t&VO~P>}O$Z&WO~O$r&YOT$iX`$iX}$iX!P$iX!Q$iX#T$iX#c$iX#h$iX#u$iX#v$iX#y$iX#{$iX$Q$iX$T$iX$V$iX$t$iX~O$t&[O~P@TOi%ZO`!bi!^!bi!d!bi!f!bi!h!bi!j!bi!l!bi!o!bi!v!bi!y!bi#O!bi#R!bi#[!bi$j!bi$y!bi$t!bi~O$r&`O$t&aO~O$t&aO~P%ZO!^!my!o!my!v!my!y!my#O!my#R!my#[!my$j!my$y!my$t!my~P!PO$r&eO$t!YX~P/eO$t&fO~O#r#na~P/eO$t&iO~P/eO!^!wy!o!wy!v!wy!y!wy#O!wy#R!wy#[!wy$j!wy$y!wy$t!wy~P!POT#rO#T#rO!^#Py!o#Py!v#Py!y#Py#O#Py#R#Py#[#Py$j#Py$y#Py$t#Py~O[&lOTWa`Wa}Wa!PWa!QWa#TWa#cWa#hWa#uWa#vWa#yWa#{Wa$QWa$TWa$VWa$rWa$tWa~Of!mOi%ZO~O$n&rO~O$t&|O~O`!bq!^!bq!d!bq!f!bq!h!bq!j!bq!l!bq!o!bq!v!bq!y!bq#O!bq#R!bq#[!bq$j!bq$y!bq$t!bq~P/eO$t'PO~P%ZO$r$ca$t$ca~P/eO$t'QO~O$r'RO$t!Ya~O$t!Ya~P%ZO#k#iq#p#iq#r#iq~P/eO$t'SO~P/eO$n'WO~Ok#XOt#XOu#XOv#XOw#XOx#XOy#XO{#WO}qO!P#ZO!Q#ZO!S#[O!T#[O!U#[O!X#]O#a#_Ofmiomi~Or#VO~P!-qOrmi~P!-qO!S#[O!T#[O!U#[Ofmikmiomirmitmiumivmiwmixmiymi{mi}mi!Xmi#ami~O!P#ZO!Q#ZO~P!/ZO!Pmi!Qmi~P!/ZOTli`li#Tli#cli#hli#uli#vli#yli#{li$Qli$Tli$Vli$rli$tli~P/eO`!by!^!by!d!by!f!by!h!by!j!by!l!by!o!by!v!by!y!by#O!by#R!by#[!by$j!by$y!by$t!by~P/eO$t!Yi~P%ZOk'ZO~O$t'^O~O$t'aO~OP!T!S!Q~",
1322
+ goto: "H}${PPP$|%QPP%U&n&r&u&xP&{'T'ZP'hP'hP'kP'}(mP(yP&{)P)VP)m*lP+UPPPPPP+pP,^P.RPP.oPPP)m/_P0P0]0w1UP1f1f1k2o2sP2sP2sP2sP2sP0w2wP3UP3[3m0w3xP0w4VP4dP0w4jP0w4wP5UP5^P5^P0w5aP5nP)m5qP6]P7l8o7l9rP:u:{P;RP;U;[P;`P7l;jPP<m=pP=pP<m>s>s?vP7l@yPA|P1p)P)PP$|$|BPPBTBZCmCsC}D^DdDrDxESPPPPPEYE^EdPGkPGt7l>s)mPHyTcOdT`OdUjR!z$O#Q!WTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'WQ!d`Q!ebQ%y$}S'T&o'[R'_'ZT%Q#v%SR%u${R&n%uR&m%uS%Q#v%ST%W#x%YX$}#v#x%S%YS!zn!}Q$O!{X$|#v#x%S%YR%x$|Q!pjQ!slQ#gxQ#w!dQ&S%OR&p%yQ!ojQ!rlQ#fxQ#z!pQ#|!sQ$c#gW%T#v#x%S%YQ&z&SR'V&pQ%[#}Q&^%]Q&o%yR'['VQ'U&oR'`'[X%P#v#x%S%Yr#ax!h!i#Q#h$Q$k$m%d%i%n&]&b&h&j&{&}R%}%O!y![Tfgrw|!]!`#O#R#a#b#c#d#e#l#n$U$Z$j$p$s%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'Wv#bx!h!i#Q#h$Q$^$k$m%d%i%n&]&b&h&j&u&{&}R&O%Oz#cx!h!i#Q#h$Q$^$_$k$m%d%i%n&]&b&h&j&u&v&{&}R&P%O|#Xx!h!i#Q#h$Q$^$_$k$m%O%d%i%n&]&b&h&j&u&v&{&}T$X#Y%z#QzTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'Wz#Yx!h!i#Q#h$Q$^$_$k$m%d%i%n&]&b&h&j&u&v&{&}Q$]#`Q%z%OR&t%|!O#dx!h!i#Q#h$Q$^$_$`$k$m%d%i%n&]&b&h&j&u&v&w&{&}R&Q%O!S#ex!h!i#Q#h$Q$^$_$`$a$k$m%d%i%n&]&b&h&j&u&v&w&x&{&}R&R%Oz#^x!h!i#Q#h$Q$^$_$k$m%d%i%n&]&b&h&j&u&v&{&}Q$Y#YQ%{%OR&q%zQ%e$ZQ&c%cQ'X&rR']'WSeOdS!qkrQ$l#mQ%e$ZQ&X%UQ&c%cQ'X&rR']'Wg^O_dkr#m$Z%U%c&r'WfRO_dkr#m$Z%U%c&r'WR%o$rVmR!z$OUlR!z$O!y!ZTfgrw|!]!`#O#R#a#b#c#d#e#l#n$U$Z$j$p$s%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'WT!|n!}T!{n!}gTO_dkr#m$Z%U%c&r'WQwTR$p#nQvTQ#TwQ#q!`]$T#R$U$s%a%b%pcuTw!`#R$U$s%a%b%pgfO_dkr#m$Z%U%c&r'WgWO_dkr#m$Z%U%c&r'WQ!`WR!aZggO_dkr#m$Z%U%c&r'WgZO_dkr#m$Z%U%c&r'WQ#s!aV%q$x%r&kR$w#rg]O_dkr#m$Z%U%c&r'WR#u!bz#`x!h!i#Q#h$Q$^$_$k$m%d%i%n&]&b&h&j&u&v&{&}R%|%O#Q!STfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'WQ$[#`Q%f$]Q&s%|R'Y&t#R!YTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R!]Tfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R|Tfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'WX#j|#h#k$dX#l|#h#k$dR%k$kQ$i#kR%h$dT$j#k$dQ$h#kS%g$d$iR&g%h#R!OTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R!TTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R!RTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R!WTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R!VTfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'W#R!^Tfgrw|!]!`#O#R#a#b#c#d#e#l#n#v#x$U$Z$j$p$s%S%Y%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'WR$r#pTbOdQdOR!gd#QiR`bfgr|!]!z#O#a#b#c#d#e#l#n#v#x$O$Z$j$p$}%S%Y%[%_%c%k%o%}&O&P&Q&R&W&^&`&e&o&r'O'R'W'Z'[bpTw!`#R$U$s%a%b%pT!lipQ%S#vR&U%SQ%^$QS&_%^&dR&d%dd_Odkr#m$Z%U%c&r'WR!c_Q!}nR$P!}Q#SvU$V#S$W$tQ$W#TR$t#qQ$y#sR%s$yQ#k|Q$d#hT$e#k$dQ%Y#xR&Z%YT%R#v%SX%O#v#x%S%YbxTw!`#R$U$s%a%b%pQ!hfQ!igQ#QrQ#h|Q#o!]Q$Q#OQ$^#aQ$_#bQ$`#cQ$a#dQ$b#eQ$k#lQ$m#nW%d$Z%c&r'WQ%i$jQ%n$pQ&]%[Y&b%_&`&e'O'RQ&h%kQ&j%oQ&u%}Q&v&OQ&w&PQ&x&QQ&y&RQ&{&WR&}&^QnRQ#}!zR%]$O!x![Tfgrw|!]!`#O#R#a#b#c#d#e#l#n$U$Z$j$p$s%[%_%a%b%c%k%o%p%}&O&P&Q&R&W&^&`&e&r'O'R'WX%P#v#x%S%YT%X#x%Y",
1323
+ nodeNames: "\u26A0 Comment Program TableStatement Kw Identifier table Ref ColumnDef DataType PrimaryKey Kw primary_key JoinDef JoinType Kw join Kw one Kw many Kw as Alias Kw on BinaryExpression = ComputedDef BinaryExpression Kw or BinaryExpression Kw and ComparisonOp != <> < > <= >= Kw like Kw not AddOp + - MulOp * / % InExpression Kw in InValueList QueryStatement FromClause Kw from TableName Subquery SubqueryExpression JoinClause Kw inner Kw left Kw right Kw full Kw cross SelectClause Kw select Kw distinct SelectItem Wildcard WhereClause Kw where GroupByClause Kw group Kw by HavingClause Kw having OrderByClause Kw order OrderItem Number Kw asc Kw desc LimitClause Kw limit Kw offset NullTestExpression Kw is Kw null UnaryExpression UnaryOperator CaseExpression Kw case WhenClause Kw when Kw then ElseClause Kw else Kw end ExistsExpression Kw exists String Boolean Kw true Kw false Null FunctionCall Count Kw count ExtractExpression Kw extract ExtractUnit Param Parenthetical InExpression NullTestExpression : ViewStatement ExtendStatement Kw extend",
1276
1324
  maxTerm: 180,
1277
1325
  nodeProps: [
1278
- ["group", -9, 25, 29, 32, 53, 63, 107, 146, 147, 148, "Expression", -12, 26, 97, 112, 114, 127, 130, 131, 136, 137, 138, 141, 145, "Expression Expression", -2, 61, 62, "TablePrimary"]
1326
+ ["group", -12, 7, 97, 112, 114, 127, 130, 131, 136, 137, 138, 141, 145, "Expression Expression", -9, 26, 29, 32, 53, 63, 107, 146, 147, 148, "Expression", -2, 61, 62, "TablePrimary"]
1279
1327
  ],
1280
1328
  skippedNodes: [0, 1],
1281
1329
  repeatNodeCount: 10,
1282
- tokenData: "*o~RoX^#Spq#Sqr#wrs$Stu$quv%cwx%hxy&Qyz&Vz{&[{|&a|}&f}!O&k!O!P'[!P!Q'a!Q![(p![!])Z!]!^)`!^!_)e!_!`)z!`!a*P!c!}*^#T#o*^#y#z#S$f$g#S#BY#BZ#S$IS$I_#S$I|$JO#S$JT$JU#S$KV$KW#S&FU&FV#S~#XY$l~X^#Spq#S#y#z#S$f$g#S#BY#BZ#S$IS$I_#S$I|$JO#S$JT$JU#S$KV$KW#S&FU&FV#S~#zP!_!`#}~$SOt~~$VTOr$Srs$fs;'S$S;'S;=`$k<%lO$S~$kO#v~~$nP;=`<%l$S~$tS!Q![%Q!c!}%Q#R#S%Q#T#o%Q~%VS$V~!Q![%Q!c!}%Q#R#S%Q#T#o%Q~%hO!U~~%kTOw%hwx$fx;'S%h;'S;=`%z<%lO%h~%}P;=`<%l%h~&VO$m~~&[O$t~~&aO!S~~&fO!P~~&kO$r~~&pP!Q~}!O&s~&xSP~OY&sZ;'S&s;'S;=`'U<%lO&s~'XP;=`<%l&s~'aO$o~~'fP!T~z{'i~'lTOz'iz{'{{;'S'i;'S;=`(j<%lO'i~(OVOz'iz{'{{!P'i!P!Q(e!Q;'S'i;'S;=`(j<%lO'i~(jOP~~(mP;=`<%l'i~(uQ#T~!O!P({!Q![(p~)OP!Q![)R~)WP#T~!Q![)R~)`O$Z~~)eO$y~~)jQv~!_!`)p!`!a)u~)uOx~~)zOu~~*POk~~*UPw~!_!`*X~*^Oy~~*cST~!Q![*^!c!}*^#R#S*^#T#o*^",
1330
+ tokenData: "*o~RoX^#Spq#Sqr#wrs$Stu$quv%cwx%hxy&Qyz&Vz{&[{|&a|}&f}!O&k!O!P'[!P!Q'a!Q![(p![!])Z!]!^)`!^!_)e!_!`)z!`!a*P!c!}*^#T#o*^#y#z#S$f$g#S#BY#BZ#S$IS$I_#S$I|$JO#S$JT$JU#S$KV$KW#S&FU&FV#S~#XY$l~X^#Spq#S#y#z#S$f$g#S#BY#BZ#S$IS$I_#S$I|$JO#S$JT$JU#S$KV$KW#S&FU&FV#S~#zP!_!`#}~$SOt~~$VTOr$Srs$fs;'S$S;'S;=`$k<%lO$S~$kO#v~~$nP;=`<%l$S~$tS!Q![%Q!c!}%Q#R#S%Q#T#o%Q~%VS$V~!Q![%Q!c!}%Q#R#S%Q#T#o%Q~%hO!U~~%kTOw%hwx$fx;'S%h;'S;=`%z<%lO%h~%}P;=`<%l%h~&VO$n~~&[O$t~~&aO!S~~&fO!P~~&kO$r~~&pP!Q~}!O&s~&xSP~OY&sZ;'S&s;'S;=`'U<%lO&s~'XP;=`<%l&s~'aO$m~~'fP!T~z{'i~'lTOz'iz{'{{;'S'i;'S;=`(j<%lO'i~(OVOz'iz{'{{!P'i!P!Q(e!Q;'S'i;'S;=`(j<%lO'i~(jOP~~(mP;=`<%l'i~(uQ#T~!O!P({!Q![(p~)OP!Q![)R~)WP#T~!Q![)R~)`O$Z~~)eO$y~~)jQv~!_!`)p!`!a)u~)uOx~~)zOu~~*POk~~*UPw~!_!`*X~*^Oy~~*cST~!Q![*^!c!}*^#R#S*^#T#o*^",
1283
1331
  tokenizers: [0],
1284
1332
  topRules: { "Program": [0, 2] },
1285
1333
  specialized: [{ term: 5, get: (value, stack) => specializeIdentifier(value, stack) << 1, external: specializeIdentifier }, { term: 5, get: (value) => spec_Identifier[value] || -1 }],
1286
- tokenPrec: 2957
1334
+ tokenPrec: 2964
1287
1335
  });
1288
1336
  }
1289
1337
  });
@@ -1607,7 +1655,7 @@ var init_core = __esm({
1607
1655
  init_params();
1608
1656
  init_util();
1609
1657
  init_config();
1610
- init_functions();
1658
+ init_functionDefs();
1611
1659
  init_parser();
1612
1660
  init_markdown();
1613
1661
  init_snowflake();
@@ -2167,6 +2215,10 @@ __export(bigQuery_exports, {
2167
2215
  BigQueryConnection: () => BigQueryConnection
2168
2216
  });
2169
2217
  import { BigQuery, BigQueryDate, BigQueryTimestamp } from "@google-cloud/bigquery";
2218
+ import { readFileSync as readFileSync3 } from "fs";
2219
+ function sqlStringLiteral(value) {
2220
+ return `'${value.replace(/'/g, "''")}'`;
2221
+ }
2170
2222
  var BigQueryConnection;
2171
2223
  var init_bigQuery = __esm({
2172
2224
  "connections/bigQuery.ts"() {
@@ -2174,6 +2226,8 @@ var init_bigQuery = __esm({
2174
2226
  init_config();
2175
2227
  BigQueryConnection = class {
2176
2228
  client;
2229
+ projectId;
2230
+ defaultNamespace;
2177
2231
  constructor(options = {}) {
2178
2232
  options.projectId ||= config.bigquery?.projectId;
2179
2233
  if (process.env.GOOGLE_CREDENTIALS_CONTENT) {
@@ -2181,8 +2235,14 @@ var init_bigQuery = __esm({
2181
2235
  options.projectId = parsed.project_id;
2182
2236
  options.credentials = parsed;
2183
2237
  }
2238
+ if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
2239
+ let tmp = JSON.parse(readFileSync3(process.env.GOOGLE_APPLICATION_CREDENTIALS, { encoding: "utf-8" }));
2240
+ options.projectId = tmp.project_id;
2241
+ }
2184
2242
  if (!options.projectId) throw new Error("projectId must be set in config or provided in service account credentials");
2243
+ this.projectId = options.projectId;
2185
2244
  this.client = new BigQuery({ ...options, userAgent: "Graphene" });
2245
+ this.defaultNamespace = config.namespace;
2186
2246
  }
2187
2247
  async runQuery(sql) {
2188
2248
  let [job] = await this.client.createQueryJob({ query: sql, useLegacySql: false });
@@ -2197,6 +2257,32 @@ var init_bigQuery = __esm({
2197
2257
  });
2198
2258
  return { rows, totalRows };
2199
2259
  }
2260
+ async listDatasets() {
2261
+ let [datasets] = await this.client.getDatasets();
2262
+ return datasets.map((d) => d.id || d.metadata.datasetReference?.datasetId);
2263
+ }
2264
+ async listTables(dataset) {
2265
+ if (!dataset) throw new Error("BigQuery requires a dataset");
2266
+ let res = await this.runQuery(`select table_schema as table_schema, table_name as table_name
2267
+ from \`${dataset}.INFORMATION_SCHEMA.TABLES\`
2268
+ where table_type in ('BASE TABLE', 'VIEW') order by table_name`);
2269
+ return res.rows.map((r) => `${r["table_schema"]}.${r["table_name"]}`);
2270
+ }
2271
+ async describeTable(target) {
2272
+ let parts = target.split(".");
2273
+ let table2 = parts.pop() || "";
2274
+ let dataset = parts.join(".");
2275
+ let sql = `
2276
+ select column_name as column_name, data_type as data_type, ordinal_position as ordinal_position
2277
+ from \`${dataset}.INFORMATION_SCHEMA.COLUMNS\`
2278
+ where lower(table_name) = lower(${sqlStringLiteral(table2)})
2279
+ order by ordinal_position
2280
+ `.trim();
2281
+ let res = await this.runQuery(sql);
2282
+ return res.rows.map((row) => {
2283
+ return { name: String(row["column_name"]), dataType: String(row["data_type"]) };
2284
+ });
2285
+ }
2200
2286
  };
2201
2287
  }
2202
2288
  });
@@ -2209,6 +2295,9 @@ __export(duckdb_exports, {
2209
2295
  import { promises as fs5 } from "fs";
2210
2296
  import path6 from "path";
2211
2297
  import { DuckDBTimestampValue, DuckDBInstance, DuckDBDateValue } from "@duckdb/node-api";
2298
+ function sqlStringLiteral2(value) {
2299
+ return `'${value.replace(/'/g, "''")}'`;
2300
+ }
2212
2301
  var DuckDBConnection;
2213
2302
  var init_duckdb = __esm({
2214
2303
  "connections/duckdb.ts"() {
@@ -2253,6 +2342,35 @@ var init_duckdb = __esm({
2253
2342
  });
2254
2343
  return { rows };
2255
2344
  }
2345
+ async listDatasets() {
2346
+ return await Promise.resolve([]);
2347
+ }
2348
+ async listTables() {
2349
+ let sql = `
2350
+ select table_schema as table_schema, table_name as table_name
2351
+ from information_schema.tables
2352
+ where table_type in ('BASE TABLE', 'VIEW') and table_schema not in ('information_schema', 'pg_catalog')
2353
+ order by table_schema, table_name
2354
+ `.trim();
2355
+ let res = await this.runQuery(sql);
2356
+ return res.rows.map((row) => String(row["table_name"]));
2357
+ }
2358
+ async describeTable(target) {
2359
+ let parts = target.split(".");
2360
+ let table2 = parts.pop() || "";
2361
+ let schema = parts[0];
2362
+ let schemaFilter = schema ? `lower(table_schema) = lower(${sqlStringLiteral2(schema)})` : "table_schema not in ('information_schema', 'pg_catalog')";
2363
+ let sql = `
2364
+ select column_name as column_name, data_type as data_type, ordinal_position as ordinal_position
2365
+ from information_schema.columns
2366
+ where lower(table_name) = lower(${sqlStringLiteral2(table2)}) and ${schemaFilter}
2367
+ order by ordinal_position
2368
+ `.trim();
2369
+ let res = await this.runQuery(sql);
2370
+ return res.rows.map((row) => {
2371
+ return { name: String(row["column_name"]), dataType: String(row["data_type"]) };
2372
+ });
2373
+ }
2256
2374
  };
2257
2375
  }
2258
2376
  });
@@ -2264,6 +2382,16 @@ __export(snowflake_exports, {
2264
2382
  });
2265
2383
  import { createPrivateKey } from "node:crypto";
2266
2384
  import snowflake from "snowflake-sdk";
2385
+ function getSnowflakeNamespace() {
2386
+ throw new Error("Not yet implemented");
2387
+ }
2388
+ function snowflakeIdent(value) {
2389
+ if (!value) throw new Error("Snowflake identifiers cannot be empty");
2390
+ return `"${value.replace(/"/g, '""')}"`;
2391
+ }
2392
+ function sqlStringLiteral3(value) {
2393
+ return `'${value.replace(/'/g, "''")}'`;
2394
+ }
2267
2395
  var SnowflakeConnection;
2268
2396
  var init_snowflake2 = __esm({
2269
2397
  "connections/snowflake.ts"() {
@@ -2324,36 +2452,70 @@ var init_snowflake2 = __esm({
2324
2452
  });
2325
2453
  });
2326
2454
  }
2455
+ async listDatasets() {
2456
+ await Promise.resolve();
2457
+ throw new Error("Not yet implemented");
2458
+ }
2459
+ async listTables() {
2460
+ let { database } = getSnowflakeNamespace();
2461
+ let tablesRef = `${snowflakeIdent(database)}.${snowflakeIdent("INFORMATION_SCHEMA")}.${snowflakeIdent("TABLES")}`;
2462
+ let sql = `
2463
+ select table_schema as "table_schema", table_name as "table_name"
2464
+ from ${tablesRef}
2465
+ where table_type in ('BASE TABLE', 'VIEW')
2466
+ order by table_schema, table_name
2467
+ `.trim();
2468
+ let res = await this.runQuery(sql);
2469
+ return res.rows.map((row) => String(row["table_name"] || ""));
2470
+ }
2471
+ async describeTable(target) {
2472
+ let parts = target.split(".");
2473
+ let table2 = parts.pop() || "";
2474
+ let database = parts.shift() || "";
2475
+ let schema = parts.join(".");
2476
+ let columnsRef = `${snowflakeIdent(database)}.${snowflakeIdent("INFORMATION_SCHEMA")}.${snowflakeIdent("COLUMNS")}`;
2477
+ let sql = `
2478
+ select column_name as "column_name", data_type as "data_type", ordinal_position as ordinal_position
2479
+ from ${columnsRef}
2480
+ where upper(table_schema) = upper(${sqlStringLiteral3(schema)}) and upper(table_name) = upper(${sqlStringLiteral3(table2)})
2481
+ order by ordinal_position
2482
+ `.trim();
2483
+ let res = await this.runQuery(sql);
2484
+ return res.rows.map((row) => {
2485
+ return { name: String(row["column_name"]), dataType: String(row["data_type"]) };
2486
+ });
2487
+ }
2327
2488
  };
2328
2489
  }
2329
2490
  });
2330
2491
 
2331
2492
  // connections/index.ts
2332
- async function runQuery(sql) {
2333
- if (config.host) {
2334
- let resp = await authenticatedFetch("/_api/query", {
2335
- method: "POST",
2336
- headers: { "Content-Type": "application/json" },
2337
- body: JSON.stringify({ sql })
2338
- });
2339
- return await resp.json();
2340
- }
2493
+ async function getConnection() {
2341
2494
  if (config.dialect === "bigquery") {
2342
2495
  let mod = await Promise.resolve().then(() => (init_bigQuery(), bigQuery_exports));
2343
- let conn = new mod.BigQueryConnection();
2344
- return await conn.runQuery(sql);
2496
+ return new mod.BigQueryConnection();
2345
2497
  } else if (config.dialect === "duckdb") {
2346
2498
  let mod = await Promise.resolve().then(() => (init_duckdb(), duckdb_exports));
2347
- let conn = new mod.DuckDBConnection({});
2348
- return await conn.runQuery(sql);
2499
+ return new mod.DuckDBConnection({});
2349
2500
  } else if (config.dialect === "snowflake") {
2350
2501
  let mod = await Promise.resolve().then(() => (init_snowflake2(), snowflake_exports));
2351
- let conn = new mod.SnowflakeConnection({});
2352
- return await conn.runQuery(sql);
2502
+ return new mod.SnowflakeConnection({});
2353
2503
  } else {
2354
2504
  throw new Error(`Unsupported dialect: ${config.dialect}`);
2355
2505
  }
2356
2506
  }
2507
+ async function runQuery(sql) {
2508
+ if (config.host) {
2509
+ let resp = await authenticatedFetch("/_api/query", {
2510
+ method: "POST",
2511
+ headers: { "Content-Type": "application/json" },
2512
+ body: JSON.stringify({ sql })
2513
+ });
2514
+ return await resp.json();
2515
+ }
2516
+ let conn = await getConnection();
2517
+ return await conn.runQuery(sql);
2518
+ }
2357
2519
  var init_connections = __esm({
2358
2520
  "connections/index.ts"() {
2359
2521
  "use strict";
@@ -2489,7 +2651,6 @@ async function serve2() {
2489
2651
  updateWorkspacePlugin,
2490
2652
  mockFilesForTests()
2491
2653
  ],
2492
- publicDir: path8.resolve(uiRoot),
2493
2654
  server: {
2494
2655
  port,
2495
2656
  fs: { strict: false },
@@ -2548,7 +2709,7 @@ async function handlePage(server, res, filePath, mount) {
2548
2709
  <meta charset="UTF-8" />
2549
2710
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2550
2711
  <title>Graphene</title>
2551
- <link rel="icon" href="/assets/favicon.ico" />
2712
+ <link rel="icon" href="/favicon.ico" />
2552
2713
  <link rel="preconnect" href="https://fonts.googleapis.com">
2553
2714
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
2554
2715
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet">
@@ -2607,6 +2768,10 @@ var init_serve2 = __esm({
2607
2768
  let [pathName] = (req.url || "").split("?");
2608
2769
  if (pathName == "/_api/query") return await handleQuery(req, res);
2609
2770
  if (pathName == "/__ct") return await handlePage(s, res, "__ct", false);
2771
+ if (pathName == "/favicon.ico") {
2772
+ res.setHeader("Content-Type", "image/x-icon");
2773
+ return res.end(await fs7.readFile(path8.resolve(uiRoot, "assets/favicon.ico")));
2774
+ }
2610
2775
  if (!pathName || pathName == "/") pathName = "index";
2611
2776
  let mdPath = path8.join(config.root, pathName + ".md");
2612
2777
  if (await fs7.exists(mdPath)) {
@@ -2636,6 +2801,8 @@ init_auth();
2636
2801
  import { Command } from "commander";
2637
2802
  import fs8 from "fs-extra";
2638
2803
  import path9 from "path";
2804
+ import dotenv from "dotenv";
2805
+ dotenv.config({ quiet: true });
2639
2806
  var program = new Command();
2640
2807
  program.name("graphene").description("Graphene CLI").version("1.0.0");
2641
2808
  program.hook("preAction", async () => {
@@ -2660,6 +2827,29 @@ program.command("run").description("Run a query against your database").argument
2660
2827
  let res = await runQuery(sql);
2661
2828
  printTable(res.rows);
2662
2829
  });
2830
+ program.command("schema").description("Inspect database tables or describe a table").argument("[schema | table]", "Optional schema or table name to describe").action(async (tableArg) => {
2831
+ let connection = await getConnection();
2832
+ let datasets = await connection.listDatasets();
2833
+ if (!tableArg && datasets.length > 1) {
2834
+ return console.log(`Datasets available:
2835
+ ${datasets.join("\n")}`);
2836
+ }
2837
+ let dsToList = null;
2838
+ if (datasets.includes(tableArg)) dsToList = tableArg;
2839
+ else if (!tableArg && datasets.length == 1) dsToList = datasets[0];
2840
+ else if (!tableArg && config.namespace) dsToList = config.namespace;
2841
+ else if (!tableArg && config.dialect == "duckdb") dsToList = "<default>";
2842
+ if (dsToList) {
2843
+ let tables = await connection.listTables(dsToList);
2844
+ return console.log(`Tables${dsToList ? ` in ${dsToList}` : ""}:
2845
+ ${tables.join("\n")}`);
2846
+ }
2847
+ let cols = await connection.describeTable(tableArg);
2848
+ if (!cols.length) return console.log(`Table ${tableArg} not found`);
2849
+ console.log(`table ${tableArg} (`);
2850
+ cols.forEach((col) => console.log(` ${col.name} ${col.dataType}`));
2851
+ console.log(")");
2852
+ });
2663
2853
  program.command("serve").description("Run the local server").option("--bg", "Run the server in the background").action(async (options) => {
2664
2854
  await stopGrapheneIfRunning();
2665
2855
  if (options.bg) {
@@ -379,6 +379,10 @@ FROM `bigquery-public-data.thelook_ecommerce.orders` as base
379
379
 
380
380
  You don't have to understand this; the point is that GSQL is minimizing the chances that naive users aggregate data incorrectly.
381
381
 
382
+ #### Percentile shorthand
383
+
384
+ Graphene provides percentile helpers so you rarely have to remember the SQL form for each warehouse. Anywhere you can call an aggregate, you can also write `pXX(column)` where `XX` is a whole number between 0 and 100. If you need precision finer than a whole percentile, append extra digits—everything after the first two digits is treated as decimals. Examples: `p975` → 97.5th percentile, `p9999` → 99.99th percentile. Graphene rewrites these shorthands to the dialect’s native function (`quantile_cont` on DuckDB, `approx_quantiles` on BigQuery, `PERCENTILE_CONT` on Snowflake) and ensures they behave like other aggregates (automatic grouping, structPath handling, etc.).
385
+
382
386
  ### `table as` statements
383
387
 
384
388
  You can turn the output of any `select` statement into a table with `table foo as (select ...)`. Here's an example of an additional table `user_facts` added to the two tables from earlier:
@@ -1132,44 +1136,6 @@ where email ilike concat('%', $name_of_input, '%')
1132
1136
  | description | Adds an info icon with description tooltip on hover | false | string | - |
1133
1137
 
1134
1138
 
1135
- #### Date range
1136
-
1137
- Creates a date picker that can be used to filter a query. Includes a set of preset ranges for quick selection of common date ranges (relative to the supplied end date). To see how to filter a query using an input component, see Filters.
1138
-
1139
- Here's an example:
1140
-
1141
- ```markdown
1142
- <DateRange
1143
- name=date_range_name
1144
- data=orders_by_day
1145
- dates=day
1146
- />
1147
- ```
1148
-
1149
- The start and end dates for the user-selected range would then be referenced in GSQL as `$date_range_name_start` and `$date_range_name_end` at the end. For example:
1150
-
1151
- ```sql
1152
- select *
1153
- from orders
1154
- where created_at > $date_range_name_start and < $date_range_name_end
1155
- ```
1156
-
1157
- ##### All date range attributes
1158
-
1159
- | Attribute | Description | Required | Options | Default |
1160
- |------|-------------|----------|---------|---------|
1161
- | name | Name of the DateRange, used to reference the selected values elsewhere as `"$name_start"` or `"$name_end"` | true | string | - |
1162
- | data | Query name, wrapped in curly braces | false | query name | - |
1163
- | dates | Column or expression from the query containing date range to span | false | column name, stored expression name, GSQL expression | - |
1164
- | start | A manually specified start date to use for the range | false | string formatted YYYY-MM-DD | - |
1165
- | end | A manually specified end date to use for the range | false | string formatted YYYY-MM-DD | - |
1166
- | title | Title to display in the Date Range component | false | string | - |
1167
- | presetRanges | Customize "Select a Range" drop down, by including preset range options | false | list of values e.g. `"Last 7 Days, Last 30 Days"`. Allowed values: `Last 7 Days`, `Last 30 Days`, `Last 90 Days`, `Last 365 Days`, `Last 3 Months`, `Last 6 Months`, `Last 12 Months`, `Last Month`, `Last Year`, `Month to Date`, `Month to Today`, `Year to Date`, `Year to Today`, `All Time` | - |
1168
- | defaultValue | Accepts preset in string format to apply default value in Date Range picker | false | `"Last 7 Days"`, `"Last 30 Days"`, `"Last 90 Days"`, `"Last 365 Days"`, `"Last 3 Months"`, `"Last 6 Months"`, `"Last 12 Months"`, `"Last Month"`, `"Last Year"`, `"Month to Date"`, `"Month to Today"`, `"Year to Date"`, `"Year to Today"`, `"All Time"` | - |
1169
- | hideDuringPrint | Hide the component when the report is printed | false | `true`, `false` | `true` |
1170
- | description | Adds an info icon with description tooltip on hover | false | string | - |
1171
-
1172
-
1173
1139
  #### Dropdown
1174
1140
 
1175
1141
  Creates a dropdown menu with a list of options that can be selected. The selected option can be used to filter queries or in markdown. To see how to filter a query using a dropdown, see Filters.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "main": "cli.ts",
4
4
  "type": "module",
5
5
  "author": "Graphene Systems Inc",
6
- "version": "0.0.6",
6
+ "version": "0.0.7",
7
7
  "license": "Elastic-2.0",
8
8
  "engines": {
9
9
  "node": ">=16"
@@ -25,6 +25,7 @@
25
25
  "dependencies": {
26
26
  "@duckdb/node-api": "1.3.2-alpha.26",
27
27
  "@google-cloud/bigquery": "^8.1.1",
28
+ "@graphenedata/html2canvas": "^1.4.1",
28
29
  "@graphenedata/malloy": "0.0.304",
29
30
  "@lezer/common": "^1.2.3",
30
31
  "@lezer/lr": "^1.4.2",
@@ -36,10 +37,10 @@
36
37
  "cli-table3": "^0.6.3",
37
38
  "commander": "^11.0.0",
38
39
  "debounce": "^1.2.1",
40
+ "dotenv": "^17.2.3",
39
41
  "echarts": "^5.5.0",
40
42
  "fs-extra": "11.2.0",
41
43
  "glob": "^11.0.3",
42
- "@graphenedata/html2canvas": "^1.4.1",
43
44
  "marked": "^16.3.0",
44
45
  "mdsvex": "^0.12.6",
45
46
  "nanoid": "3.3.8",