sqllens 1.2.0 → 1.4.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.
Files changed (47) hide show
  1. package/THIRD-PARTY-NOTICES.md +31 -6
  2. package/dist/api.d.ts +2 -0
  3. package/dist/api.js +2 -0
  4. package/dist/bigquery/fn-docs.generated.d.ts +2 -0
  5. package/dist/bigquery/fn-docs.generated.js +1704 -0
  6. package/dist/completion/complete.js +13 -4
  7. package/dist/completion/jinja-slot.d.ts +6 -0
  8. package/dist/completion/jinja-slot.js +13 -3
  9. package/dist/databricks/fn-docs.generated.d.ts +2 -0
  10. package/dist/databricks/fn-docs.generated.js +3257 -0
  11. package/dist/duckdb/fn-docs.generated.d.ts +2 -0
  12. package/dist/duckdb/fn-docs.generated.js +2051 -0
  13. package/dist/duckdb/fold.js +6 -4
  14. package/dist/ident/fold.d.ts +5 -2
  15. package/dist/ident/fold.js +2 -0
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/infer/types.js +4 -1
  19. package/dist/minijinja/apply-tags.js +19 -10
  20. package/dist/mysql/fn-docs.generated.d.ts +2 -0
  21. package/dist/mysql/fn-docs.generated.js +1204 -0
  22. package/dist/parse-diagnostics.js +33 -1
  23. package/dist/postgres/fn-docs.generated.d.ts +2 -0
  24. package/dist/postgres/fn-docs.generated.js +2818 -0
  25. package/dist/postgres/fold.js +9 -3
  26. package/dist/postgres/signatures.generated.js +585 -585
  27. package/dist/qualify/template-provider.d.ts +5 -2
  28. package/dist/qualify/template-provider.js +5 -2
  29. package/dist/redshift/fn-docs.generated.d.ts +2 -0
  30. package/dist/redshift/fn-docs.generated.js +1457 -0
  31. package/dist/redshift/fold.js +6 -4
  32. package/dist/sema/resolve.js +3 -1
  33. package/dist/signature/docs.d.ts +16 -0
  34. package/dist/signature/docs.js +47 -0
  35. package/dist/signature/render.d.ts +7 -0
  36. package/dist/signature/render.js +40 -0
  37. package/dist/signature/signature.js +5 -5
  38. package/dist/snowflake/fn-docs.generated.d.ts +2 -0
  39. package/dist/snowflake/fn-docs.generated.js +2461 -0
  40. package/dist/sqlite/fn-docs.generated.d.ts +2 -0
  41. package/dist/sqlite/fn-docs.generated.js +609 -0
  42. package/dist/sqlite/fold.js +5 -7
  43. package/dist/trino/fn-docs.generated.d.ts +2 -0
  44. package/dist/trino/fn-docs.generated.js +1932 -0
  45. package/dist/tsql/fn-docs.generated.d.ts +2 -0
  46. package/dist/tsql/fn-docs.generated.js +1017 -0
  47. package/package.json +1 -1
@@ -4,14 +4,16 @@
4
4
  // duckdb.org/docs/current/sql/dialect/keywords_and_identifiers.html — verified live: "Identifiers
5
5
  // in DuckDB are always case-insensitive, similarly to PostgreSQL. However, unlike PostgreSQL...
6
6
  // DuckDB also treats quoted identifiers as case-insensitive" — quoting only preserves the
7
- // identifier for DISPLAY, not identity. Doubled-quote escape: "Double quotes can be escaped by
8
- // repeating the quote character."
7
+ // identifier for DISPLAY, not identity. The insensitivity is ASCII-scoped, same page (#22):
8
+ // "Case-insensitivity is implemented using an ASCII-based comparison: col_A and col_a are equal
9
+ // but col_á is not equal to them"; hence ascii-lower for both forms. Doubled-quote escape:
10
+ // "Double quotes can be escaped by repeating the quote character."
9
11
  import { displayWith, foldWith } from "../ident/fold.js";
10
12
  const DOUBLE_QUOTE = ['"', '"'];
11
13
  export const DUCKDB_FOLD_RULE = {
12
14
  delimiters: [DOUBLE_QUOTE],
13
- unquoted: "lower",
14
- quoted: "lower",
15
+ unquoted: "ascii-lower",
16
+ quoted: "ascii-lower",
15
17
  };
