@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 +37 -6
- package/dist/cli/cli.js +310 -120
- package/dist/docs/graphene.md +4 -38
- package/package.json +3 -2
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
|
|
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/
|
|
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
|
|
187
|
-
"../lang/
|
|
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
|
-
|
|
258
|
-
|
|
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("
|
|
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("
|
|
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
|
-
|
|
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("
|
|
752
|
+
let tableName = txt(node.getChild("Ref"));
|
|
649
753
|
let target = lookupTable(tableName, node);
|
|
650
754
|
if (!target) {
|
|
651
|
-
return diag(node.getChild("
|
|
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("
|
|
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("
|
|
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("
|
|
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",
|
|
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
|
|
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",
|
|
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",
|
|
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}`,
|
|
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)}`,
|
|
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",
|
|
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",
|
|
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",
|
|
991
|
-
if (right2.type !== left2.type) return diag(node, `Expected right side to be a ${left2.type}`,
|
|
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",
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
1186
|
-
join =
|
|
1187
|
-
as =
|
|
1188
|
-
on =
|
|
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:
|
|
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$
|
|
1273
|
-
stateData: "!
|
|
1274
|
-
goto: "
|
|
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
|
|
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", -
|
|
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$
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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="/
|
|
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) {
|
package/dist/docs/graphene.md
CHANGED
|
@@ -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
|
+
"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",
|