sqllens 1.0.0 → 1.2.0
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/LICENSE +0 -10
- package/README.md +95 -85
- package/THIRD-PARTY-NOTICES.md +70 -5
- package/dist/api.d.ts +5 -3
- package/dist/api.js +17 -4
- package/dist/bigquery/behavior.d.ts +2 -0
- package/dist/bigquery/behavior.js +20 -0
- package/dist/bigquery/dot-path.d.ts +0 -2
- package/dist/bigquery/dot-path.js +0 -1
- package/dist/bigquery/fold.d.ts +8 -0
- package/dist/bigquery/fold.js +39 -0
- package/dist/bigquery/index.d.ts +7 -0
- package/dist/bigquery/index.js +10 -0
- package/dist/{infer/bigquery.d.ts → bigquery/infer.d.ts} +2 -2
- package/dist/{infer/bigquery.js → bigquery/infer.js} +4 -3
- package/dist/bigquery/lower.js +35 -12
- package/dist/bigquery/signatures.generated.d.ts +6 -0
- package/dist/bigquery/signatures.generated.js +1068 -0
- package/dist/completion/atn-walk.d.ts +13 -2
- package/dist/completion/atn-walk.js +13 -10
- package/dist/completion/complete.d.ts +4 -3
- package/dist/completion/complete.js +101 -45
- package/dist/completion/config.js +66 -0
- package/dist/completion/jinja-slot.d.ts +24 -0
- package/dist/completion/jinja-slot.js +126 -0
- package/dist/completion/parser-factory.d.ts +13 -1
- package/dist/completion/parser-factory.js +48 -0
- package/dist/databricks/behavior.d.ts +2 -0
- package/dist/databricks/behavior.js +19 -0
- package/dist/databricks/fold.d.ts +8 -0
- package/dist/databricks/fold.js +27 -0
- package/dist/databricks/index.d.ts +7 -0
- package/dist/databricks/index.js +10 -0
- package/dist/databricks/infer.d.ts +6 -0
- package/dist/databricks/infer.js +638 -0
- package/dist/databricks/signatures.generated.d.ts +6 -0
- package/dist/databricks/signatures.generated.js +1745 -0
- package/dist/derived-dialects.js +19 -1
- package/dist/dialect-behavior/behavior.d.ts +26 -0
- package/dist/dialect-behavior/behavior.js +1 -0
- package/dist/dialect-behavior/carrier.d.ts +5 -0
- package/dist/dialect-behavior/carrier.js +5 -0
- package/dist/dialect-behavior/coerce-rules.d.ts +7 -0
- package/dist/dialect-behavior/coerce-rules.js +69 -0
- package/dist/dialect-behavior/public-fold.d.ts +6 -0
- package/dist/dialect-behavior/public-fold.js +9 -0
- package/dist/dialect-behavior/registry.d.ts +6 -0
- package/dist/dialect-behavior/registry.js +34 -0
- package/dist/dialect-symbols.js +22 -17
- package/dist/dialect.d.ts +2 -2
- package/dist/document/document.d.ts +1 -1
- package/dist/document/document.js +8 -7
- package/dist/duckdb/behavior.d.ts +2 -0
- package/dist/duckdb/behavior.js +21 -0
- package/dist/duckdb/fold.d.ts +8 -0
- package/dist/duckdb/fold.js +29 -0
- package/dist/duckdb/index.d.ts +7 -0
- package/dist/duckdb/index.js +10 -0
- package/dist/{infer/duckdb.d.ts → duckdb/infer.d.ts} +2 -2
- package/dist/{infer/duckdb.js → duckdb/infer.js} +4 -3
- package/dist/duckdb/lower.js +47 -15
- package/dist/duckdb/signatures.generated.d.ts +6 -0
- package/dist/duckdb/signatures.generated.js +1072 -0
- package/dist/generated/bigquery/GoogleSQLParser.js +0 -7060
- package/dist/generated/databricks/DatabricksParser.js +0 -4800
- package/dist/generated/duckdb/DuckdbParser.js +0 -9100
- package/dist/generated/minijinja/MinijinjaParser.js +0 -380
- package/dist/generated/mysql/MysqlLexer.js +7357 -0
- package/dist/generated/mysql/MysqlParser.js +78520 -0
- package/dist/generated/postgres/PostgresParser.js +0 -8530
- package/dist/generated/redshift/RedshiftParser.js +0 -10970
- package/dist/generated/snowflake/SnowflakeParser.js +0 -7320
- package/dist/generated/sqlite/SqliteLexer.js +945 -0
- package/dist/generated/sqlite/SqliteParser.js +14682 -0
- package/dist/generated/trino/TrinoParser.js +0 -3770
- package/dist/generated/tsql/TSqlParser.js +0 -8420
- package/dist/ident/fold.d.ts +24 -15
- package/dist/ident/fold.js +12 -145
- package/dist/index.d.ts +6 -2
- package/dist/index.js +14 -8
- package/dist/infer/functions.d.ts +22 -11
- package/dist/infer/functions.js +26 -888
- package/dist/infer/infer.js +16 -14
- package/dist/infer/nullability.js +3 -4
- package/dist/infer/types.d.ts +1 -1
- package/dist/infer/types.js +7 -7
- package/dist/ir/ir.d.ts +26 -17
- package/dist/ir/part-span.d.ts +16 -1
- package/dist/ir/part-span.js +38 -10
- package/dist/ir/span.js +0 -2
- package/dist/ir/walk.js +3 -3
- package/dist/lineage/hops.js +13 -10
- package/dist/lineage/lineage.js +10 -7
- package/dist/minijinja/apply-tags.d.ts +14 -7
- package/dist/minijinja/apply-tags.js +66 -121
- package/dist/minijinja/parse.js +6 -6
- package/dist/minijinja/tag-ast.d.ts +29 -36
- package/dist/minijinja/tag-ast.js +210 -90
- package/dist/mysql/behavior.d.ts +2 -0
- package/dist/mysql/behavior.js +21 -0
- package/dist/mysql/fold.d.ts +8 -0
- package/dist/mysql/fold.js +49 -0
- package/dist/mysql/index.d.ts +7 -0
- package/dist/mysql/index.js +10 -0
- package/dist/mysql/infer.d.ts +20 -0
- package/dist/mysql/infer.js +156 -0
- package/dist/mysql/lower.d.ts +13 -0
- package/dist/mysql/lower.js +1443 -0
- package/dist/mysql/parse.d.ts +10 -0
- package/dist/mysql/parse.js +70 -0
- package/dist/mysql/signatures.generated.d.ts +6 -0
- package/dist/mysql/signatures.generated.js +508 -0
- package/dist/postgres/behavior.d.ts +2 -0
- package/dist/postgres/behavior.js +19 -0
- package/dist/postgres/fold.d.ts +8 -0
- package/dist/postgres/fold.js +30 -0
- package/dist/postgres/index.d.ts +7 -0
- package/dist/postgres/index.js +10 -0
- package/dist/{infer/postgres.d.ts → postgres/infer.d.ts} +2 -2
- package/dist/{infer/postgres.js → postgres/infer.js} +4 -3
- package/dist/postgres/lower.js +2 -2
- package/dist/postgres/signatures.generated.d.ts +6 -0
- package/dist/postgres/signatures.generated.js +2973 -0
- package/dist/qualify/check-calls.js +80 -134
- package/dist/qualify/qualify.js +16 -14
- package/dist/qualify/schema-provider.js +2 -2
- package/dist/qualify/schema.js +3 -3
- package/dist/qualify/template-provider.d.ts +45 -12
- package/dist/qualify/template-provider.js +69 -38
- package/dist/redshift/behavior.d.ts +2 -0
- package/dist/redshift/behavior.js +19 -0
- package/dist/redshift/fold.d.ts +8 -0
- package/dist/redshift/fold.js +31 -0
- package/dist/redshift/index.d.ts +7 -0
- package/dist/redshift/index.js +10 -0
- package/dist/{infer/redshift.d.ts → redshift/infer.d.ts} +2 -2
- package/dist/{infer/redshift.js → redshift/infer.js} +4 -3
- package/dist/redshift/lower.js +2 -2
- package/dist/redshift/signatures.generated.d.ts +6 -0
- package/dist/redshift/signatures.generated.js +757 -0
- package/dist/references/references.js +17 -12
- package/dist/scope/like-pattern.d.ts +2 -0
- package/dist/scope/like-pattern.js +15 -0
- package/dist/scope/scope.d.ts +5 -5
- package/dist/scope/scope.js +48 -42
- package/dist/sema/resolve.js +17 -12
- package/dist/session.d.ts +2 -2
- package/dist/signature/signature.d.ts +14 -6
- package/dist/signature/signature.js +30 -22
- package/dist/signature/signatures.d.ts +14 -12
- package/dist/signature/signatures.js +42 -582
- package/dist/snowflake/behavior.d.ts +2 -0
- package/dist/snowflake/behavior.js +22 -0
- package/dist/snowflake/fold.d.ts +8 -0
- package/dist/snowflake/fold.js +25 -0
- package/dist/snowflake/index.d.ts +7 -0
- package/dist/snowflake/index.js +10 -0
- package/dist/{infer/snowflake.d.ts → snowflake/infer.d.ts} +2 -2
- package/dist/{infer/snowflake.js → snowflake/infer.js} +4 -3
- package/dist/snowflake/lower.js +59 -19
- package/dist/snowflake/signatures.generated.d.ts +6 -0
- package/dist/snowflake/signatures.generated.js +2080 -0
- package/dist/sqlite/behavior.d.ts +2 -0
- package/dist/sqlite/behavior.js +19 -0
- package/dist/sqlite/fold.d.ts +8 -0
- package/dist/sqlite/fold.js +41 -0
- package/dist/sqlite/index.d.ts +7 -0
- package/dist/sqlite/index.js +10 -0
- package/dist/sqlite/infer.d.ts +12 -0
- package/dist/sqlite/infer.js +122 -0
- package/dist/sqlite/lower.d.ts +11 -0
- package/dist/sqlite/lower.js +1093 -0
- package/dist/sqlite/parse.d.ts +10 -0
- package/dist/sqlite/parse.js +70 -0
- package/dist/sqlite/signatures.generated.d.ts +6 -0
- package/dist/sqlite/signatures.generated.js +277 -0
- package/dist/symbols/symbols.js +14 -12
- package/dist/token/classify.js +31 -0
- package/dist/token/tokenize.js +4 -0
- package/dist/trino/behavior.d.ts +2 -0
- package/dist/trino/behavior.js +21 -0
- package/dist/trino/fold.d.ts +8 -0
- package/dist/trino/fold.js +39 -0
- package/dist/trino/index.d.ts +7 -0
- package/dist/trino/index.js +10 -0
- package/dist/{infer/trino.d.ts → trino/infer.d.ts} +2 -2
- package/dist/{infer/trino.js → trino/infer.js} +4 -3
- package/dist/trino/lower.js +5 -5
- package/dist/trino/signatures.generated.d.ts +6 -0
- package/dist/trino/signatures.generated.js +968 -0
- package/dist/tsql/behavior.d.ts +2 -0
- package/dist/tsql/behavior.js +20 -0
- package/dist/tsql/fold.d.ts +8 -0
- package/dist/tsql/fold.js +34 -0
- package/dist/tsql/index.d.ts +7 -0
- package/dist/tsql/index.js +10 -0
- package/dist/tsql/infer.d.ts +16 -0
- package/dist/tsql/infer.js +289 -0
- package/dist/tsql/lower.js +4 -4
- package/dist/tsql/signatures.generated.d.ts +6 -0
- package/dist/tsql/signatures.generated.js +640 -0
- package/package.json +15 -11
- package/dist/generated/bigquery/GoogleSQLLexer.d.ts +0 -407
- package/dist/generated/bigquery/GoogleSQLParser.d.ts +0 -9558
- package/dist/generated/bigquery/GoogleSQLParserListener.d.ts +0 -7777
- package/dist/generated/bigquery/GoogleSQLParserListener.js +0 -7070
- package/dist/generated/databricks/DatabricksLexer.d.ts +0 -566
- package/dist/generated/databricks/DatabricksParser.d.ts +0 -7771
- package/dist/generated/databricks/DatabricksParserListener.d.ts +0 -5737
- package/dist/generated/databricks/DatabricksParserListener.js +0 -5256
- package/dist/generated/duckdb/DuckdbLexer.d.ts +0 -691
- package/dist/generated/duckdb/DuckdbParser.d.ts +0 -13932
- package/dist/generated/duckdb/DuckdbParserListener.d.ts +0 -10049
- package/dist/generated/duckdb/DuckdbParserListener.js +0 -9138
- package/dist/generated/minijinja/MinijinjaLexer.d.ts +0 -108
- package/dist/generated/minijinja/MinijinjaParser.d.ts +0 -604
- package/dist/generated/minijinja/MinijinjaParserListener.d.ts +0 -449
- package/dist/generated/minijinja/MinijinjaParserListener.js +0 -410
- package/dist/generated/postgres/PostgresLexer.d.ts +0 -663
- package/dist/generated/postgres/PostgresParser.d.ts +0 -12963
- package/dist/generated/postgres/PostgresParserListener.d.ts +0 -9408
- package/dist/generated/postgres/PostgresParserListener.js +0 -8554
- package/dist/generated/redshift/RedshiftLexer.d.ts +0 -954
- package/dist/generated/redshift/RedshiftParser.d.ts +0 -16939
- package/dist/generated/redshift/RedshiftParserListener.d.ts +0 -12092
- package/dist/generated/redshift/RedshiftParserListener.js +0 -10994
- package/dist/generated/snowflake/SnowflakeLexer.d.ts +0 -1046
- package/dist/generated/snowflake/SnowflakeParser.d.ts +0 -14196
- package/dist/generated/snowflake/SnowflakeParserListener.d.ts +0 -8063
- package/dist/generated/snowflake/SnowflakeParserListener.js +0 -7330
- package/dist/generated/trino/TrinoLexer.d.ts +0 -381
- package/dist/generated/trino/TrinoParser.d.ts +0 -5340
- package/dist/generated/trino/TrinoParserListener.d.ts +0 -4704
- package/dist/generated/trino/TrinoParserListener.js +0 -4326
- package/dist/generated/tsql/TSqlLexer.d.ts +0 -1278
- package/dist/generated/tsql/TSqlParser.d.ts +0 -17267
- package/dist/generated/tsql/TSqlParserListener.d.ts +0 -9697
- package/dist/generated/tsql/TSqlParserListener.js +0 -8854
- package/dist/infer/dialect.d.ts +0 -21
- package/dist/infer/dialect.js +0 -74
- package/dist/infer/literals.d.ts +0 -6
- package/dist/infer/literals.js +0 -44
- package/dist/signature/generated/tsql.d.ts +0 -3
- package/dist/signature/generated/tsql.js +0 -260
package/LICENSE
CHANGED
|
@@ -19,13 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
This MIT license covers the original sqllens source: everything under src/
|
|
26
|
-
(excluding generated output), tools/, tests/, and the project documentation.
|
|
27
|
-
|
|
28
|
-
The hand-maintained ANTLR grammars under grammars/ are forks of third-party
|
|
29
|
-
grammars and remain under their upstream licenses (Apache-2.0, BSD-3-Clause, and
|
|
30
|
-
MIT, depending on the grammar). Each grammar file retains its original license
|
|
31
|
-
header, and the full per-grammar attributions are in THIRD-PARTY-NOTICES.md.
|
package/README.md
CHANGED
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/sqllens) [](LICENSE)
|
|
4
4
|
|
|
5
|
-
A TypeScript SQL parser and static analyzer. It parses SQL into a tree
|
|
6
|
-
to a dialect-neutral intermediate representation (IR), and runs
|
|
7
|
-
over that IR: name resolution (scope), schema-fed qualification,
|
|
8
|
-
and column lineage. Give it a
|
|
5
|
+
A TypeScript SQL parser and static analyzer. It parses SQL into a syntax tree
|
|
6
|
+
(AST), lowers it to a dialect-neutral intermediate representation (IR), and runs
|
|
7
|
+
a semantic layer over that IR: name resolution (scope), schema-fed qualification,
|
|
8
|
+
type inference, and column lineage. Give it a
|
|
9
9
|
query and it tells you the query's sources, its output columns, their types, and
|
|
10
10
|
where each column comes from. The parsers are generated TypeScript on the
|
|
11
|
-
[antlr4ng](https://github.com/mike-lischke/antlr4ng) runtime.
|
|
11
|
+
[antlr4ng](https://github.com/mike-lischke/antlr4ng) runtime. Dialects covered:
|
|
12
|
+
Databricks (Spark SQL), T-SQL, Snowflake, BigQuery (GoogleSQL), Redshift,
|
|
13
|
+
PostgreSQL, DuckDB, Trino, SQLite, and MySQL, plus derived engines such as
|
|
14
|
+
Athena, Fabric, and MariaDB (see [Dialects](#dialects)).
|
|
12
15
|
|
|
13
16
|
The front end is error-tolerant and token-first, so the library drives editor
|
|
14
17
|
features (completion, hover, diagnostics, go-to-definition) over incomplete,
|
|
15
|
-
mid-edit text. See [Editor / language tooling](#editor--language-tooling).
|
|
16
|
-
(Language Server Protocol) server built on it lives in the repo, but it is
|
|
17
|
-
experimental and not part of the published package.
|
|
18
|
+
mid-edit text. See [Editor / language tooling](#editor--language-tooling).
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
21
|
npm install sqllens
|
|
@@ -32,9 +33,9 @@ q.lineage.originsOf("total"); // → orders.total
|
|
|
32
33
|
|
|
33
34
|
## Dialects
|
|
34
35
|
|
|
35
|
-
sqllens implements
|
|
36
|
-
|
|
37
|
-
of
|
|
36
|
+
sqllens implements the major SQL dialects directly, each with its own grammar.
|
|
37
|
+
More engines are covered as *derived dialects*: their SQL is already parsed by
|
|
38
|
+
one of the primary grammars.
|
|
38
39
|
|
|
39
40
|
| Dialect | Derived dialects | Parse + lower | Semantic layer | Notes |
|
|
40
41
|
|---|---|---|---|---|
|
|
@@ -46,6 +47,8 @@ of those eight grammars, for 15 engines in total.
|
|
|
46
47
|
| PostgreSQL | — | yes | yes | grammar forked from `bytebase/parser` `postgresql/` (BSD-3, PG18 keywords) |
|
|
47
48
|
| DuckDB | — | yes | yes | grammar forked from this repo's own postgres pair (no open ANTLR grammar exists) |
|
|
48
49
|
| Trino | Presto, Amazon Athena | yes | yes | grammar is the first-party trinodb `SqlBase.g4` (release 482), mechanically split |
|
|
50
|
+
| SQLite | — | yes | yes | grammar forked from grammars-v4 `sql/sqlite` (MIT); entry rule `parse` |
|
|
51
|
+
| MySQL | MariaDB (partial — ordinary DQL/DML only, MariaDB-only extensions unmodeled) | yes | yes | grammar forked from grammars-v4 `sql/mysql/Positive-Technologies` (MIT); entry rule `root` |
|
|
49
52
|
|
|
50
53
|
Each grammar began as a fork of the upstream noted above, but most are now far from
|
|
51
54
|
verbatim copies. They've had substantial extension and correction, driven by a full
|
|
@@ -58,9 +61,21 @@ the primary dialect's. Microsoft Fabric runs a restricted subset of T-SQL, Amazo
|
|
|
58
61
|
Athena's engine is Trino, and AWS Glue runs Spark. Each one is checked against real
|
|
59
62
|
SQL from that engine before it goes on the list.
|
|
60
63
|
|
|
61
|
-
In code, the `dialect` argument is
|
|
62
|
-
engine name (or a dialect name) into the
|
|
63
|
-
|
|
64
|
+
In code, the `dialect` argument is a plain string, and `resolveDialect` turns an
|
|
65
|
+
engine name (or a dialect name) into the dialect that parses its SQL:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { parse, resolveDialect } from "sqllens";
|
|
69
|
+
|
|
70
|
+
// dialect strings: "databricks" | "tsql" | "snowflake" | "bigquery" |
|
|
71
|
+
// "redshift" | "postgres" | "duckdb" | "trino" | "sqlite" | "mysql"
|
|
72
|
+
parse("SELECT 1", "snowflake");
|
|
73
|
+
|
|
74
|
+
// engine name → the dialect that parses it
|
|
75
|
+
resolveDialect("athena"); // "trino"
|
|
76
|
+
resolveDialect("fabric"); // "tsql"
|
|
77
|
+
resolveDialect("mariadb"); // "mysql"
|
|
78
|
+
```
|
|
64
79
|
|
|
65
80
|
The semantic layer is dialect-agnostic: it operates on the shared IR and runs
|
|
66
81
|
unchanged on every dialect. Only the parse and lower stages are dialect-specific.
|
|
@@ -73,7 +88,7 @@ parse → lower → resolveScopes → qualify → infer / lineage / symbols
|
|
|
73
88
|
|
|
74
89
|
Each stage produces one value, and that value is what a specific editor feature
|
|
75
90
|
reads from. Only the first two stages, parse and lower, are dialect-specific;
|
|
76
|
-
everything after them is shared and runs unchanged across
|
|
91
|
+
everything after them is shared and runs unchanged across every dialect.
|
|
77
92
|
|
|
78
93
|
**parse** turns SQL text into a *concrete syntax tree* (CST): the full parse tree,
|
|
79
94
|
every token and grammar node exactly as written, nothing dropped or simplified. It
|
|
@@ -191,7 +206,7 @@ import { parse, qualify, lineage, deriveSymbols, toScopes, Schema } from "sqllen
|
|
|
191
206
|
const { ast, errors, cst } = parse("SELECT a, b FROM t", "snowflake");
|
|
192
207
|
// ast = dialect-neutral IR (frozen); cst = the raw antlr tree (escape hatch)
|
|
193
208
|
|
|
194
|
-
const scopes = toScopes(ast
|
|
209
|
+
const scopes = toScopes(ast); // idempotent lift
|
|
195
210
|
qualify(scopes, schema); // reuses scopes — never re-parses or re-resolves
|
|
196
211
|
lineage(scopes, schema); // safe on the same scopes, in any order
|
|
197
212
|
deriveSymbols(scopes); // independent results
|
|
@@ -285,6 +300,41 @@ invariants the conformance gates check. Tokens tile the source byte-for-byte, ev
|
|
|
285
300
|
span is in original document coordinates, broken input never throws, and tag-free
|
|
286
301
|
text is identical to a plain parse.
|
|
287
302
|
|
|
303
|
+
## Broken and incomplete SQL
|
|
304
|
+
|
|
305
|
+
sqllens is error-tolerant by construction, because its first consumer is an
|
|
306
|
+
editor and editor input is mid-keystroke most of the time. Parsing broken,
|
|
307
|
+
partial, or invalid SQL never throws: syntax errors come back as positioned
|
|
308
|
+
diagnostics (line, column, offset, length), ready for editor squiggles, and
|
|
309
|
+
the rest of the result stays usable.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { parse } from "sqllens";
|
|
313
|
+
|
|
314
|
+
// mid-edit input: a dangling comma and an unfinished WHERE
|
|
315
|
+
const r = parse("SELECT total, FROM orders WHERE", "postgres");
|
|
316
|
+
|
|
317
|
+
r.errors; // 1 — counted, not thrown
|
|
318
|
+
r.diagnostics[0]; // { message: "mismatched input ','…", line: 1, column: 12, offset: 12, length: 1 }
|
|
319
|
+
r.ast; // still a usable IR — lower() is total on broken input
|
|
320
|
+
r.tokens.length; // 10 — the full token stream, exact spans intact
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Every downstream pass keeps the same contract: `lower()` yields a flagged IR
|
|
324
|
+
instead of throwing, statement-level containment keeps one broken statement
|
|
325
|
+
from taking down its neighbors, and the interactive features run on the
|
|
326
|
+
broken text directly:
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
import { SqlSession, Schema } from "sqllens";
|
|
330
|
+
|
|
331
|
+
const schema = new Schema({ orders: { id: "int", total: "decimal" } });
|
|
332
|
+
|
|
333
|
+
// the projection slot is empty — the user just hasn't typed it yet
|
|
334
|
+
const s = SqlSession.create("SELECT FROM orders", "postgres", { schema });
|
|
335
|
+
s.completeAt(7); // candidates for the empty slot: total, id, keywords, functions
|
|
336
|
+
```
|
|
337
|
+
|
|
288
338
|
## Editor / language tooling
|
|
289
339
|
|
|
290
340
|
The front end is error-tolerant and token-first, so it serves editor features
|
|
@@ -309,6 +359,17 @@ that run on incomplete, mid-edit text. They never need a clean parse:
|
|
|
309
359
|
of the symbol under the cursor; backs find-references, document highlight, and
|
|
310
360
|
code-lens reference counts.
|
|
311
361
|
|
|
362
|
+
To tokenize SQL without parsing at all, `tokenize` is lexer-only and works on
|
|
363
|
+
any text, including text no parser would accept:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import { tokenize } from "sqllens";
|
|
367
|
+
|
|
368
|
+
const tokens = tokenize("SELECT amount FROM sales", "snowflake");
|
|
369
|
+
tokens[0]; // { text: "SELECT", start: 0, stop: 5, line: 1, column: 0, role: "keyword", channel: 0, … }
|
|
370
|
+
tokens[1]; // whitespace rides the hidden channel: { text: " ", channel: 1, role: "whitespace", … }
|
|
371
|
+
```
|
|
372
|
+
|
|
312
373
|
```ts
|
|
313
374
|
import { SqlDocument, Schema } from "sqllens";
|
|
314
375
|
|
|
@@ -318,75 +379,24 @@ doc.tokenAt(7); // token under an offset
|
|
|
318
379
|
const next = doc.withText("SELECT amount, id FROM sales", 2); // immutable edit → new doc
|
|
319
380
|
```
|
|
320
381
|
|
|
321
|
-
##
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
SQL
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
| Hover — nullability | ✅ (` — not null` / ` — nullable` suffix when provable) |
|
|
340
|
-
| Signature help | ✅ |
|
|
341
|
-
| Go to definition | ✅ |
|
|
342
|
-
| Find references | ✅ |
|
|
343
|
-
| Document highlight | ✅ |
|
|
344
|
-
| Document symbols | ✅ |
|
|
345
|
-
| Folding range | ✅ |
|
|
346
|
-
| Selection range | ✅ |
|
|
347
|
-
| Semantic tokens (full / range / delta) | ✅ all three |
|
|
348
|
-
| Inlay hints | ✅ (no resolve) |
|
|
349
|
-
| Code lens | ✅ (no resolve) |
|
|
350
|
-
| Go to declaration | ◻️ not yet |
|
|
351
|
-
| Go to type definition | ◻️ not yet |
|
|
352
|
-
| Go to implementation | ◻️ not yet — name → its defining query (view / model); needs the project model |
|
|
353
|
-
| Call hierarchy | ◻️ not yet — the CTE / view / model dependency graph |
|
|
354
|
-
| Document link | ◻️ not yet |
|
|
355
|
-
| Linked editing range | ◻️ not yet — live alias / name sync-edit |
|
|
356
|
-
| Code action (quick fixes) | ◻️ next phase |
|
|
357
|
-
| Rename (+ prepare) | ◻️ next phase |
|
|
358
|
-
| Formatting / range / on-type | ◻️ deferred (external formatter) |
|
|
359
|
-
| Inline values | ◻️ debugger surface |
|
|
360
|
-
| Type hierarchy | — n/a — SQL has no type-inheritance relation |
|
|
361
|
-
| Document color | — n/a — no color literals |
|
|
362
|
-
| Moniker | — n/a — LSIF / cross-repo indexing concern |
|
|
363
|
-
|
|
364
|
-
### Diagnostics & document sync
|
|
365
|
-
|
|
366
|
-
| Feature | Status |
|
|
367
|
-
| --- | --- |
|
|
368
|
-
| Diagnostics — push (`publishDiagnostics`) | ✅ |
|
|
369
|
-
| Diagnostics — call signature (arity / argument type) | ✅ (curated tables; never-wrong, per-dialect coercion) |
|
|
370
|
-
| Diagnostics — pull (document) | ✅ |
|
|
371
|
-
| Diagnostics — pull (workspace) | ◻️ not yet |
|
|
372
|
-
| Text sync — open / change / close | ✅ (full-document) |
|
|
373
|
-
| Incremental sync | ◻️ full-document only (fine at SQL file sizes) |
|
|
374
|
-
| Save notifications (`didSave` / `willSave`) | ◻️ not yet |
|
|
375
|
-
| Notebook document sync | ◻️ not yet |
|
|
376
|
-
|
|
377
|
-
### Workspace features
|
|
378
|
-
|
|
379
|
-
| Feature | Status |
|
|
380
|
-
| --- | --- |
|
|
381
|
-
| Workspace symbols | ◻️ needs a project / multi-file model |
|
|
382
|
-
| Execute command | ◻️ not yet |
|
|
383
|
-
| Configuration / watched-files | ◻️ not yet (protocol config; file-based `.sqllens.json` config exists) |
|
|
384
|
-
| File operations (create / rename / delete) | ◻️ not yet |
|
|
385
|
-
|
|
386
|
-
Legend: ✅ implemented · ◻️ not yet / deferred · — not applicable to SQL. The
|
|
387
|
-
deferred items are tracked work: rename and
|
|
388
|
-
code actions are the next LSP phase, workspace symbols need the project model,
|
|
389
|
-
and formatting is expected to wrap an existing external formatter.
|
|
382
|
+
## How sqllens compares
|
|
383
|
+
|
|
384
|
+
The SQL-parser field splits into parse-only libraries and semantic tools bound
|
|
385
|
+
to a single borrowed parser. The full survey, with the whole field catalogued,
|
|
386
|
+
is in [docs/sql-parser-landscape.md](docs/sql-parser-landscape.md); the short
|
|
387
|
+
version against the libraries people usually reach for:
|
|
388
|
+
|
|
389
|
+
| | Language | Dialect breadth | Semantic analysis | Error-tolerant, editor-grade |
|
|
390
|
+
|---|---|---|---|---|
|
|
391
|
+
| **sqllens** | TypeScript | Databricks, T-SQL, Snowflake, BigQuery, Redshift, PostgreSQL, DuckDB, Trino, SQLite, MySQL | scope, schema qualification, type inference, column lineage, symbols | yes: parses mid-keystroke input, positioned diagnostics, total pipeline |
|
|
392
|
+
| [sqlglot](https://github.com/tobymao/sqlglot) | Python | 31 dialects | transpile, optimize, qualify, lineage | no: a batch library, not built for per-keystroke reparse |
|
|
393
|
+
| [node-sql-parser](https://github.com/taozhi8833998/node-sql-parser) | JS/TS | MySQL, PostgreSQL, and more | table/column lists only; no lineage, no types | no |
|
|
394
|
+
| [sqllineage](https://github.com/reata/sqllineage) | Python | via sqlfluff's parser | column lineage only | no |
|
|
395
|
+
| [libpg_query](https://github.com/pganalyze/libpg_query) | C (bindings) | PostgreSQL, exact | parse only | no: one syntax error fails the whole buffer |
|
|
396
|
+
|
|
397
|
+
The corner sqllens occupies: multi-dialect breadth, schema-fed semantics, and
|
|
398
|
+
editor-grade error tolerance in one TypeScript library. Each piece exists
|
|
399
|
+
elsewhere; the combination did not.
|
|
390
400
|
|
|
391
401
|
## Architecture
|
|
392
402
|
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -100,14 +100,79 @@ Forked from [antlr/grammars-v4](https://github.com/antlr/grammars-v4)
|
|
|
100
100
|
`sql/snowflake`. Copyright (c) 2022 Michał Lorek. Licensed under the MIT License
|
|
101
101
|
(full text retained in the file header).
|
|
102
102
|
|
|
103
|
+
### SQLite grammar — MIT
|
|
104
|
+
|
|
105
|
+
`grammars/sqlite/SqliteLexer.g4`, `grammars/sqlite/SqliteParser.g4`
|
|
106
|
+
|
|
107
|
+
Forked from [antlr/grammars-v4](https://github.com/antlr/grammars-v4) `sql/sqlite`
|
|
108
|
+
(upstream commit `8af0d4c26c796ea27c15c3d85418f2d0f77c3adb`, retrieved 2026-07-10).
|
|
109
|
+
Copyright (c) 2020 Martin Mirchev; (c) 2014 Bart Kiers. Licensed under the MIT
|
|
110
|
+
License (full text retained in the file headers).
|
|
111
|
+
|
|
112
|
+
### MySQL grammar — MIT
|
|
113
|
+
|
|
114
|
+
`grammars/mysql/MysqlLexer.g4`, `grammars/mysql/MysqlParser.g4`
|
|
115
|
+
|
|
116
|
+
Forked from [antlr/grammars-v4](https://github.com/antlr/grammars-v4)
|
|
117
|
+
`sql/mysql/Positive-Technologies` (upstream commit
|
|
118
|
+
`bf61744020dc46f2d7b8761e35b0c0cb39b3f31a`, retrieved 2026-07-10) — not the
|
|
119
|
+
`sql/mysql/Oracle` sibling variant. Copyright (c) 2015-2017 Ivan Kochurkin,
|
|
120
|
+
Positive Technologies; (c) 2017 Ivan Khudyashev. Licensed under the MIT License
|
|
121
|
+
(full text retained in the file headers).
|
|
122
|
+
|
|
123
|
+
## Doc-derived function-signature tables (distributed in this repository)
|
|
124
|
+
|
|
125
|
+
The generated per-dialect function-signature tables (produced by
|
|
126
|
+
`tools/harvest-signatures.mjs`, committed under `src/`) carry factual API-surface
|
|
127
|
+
data (function names, parameter names, arity, optionality) extracted from each
|
|
128
|
+
dialect's reference documentation. Sources and their licenses:
|
|
129
|
+
|
|
130
|
+
### T-SQL signatures, from MicrosoftDocs/sql-docs (CC BY 4.0)
|
|
131
|
+
|
|
132
|
+
Derived from the Transact-SQL reference markdown in
|
|
133
|
+
[MicrosoftDocs/sql-docs](https://github.com/MicrosoftDocs/sql-docs), (c) Microsoft
|
|
134
|
+
Corporation, licensed [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
|
|
135
|
+
Changes were made: the documented syntax notation was mechanically parsed and
|
|
136
|
+
transformed into TypeScript signature tables; no documentation prose is reproduced.
|
|
137
|
+
|
|
138
|
+
### DuckDB signatures, from duckdb/duckdb-web (MIT)
|
|
139
|
+
|
|
140
|
+
Derived from the function reference markdown in
|
|
141
|
+
[duckdb/duckdb-web](https://github.com/duckdb/duckdb-web), Copyright 2018-2025
|
|
142
|
+
Stichting DuckDB Foundation, MIT License.
|
|
143
|
+
|
|
144
|
+
### PostgreSQL signatures, from the PostgreSQL documentation (PostgreSQL License)
|
|
145
|
+
|
|
146
|
+
Derived from `doc/src/sgml/func.sgml` of
|
|
147
|
+
[postgres/postgres](https://github.com/postgres/postgres) (REL_18_STABLE),
|
|
148
|
+
Copyright (c) 1996-2025, PostgreSQL Global Development Group, PostgreSQL License.
|
|
149
|
+
|
|
150
|
+
### Trino signatures, from trinodb/trino docs (Apache License 2.0)
|
|
151
|
+
|
|
152
|
+
Derived from the sphinx function reference in
|
|
153
|
+
[trinodb/trino](https://github.com/trinodb/trino) `docs/src/main/sphinx/functions`
|
|
154
|
+
(release 482), Apache License 2.0. trinodb/trino ships no `NOTICE` file, so there
|
|
155
|
+
is no 4(d) attribution to reproduce.
|
|
156
|
+
|
|
157
|
+
### BigQuery / GoogleSQL signatures, from google/googlesql docs (Apache License 2.0)
|
|
158
|
+
|
|
159
|
+
Derived from the function reference markdown in
|
|
160
|
+
[google/googlesql](https://github.com/google/googlesql) `docs/`, (c) Google LLC,
|
|
161
|
+
Apache License 2.0.
|
|
162
|
+
|
|
163
|
+
### Databricks and Snowflake signatures, from the vendors' public SQL references
|
|
164
|
+
|
|
165
|
+
Derived from the Syntax sections of the public SQL language references at
|
|
166
|
+
[docs.databricks.com](https://docs.databricks.com) ((c) Databricks, Inc.) and
|
|
167
|
+
[docs.snowflake.com](https://docs.snowflake.com) ((c) Snowflake Inc.). These sites
|
|
168
|
+
publish no redistribution license; the tables reproduce only the factual call
|
|
169
|
+
shape of each function (name, parameter names, arity, optionality), the same facts
|
|
170
|
+
any SQL tool documents about a dialect's API surface, and no documentation prose.
|
|
171
|
+
Credited here as sources with thanks.
|
|
172
|
+
|
|
103
173
|
## Runtime and build dependencies (not redistributed in source)
|
|
104
174
|
|
|
105
175
|
- **antlr4ng** — the TypeScript ANTLR runtime (BSD-3-Clause). Runtime dependency.
|
|
106
176
|
- **antlr-ng** — the pure-TypeScript ANTLR generator used by `npm run gen` to
|
|
107
177
|
produce `src/generated/` (a build product, gitignored). Dev dependency.
|
|
108
|
-
- **vscode-languageserver** / **vscode-languageserver-protocol** /
|
|
109
|
-
**vscode-languageserver-textdocument** / **vscode-languageserver-types** — the
|
|
110
|
-
LSP layer's runtime dependencies (MIT).
|
|
111
|
-
- **minimatch** — glob matching used by the LSP layer (ISC).
|
|
112
|
-
|
|
113
178
|
Consult each package's own license for the authoritative terms.
|
package/dist/api.d.ts
CHANGED
|
@@ -117,10 +117,12 @@ export { SqlDocument, type DocumentAnalysis, type StatementCell, type DocumentVa
|
|
|
117
117
|
export { LineIndex } from "./document/line-index.js";
|
|
118
118
|
export type { StatementCellSpan } from "./document/split.js";
|
|
119
119
|
export { complete, completeAt, type Completion } from "./completion/complete.js";
|
|
120
|
-
export {
|
|
121
|
-
export {
|
|
120
|
+
export { jinjaSlotAt, type JinjaSlot } from "./completion/jinja-slot.js";
|
|
121
|
+
export { signatureAt, type SignatureHelpInfo, type SignatureLabel } from "./signature/signature.js";
|
|
122
|
+
export { SIGNATURES, lookupSignature, hasSignature, type FnSignature, type ParamSig } from "./signature/signatures.js";
|
|
122
123
|
export { referencesAt, type Occurrence, type Occurrences } from "./references/references.js";
|
|
123
124
|
export { dialectSymbols, type DialectSymbols } from "./dialect-symbols.js";
|
|
124
125
|
export { CallbackSchema, type SchemaProvider, type TableResolver } from "./qualify/schema-provider.js";
|
|
125
126
|
export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
|
|
126
|
-
export { foldIdentifier, displayName
|
|
127
|
+
export { foldIdentifier, displayName } from "./dialect-behavior/public-fold.js";
|
|
128
|
+
export type { IdentKind } from "./ident/fold.js";
|
package/dist/api.js
CHANGED
|
@@ -27,6 +27,10 @@ import { parseDuckdb } from "./duckdb/parse.js";
|
|
|
27
27
|
import { lower as lowerDuckdb } from "./duckdb/lower.js";
|
|
28
28
|
import { parseTrino } from "./trino/parse.js";
|
|
29
29
|
import { lower as lowerTrino } from "./trino/lower.js";
|
|
30
|
+
import { parseSqlite } from "./sqlite/parse.js";
|
|
31
|
+
import { lower as lowerSqlite } from "./sqlite/lower.js";
|
|
32
|
+
import { parseMysql } from "./mysql/parse.js";
|
|
33
|
+
import { lower as lowerMysql } from "./mysql/lower.js";
|
|
30
34
|
import { resolveScopes } from "./scope/scope.js";
|
|
31
35
|
import { qualify as qualifyScopes } from "./qualify/qualify.js";
|
|
32
36
|
import { OPEN_PROVIDER } from "./qualify/template-provider.js";
|
|
@@ -43,6 +47,8 @@ const DIALECTS = {
|
|
|
43
47
|
postgres: { parse: parsePostgres, lower: lowerPostgres },
|
|
44
48
|
duckdb: { parse: parseDuckdb, lower: lowerDuckdb },
|
|
45
49
|
trino: { parse: parseTrino, lower: lowerTrino },
|
|
50
|
+
sqlite: { parse: parseSqlite, lower: lowerSqlite },
|
|
51
|
+
mysql: { parse: parseMysql, lower: lowerMysql },
|
|
46
52
|
};
|
|
47
53
|
/**
|
|
48
54
|
* Parse one statement (or a dialect's statement batch) and lower it to the IR. Dispatches on
|
|
@@ -199,11 +205,18 @@ export { LineIndex } from "./document/line-index.js";
|
|
|
199
205
|
// Scope-aware completion over a SqlDocument — the broken-input editor feature (keywords + schema
|
|
200
206
|
// tables/columns + function names at the caret). Total: never throws.
|
|
201
207
|
export { complete, completeAt } from "./completion/complete.js";
|
|
208
|
+
// The NEUTRAL half of jinja completion: which call + arg slot the caret sits in inside a jinja tag
|
|
209
|
+
// (`{{ ref('cu│` → { callee: "ref", argIndex: 0, prefix: "cu" }). Carries no dbt vocabulary; a
|
|
210
|
+
// consumer maps callee + argIndex to a role and supplies candidates. Reuses the parsed tags.
|
|
211
|
+
export { jinjaSlotAt } from "./completion/jinja-slot.js";
|
|
202
212
|
// Signature help over a SqlDocument — the broken-input editor feature that shows parameter hints
|
|
203
|
-
// while typing inside a call's parens.
|
|
204
|
-
// long tail,
|
|
213
|
+
// while typing inside a call's parens. SIGNATURES is the merged per-dialect table (curated overrides
|
|
214
|
+
// folded over the harvested doc-derived long tail at generation time, tools/harvest-signatures.mjs);
|
|
215
|
+
// a name maps to an overload SET (readonly FnSignature[]), not a single shape: it is common for a
|
|
216
|
+
// builtin to be overloaded on argument type or arity. An unknown name degrades to a one-entry
|
|
217
|
+
// name-only fallback. A pure token scan; never throws.
|
|
205
218
|
export { signatureAt } from "./signature/signature.js";
|
|
206
|
-
export {
|
|
219
|
+
export { SIGNATURES, lookupSignature, hasSignature } from "./signature/signatures.js";
|
|
207
220
|
// References / occurrence engine — find the declaration + every occurrence of the symbol under a
|
|
208
221
|
// cursor offset. The core primitive behind LSP references / documentHighlight / codeLens / rename.
|
|
209
222
|
// Total: never throws; returns null off-symbol.
|
|
@@ -222,4 +235,4 @@ export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
|
|
|
222
235
|
// The dialect-true identifier fold — the identity key for name comparison (unquote + case-fold per
|
|
223
236
|
// the dialect's documented rules) and its display twin (unquote only). Exported so an embedding
|
|
224
237
|
// consumer comparing names against IR/scope output folds the same way the pipeline does.
|
|
225
|
-
export { foldIdentifier, displayName } from "./
|
|
238
|
+
export { foldIdentifier, displayName } from "./dialect-behavior/public-fold.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { acceptsFor } from "../dialect-behavior/coerce-rules.js";
|
|
2
|
+
import { likePatternToRegExp } from "../scope/like-pattern.js";
|
|
3
|
+
import { SIGNATURES } from "../signature/signatures.js";
|
|
4
|
+
import { displayName, fold, foldTableName, matchesSourceKey } from "./fold.js";
|
|
5
|
+
import { bigqueryLiteral, bigqueryParseType, bigquerySpecial, BIGQUERY_FUNCTION_RETURNS } from "./infer.js";
|
|
6
|
+
export const bigqueryBehavior = {
|
|
7
|
+
fold,
|
|
8
|
+
displayName,
|
|
9
|
+
foldTableName,
|
|
10
|
+
matchesSourceKey,
|
|
11
|
+
likeMatch: (pattern, value) => likePatternToRegExp(pattern).test(value),
|
|
12
|
+
literal: bigqueryLiteral,
|
|
13
|
+
parseType: bigqueryParseType,
|
|
14
|
+
functions: BIGQUERY_FUNCTION_RETURNS,
|
|
15
|
+
division: "float",
|
|
16
|
+
special: bigquerySpecial,
|
|
17
|
+
signatures: SIGNATURES.bigquery,
|
|
18
|
+
// BigQuery implicit coercion: no STRING->NUMBER coercion, no BOOL<->NUMBER coercion (STR_TO_NUM=false, BOOL_NUM=false).
|
|
19
|
+
accepts: (argType, paramText) => acceptsFor(bigqueryParseType, false, false, argType, paramText),
|
|
20
|
+
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type Lexer, ListTokenSource } from "antlr4ng";
|
|
2
|
-
import { GoogleSQLLexer } from "../generated/bigquery/GoogleSQLLexer.js";
|
|
3
2
|
import type { SyntaxDiagnostic } from "../parse-diagnostics.js";
|
|
4
3
|
/**
|
|
5
4
|
* Lex `sql` and return a token source with the DOT_IDENTIFIER rewrite applied, plus positioned
|
|
@@ -11,4 +10,3 @@ export declare function dotPathTokenSource(sql: string, lexer: Lexer): {
|
|
|
11
10
|
source: ListTokenSource;
|
|
12
11
|
escapeDiagnostics: SyntaxDiagnostic[];
|
|
13
12
|
};
|
|
14
|
-
export { GoogleSQLLexer };
|
|
@@ -365,4 +365,3 @@ export function dotPathTokenSource(sql, lexer) {
|
|
|
365
365
|
const tokens = lexer.getAllTokens(); // full lex (drives lexer error listeners); EOF excluded
|
|
366
366
|
return { source: new ListTokenSource(rewriteDotPaths(tokens)), escapeDiagnostics: badLiteralEscapes(tokens) };
|
|
367
367
|
}
|
|
368
|
-
export { GoogleSQLLexer };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FoldRule, type IdentKind } from "../ident/fold.js";
|
|
2
|
+
export declare const BIGQUERY_FOLD_RULE: FoldRule;
|
|
3
|
+
/** Fold an identifier to its BigQuery identity key. */
|
|
4
|
+
export declare function fold(raw: string, kind?: IdentKind): string;
|
|
5
|
+
/** Presentation twin: strip delimiters, no case change. */
|
|
6
|
+
export declare function displayName(raw: string): string;
|
|
7
|
+
export declare function foldTableName(parts: string[]): string[];
|
|
8
|
+
export declare function matchesSourceKey(key: string, rawPart: string): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// BigQuery identifier folding. The FoldRule plus its bound engine, colocated here because BOTH the
|
|
2
|
+
// upstream lower() and the downstream DialectBehavior need it (the fold rule is the one dialect concern
|
|
3
|
+
// used at two stages).
|
|
4
|
+
//
|
|
5
|
+
// cloud.google.com/bigquery/docs/reference/standard-sql/lexical — verified live (via search,
|
|
6
|
+
// the JS-rendered page would not return body text to WebFetch): "table names are case-sensitive,
|
|
7
|
+
// but column names are not" — so table identifiers preserve case and everything else (column,
|
|
8
|
+
// field, alias, CTE) folds to lower, REGARDLESS of backtick-quoting either way (backticks are
|
|
9
|
+
// required for reserved words/specials, not a case-quoting mechanism — same "not case-quoting"
|
|
10
|
+
// shape as Databricks, but the preserved/folded split is per identifier KIND here, not
|
|
11
|
+
// per-quoting). Escape mechanism corrected from the originally assumed doubling: "Quoted
|
|
12
|
+
// identifiers have the same escape sequences as string literals" (backslash-escaped, e.g.
|
|
13
|
+
// `` `a\`b` `` → `` a`b ``) — NOT doubling like every other backtick/quote dialect here. This
|
|
14
|
+
// module unescapes the identifier-relevant case (`` \` ``) plus the general `\X`→`X` pattern; it
|
|
15
|
+
// does not implement BigQuery's full string-literal escape grammar (\n, \xHH, \uXXXX, octal, …),
|
|
16
|
+
// out of scope for an identifier fold — those escapes are exotic in identifier text.
|
|
17
|
+
import { displayWith, foldWith } from "../ident/fold.js";
|
|
18
|
+
const BACKTICK = ["`", "`"];
|
|
19
|
+
export const BIGQUERY_FOLD_RULE = {
|
|
20
|
+
delimiters: [BACKTICK],
|
|
21
|
+
unquoted: "lower",
|
|
22
|
+
quoted: "lower",
|
|
23
|
+
tableCase: "preserve",
|
|
24
|
+
escapeStyle: "backslash",
|
|
25
|
+
};
|
|
26
|
+
/** Fold an identifier to its BigQuery identity key. */
|
|
27
|
+
export function fold(raw, kind = "other") {
|
|
28
|
+
return foldWith(BIGQUERY_FOLD_RULE, raw, kind);
|
|
29
|
+
}
|
|
30
|
+
/** Presentation twin: strip delimiters, no case change. */
|
|
31
|
+
export function displayName(raw) {
|
|
32
|
+
return displayWith(BIGQUERY_FOLD_RULE, raw);
|
|
33
|
+
}
|
|
34
|
+
export function foldTableName(parts) {
|
|
35
|
+
return parts.map((p) => fold(p, "table"));
|
|
36
|
+
}
|
|
37
|
+
export function matchesSourceKey(key, rawPart) {
|
|
38
|
+
return key === fold(rawPart) || key === fold(rawPart, "table");
|
|
39
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The complete bigquery dialect module: parse + lower (front end) and behavior (semantic knowledge).
|
|
2
|
+
// The registry wires this; to understand everything sqllens does for bigquery, read this folder.
|
|
3
|
+
import { bigqueryBehavior } from "./behavior.js";
|
|
4
|
+
import { lower } from "./lower.js";
|
|
5
|
+
import { parseBigQuery } from "./parse.js";
|
|
6
|
+
export const bigquery = {
|
|
7
|
+
parse: parseBigQuery,
|
|
8
|
+
lower,
|
|
9
|
+
behavior: bigqueryBehavior,
|
|
10
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Expr } from "../ir/ir.js";
|
|
2
|
-
import type { FnRule } from "
|
|
3
|
-
import { type Type } from "
|
|
2
|
+
import type { FnRule } from "../infer/functions.js";
|
|
3
|
+
import { type Type } from "../infer/types.js";
|
|
4
4
|
export declare const BQ_ALIASES: Record<string, string>;
|
|
5
5
|
export declare function bigqueryParseType(text: string): Type;
|
|
6
6
|
export declare function bigqueryLiteral(text: string): Type;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { commonType } from "
|
|
2
|
-
import { parseType, scalar, UNKNOWN } from "
|
|
1
|
+
import { commonType } from "../infer/coerce.js";
|
|
2
|
+
import { parseType, scalar, UNKNOWN } from "../infer/types.js";
|
|
3
|
+
import { fold } from "./fold.js";
|
|
3
4
|
// BigQuery / GoogleSQL inference knowledge — function return types, literal forms, and scalar-type
|
|
4
5
|
// aliases — from the GoogleSQL function reference
|
|
5
6
|
// (cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators). Same contract as
|
|
@@ -24,7 +25,7 @@ export const BQ_ALIASES = {
|
|
|
24
25
|
geography: "geography",
|
|
25
26
|
};
|
|
26
27
|
export function bigqueryParseType(text) {
|
|
27
|
-
return parseType(text, BQ_ALIASES,
|
|
28
|
+
return parseType(text, BQ_ALIASES, fold);
|
|
28
29
|
}
|
|
29
30
|
export function bigqueryLiteral(text) {
|
|
30
31
|
const t = text.trim();
|
package/dist/bigquery/lower.js
CHANGED
|
@@ -1549,37 +1549,60 @@ function lowerFunctionCall(node) {
|
|
|
1549
1549
|
name = (keyword ? keyword.getText() : (leftmostToken(node) ?? "")).toLowerCase();
|
|
1550
1550
|
}
|
|
1551
1551
|
const suffix = directChildrenOfRule(node, P.RULE_function_call_expression_with_clauses_suffix)[0];
|
|
1552
|
-
const args = suffix ? collectCallArgs(suffix) : [];
|
|
1552
|
+
const { args, argNames } = suffix ? collectCallArgs(suffix) : { args: [], argNames: [] };
|
|
1553
1553
|
const over = suffix ? firstOfRule(suffix, P.RULE_over_clause) : undefined;
|
|
1554
1554
|
const window = over ? lowerOver(over) : undefined;
|
|
1555
1555
|
const distinct = hasDirectToken(node, P.DISTINCT_SYMBOL);
|
|
1556
|
-
return {
|
|
1556
|
+
return {
|
|
1557
|
+
kind: "function",
|
|
1558
|
+
name,
|
|
1559
|
+
qualifier,
|
|
1560
|
+
args,
|
|
1561
|
+
// Named-argument invocation `fn(name => value)`: the per-arg names make the call
|
|
1562
|
+
// conservation-visible and let the arity checker's named-arg bypass fire (a named call's
|
|
1563
|
+
// positional count says nothing about the documented positional signature).
|
|
1564
|
+
...(argNames.some((n) => n !== undefined) ? { argNames } : {}),
|
|
1565
|
+
aggregate: AGGREGATES.has(name),
|
|
1566
|
+
distinct,
|
|
1567
|
+
window,
|
|
1568
|
+
cst: node,
|
|
1569
|
+
};
|
|
1557
1570
|
}
|
|
1558
|
-
/** function_call_argument children of the suffix (skipping nested calls' own args)
|
|
1571
|
+
/** function_call_argument children of the suffix (skipping nested calls' own args), with the
|
|
1572
|
+
* `name => value` parameter name per arg slot (undefined for positional args). */
|
|
1559
1573
|
function collectCallArgs(suffix) {
|
|
1560
|
-
const
|
|
1574
|
+
const args = [];
|
|
1575
|
+
const argNames = [];
|
|
1561
1576
|
for (const arg of shallowNodesOfRule(suffix, P.RULE_function_call_argument)) {
|
|
1562
1577
|
// function_call_argument: expression alias? | named_argument | lambda_argument | sequence_arg
|
|
1563
1578
|
const named = directChildrenOfRule(arg, P.RULE_named_argument)[0];
|
|
1564
1579
|
if (named) {
|
|
1580
|
+
const argName = directChildrenOfRule(named, P.RULE_identifier)[0]?.getText();
|
|
1565
1581
|
const e = directChildrenOfRule(named, P.RULE_expression)[0];
|
|
1566
|
-
if (e)
|
|
1567
|
-
|
|
1582
|
+
if (e) {
|
|
1583
|
+
args.push(lowerExpr(e));
|
|
1584
|
+
argNames.push(argName);
|
|
1585
|
+
}
|
|
1568
1586
|
const lam = directChildrenOfRule(named, P.RULE_lambda_argument)[0];
|
|
1569
|
-
if (lam)
|
|
1570
|
-
|
|
1587
|
+
if (lam) {
|
|
1588
|
+
args.push(lowerLambda(lam));
|
|
1589
|
+
argNames.push(argName);
|
|
1590
|
+
}
|
|
1571
1591
|
continue;
|
|
1572
1592
|
}
|
|
1573
1593
|
const lambda = directChildrenOfRule(arg, P.RULE_lambda_argument)[0];
|
|
1574
1594
|
if (lambda) {
|
|
1575
|
-
|
|
1595
|
+
args.push(lowerLambda(lambda));
|
|
1596
|
+
argNames.push(undefined);
|
|
1576
1597
|
continue;
|
|
1577
1598
|
}
|
|
1578
1599
|
const e = directChildrenOfRule(arg, P.RULE_expression)[0];
|
|
1579
|
-
if (e)
|
|
1580
|
-
|
|
1600
|
+
if (e) {
|
|
1601
|
+
args.push(lowerExpr(e));
|
|
1602
|
+
argNames.push(undefined);
|
|
1603
|
+
}
|
|
1581
1604
|
}
|
|
1582
|
-
return
|
|
1605
|
+
return { args, argNames };
|
|
1583
1606
|
}
|
|
1584
1607
|
/** lambda_argument: lambda_argument_list -> expression. */
|
|
1585
1608
|
function lowerLambda(node) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FnSignature } from "../signature/signatures.js";
|
|
2
|
+
/** The merged function-signature table for bigquery: curated overrides folded over the harvested
|
|
3
|
+
* doc-derived long tail (overrides win by key, replacing the whole overload set), keyed by
|
|
4
|
+
* lowercased name. Each name maps to an ORDERED overload set - a name with one documented shape
|
|
5
|
+
* is a one-element array. `origin` says which layer produced the set. */
|
|
6
|
+
export declare const BIGQUERY_SIGNATURES: Record<string, FnSignature[]>;
|