16
18
  /** Fold an identifier to its DuckDB identity key. */
17
19
  export function fold(raw, kind = "other") {
@@ -1,12 +1,15 @@
1
1
  /** "table" = a table/view identifier — only BigQuery treats these differently from everything
2
2
  * else (columns, aliases, CTE names, struct/field names, …), which is "other". */
3
3
  export type IdentKind = "table" | "other";
4
- type CaseFold = "lower" | "upper" | "preserve";
4
+ /** "ascii-lower" folds [A-Z] only, passing every other character through, for dialects whose
5
+ * engines compare identifiers ASCII-case-insensitively (sqlite, postgres, duckdb, redshift):
6
+ * there `Ä` and `ä` are DISTINCT identifiers, and a Unicode-wide fold would conflate them (#22). */
7
+ type CaseFold = "lower" | "upper" | "preserve" | "ascii-lower";
5
8
  export interface FoldRule {
6
9
  /** [open, close] delimiter pairs this dialect recognizes, tried in order. */
7
10
  delimiters: readonly (readonly [string, string])[];
8
11
  /** Case fold applied to an unquoted identifier. */
9
- unquoted: "lower" | "upper";
12
+ unquoted: "lower" | "upper" | "ascii-lower";
10
13
  /** Case fold applied to a quoted (delimited) identifier. */
11
14
  quoted: CaseFold;
12
15
  /** BigQuery only: table identifiers preserve case whether or not they're backtick-quoted —
@@ -12,6 +12,8 @@ function applyCase(text, fold) {
12
12
  return text.toLowerCase();
13
13
  if (fold === "upper")
14
14
  return text.toUpperCase();
15
+ if (fold === "ascii-lower")
16
+ return text.replace(/[A-Z]/g, (c) => c.toLowerCase());
15
17
  return text;
16
18
  }
17
19
  /** Strip one matching delimiter pair off `raw` (if present) and unescape its body.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { parse, analyze, toAst, toScopes, qualify, lineage, deriveSymbols, TypeInfo, type Nullability, Lineage, originsOfExpr, lineageAt, lineageOf, type LineageHop, type ViaStep, tokenize, SqlDocument, LineIndex, complete, completeAt, type Completion, jinjaSlotAt, type JinjaSlot, signatureAt, SIGNATURES, lookupSignature, hasSignature, referencesAt, type Occurrence, type Occurrences, dialectSymbols, type DialectSymbols, CallbackSchema, type SchemaProvider, type TableResolver, DERIVED_DIALECTS, resolveDialect, foldIdentifier, displayName, type IdentKind, type SignatureHelpInfo, type SignatureLabel, type FnSignature, type ParamSig, type Dialect, type DialectOpts, type ParseResultIR, type Analysis, type Token, type TokenRole, type DocumentAnalysis, type StatementCell, type StatementCellSpan, type DocumentVariant, type UnionCte, } from "./api.js";
1
+ export { parse, analyze, toAst, toScopes, qualify, lineage, deriveSymbols, TypeInfo, type Nullability, Lineage, originsOfExpr, lineageAt, lineageOf, type LineageHop, type ViaStep, tokenize, SqlDocument, LineIndex, complete, completeAt, type Completion, jinjaSlotAt, type JinjaSlot, signatureAt, SIGNATURES, lookupSignature, hasSignature, renderSignature, FN_DOCS, lookupFnDoc, referencesAt, type Occurrence, type Occurrences, dialectSymbols, type DialectSymbols, CallbackSchema, type SchemaProvider, type TableResolver, DERIVED_DIALECTS, resolveDialect, foldIdentifier, displayName, type IdentKind, type SignatureHelpInfo, type SignatureLabel, type FnSignature, type ParamSig, type RenderSignatureOptions, type FnDoc, type Dialect, type DialectOpts, type ParseResultIR, type Analysis, type Token, type TokenRole, type DocumentAnalysis, type StatementCell, type StatementCellSpan, type DocumentVariant, type UnionCte, } from "./api.js";
2
2
  export { SqlSession, type SessionOptions } from "./session.js";
3
3
  export { parseDatabricks } from "./databricks/parse.js";
4
4
  export { parseTSql } from "./tsql/parse.js";
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  // - Building blocks: the per-dialect parse*/lower and the raw shared passes, for callers who
11
11
  // want a specific tier or the raw CST escape hatch.
12
12
  // --- Uniform entry, lift helpers, typed wrappers, composable passes (src/api.ts) ---
13
- export { parse, analyze, toAst, toScopes, qualify, lineage, deriveSymbols, TypeInfo, Lineage, originsOfExpr, lineageAt, lineageOf, tokenize, SqlDocument, LineIndex, complete, completeAt, jinjaSlotAt, signatureAt, SIGNATURES, lookupSignature, hasSignature, referencesAt, dialectSymbols, CallbackSchema, DERIVED_DIALECTS, resolveDialect, foldIdentifier, displayName, } from "./api.js";
13
+ export { parse, analyze, toAst, toScopes, qualify, lineage, deriveSymbols, TypeInfo, Lineage, originsOfExpr, lineageAt, lineageOf, tokenize, SqlDocument, LineIndex, complete, completeAt, jinjaSlotAt, signatureAt, SIGNATURES, lookupSignature, hasSignature, renderSignature, FN_DOCS, lookupFnDoc, referencesAt, dialectSymbols, CallbackSchema, DERIVED_DIALECTS, resolveDialect, foldIdentifier, displayName, } from "./api.js";
14
14
  // SqlSession — the verb-shaped facade over one SqlDocument (offset in, answer out; pure delegation).
15
15
  export { SqlSession } from "./session.js";
16
16
  // --- Per-dialect building blocks: parse* (CST + errors) and lower* (CST → IR), kept as one
@@ -44,7 +44,10 @@ export function parseType(text, aliases = SCALAR_ALIASES, foldField) {
44
44
  const raw = part.slice(0, colon).trim();
45
45
  const name = foldField ? foldField(raw) : raw;
46
46
  if (name) {
47
- fields.push({ name, type: parseType(stripComment(part.slice(colon + 1).trim()), undefined, foldField) });
47
+ fields.push({
48
+ name,
49
+ type: parseType(stripComment(part.slice(colon + 1).trim()), undefined, foldField),
50
+ });
48
51
  }
49
52
  }
50
53
  return { kind: "struct", fields };
@@ -17,8 +17,9 @@
17
17
  // TemplateProvider for a call's relation (`provider.relationOf`) and carries no ref/source vocabulary
18
18
  // itself. The NEUTRAL provider answers nothing, so a bare parse leaves calls opaque; a
19
19
  // DbtTemplateProvider names ref/source. Naming is never-wrong: a resolved name comes from the call's
20
- // literal args, and a call whose relation the provider does not resolve keeps its placeholder name and
21
- // stays opaque. We NEVER fabricate a name.
20
+ // literal args, and a call whose relation the provider does not resolve takes the RAW TAG TEXT as its
21
+ // name ({{ ref('m') }} verbatim) and stays opaque. We NEVER fabricate a name — in particular the
22
+ // placeholder fill (scaffolding this library invented) never escapes as one (issue #35).
22
23
  //
23
24
  // The IR is frozen after lower(); this transform REBUILDS with STRUCTURAL SHARING (new objects only on
24
25
  // changed paths, an unchanged subtree keeps its original already-frozen reference) and re-freezes the
@@ -152,7 +153,10 @@ function markTemplateExprs(node, ctx) {
152
153
  // never wrong.
153
154
  // ---------------------------------------------------------------------------
154
155
  /** Match a direct string-literal argument's raw text (no escapes, one token). */
155
- const LITERAL_ARG = /^(['"])([^'"\\]*)\1$/;
156
+ /** A literal argument's value: a quoted string (quote-stripped) or a bare numeric literal —
157
+ * both are the user's own text, never computed. Everything else stays `null` (computed). */
158
+ const LITERAL_ARG = /^(?:(['"])([^'"\\]*)\1|(-?\d+(?:\.\d+)?))$/;
159
+ const literalValueOf = (m) => m[2] ?? m[3];
156
160
  /** The raw text of a span. */
157
161
  function sliceSpan(text, span) {
158
162
  return text.slice(span.start, span.end);
@@ -172,11 +176,11 @@ export function callOf(mc, text) {
172
176
  const kw = KWARG_RE.exec(raw);
173
177
  if (kw) {
174
178
  const m = LITERAL_ARG.exec(kw[2].trim());
175
- kwargs.push({ name: kw[1], value: m ? m[2] : null });
179
+ kwargs.push({ name: kw[1], value: m ? literalValueOf(m) : null });
176
180
  }
177
181
  else {
178
182
  const m = LITERAL_ARG.exec(raw);
179
- args.push(m ? m[2] : null);
183
+ args.push(m ? literalValueOf(m) : null);
180
184
  }
181
185
  }
182
186
  return {
@@ -398,6 +402,11 @@ function transformTableSource(src, ctx) {
398
402
  // where `jjj…` was a fabrication.
399
403
  const aliasTok = src.aliasCst?.start;
400
404
  const base = aliasTok != null && inSpan(aliasTok.start, tag.tagSpan) ? withoutAlias(src) : src;
405
+ // An unresolved source's name is the RAW TAG TEXT — the bytes the user actually wrote. The
406
+ // placeholder fill is scaffolding this library invented so the grammar parses; letting it
407
+ // escape as a relation name (scope sources, lineage dependencies, go-to-def) is fabrication
408
+ // under the never-wrong rule (issue #35, reported by anvil).
409
+ const rawTagName = [ctx.text.slice(tag.tagSpan.start, tag.tagSpan.end)];
401
410
  // NOTE: `template.span` intentionally aliases `tag.tagSpan` BY REFERENCE. freezeIR
402
411
  // therefore also freezes the TagNode.tagSpan object returned in `.tags`, benign
403
412
  // since spans are read-only. Every call marker carries its `call`, the provider key
@@ -406,24 +415,24 @@ function transformTableSource(src, ctx) {
406
415
  const call = callOf(tag, ctx.text);
407
416
  const rel = ctx.provider.relationOf(call);
408
417
  // A call in a FROM slot (ref/source/a TVF-like macro). When the provider resolves its
409
- // relation, carry the resolved name; otherwise keep the placeholder name (never fabricated).
418
+ // relation, carry the resolved name; otherwise the raw tag text (never the fill).
410
419
  // Either way the `call` keeps it consultable, so an unresolved call is not a dead end: a
411
420
  // provider added later resolves it. ref vs source is not stored here, it is call.name.
412
- const named = rel ? { ...base, name: [...rel.nameParts] } : base;
421
+ const named = { ...base, name: rel ? [...rel.nameParts] : rawTagName };
413
422
  const template = { kind: "call", span: tag.tagSpan, call };
414
423
  return attach(ctx, { ...named, template }, tag);
415
424
  }
416
425
  // Non-call expression tag (var / env_var / other) in a FROM slot. A bare `{{ t }}` resolving
417
426
  // through a `{% set t = … %}` single-call RHS carries the resolved relation name (when the
418
427
  // provider resolved it) or the call identity alone; every other case gets the opaque "expr"
419
- // marker, so the placeholder name stops posing as a real table.
428
+ // marker. In every unresolved case the name is the raw tag text, never the fill.
420
429
  const ident = tag.kind === "other" ? bareIdentOf(tag, ctx.text) : undefined;
421
430
  const resolved = ident !== undefined ? ctx.sets.get(ident) : undefined;
422
431
  if (resolved) {
423
- const named = resolved.name ? { ...base, name: [...resolved.name] } : base;
432
+ const named = { ...base, name: resolved.name ? [...resolved.name] : rawTagName };
424
433
  const template = { kind: "call", span: tag.tagSpan, indirect: true, call: resolved.call };
425
434
  return attach(ctx, { ...named, template }, tag);
426
435
  }
427
436
  const template = { kind: "expr", span: tag.tagSpan, opaque: true };
428
- return attach(ctx, { ...base, template }, tag);
437
+ return attach(ctx, { ...base, name: rawTagName, template }, tag);
429
438
  }
@@ -0,0 +1,2 @@
1
+ import type { FnDoc } from "../signature/docs.js";
2
+ export declare const MYSQL_FN_DOCS: Record<string, FnDoc>;