sqllens 1.5.0 → 1.5.1

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.
@@ -131,7 +131,8 @@ function collect(doc, offset, schema) {
131
131
  // failure the bare-slot fallback covers. Its member twin: read `FROM/JOIN name [alias]`
132
132
  // pairs off the token stream and answer the matching relation's schema columns.
133
133
  if (scoped.length === 0 && schema) {
134
- for (const c of qualifiedFallbackColumns(walkTokens, cfg, path.parts, schema, dialect))
134
+ const fallback = qualifiedFallbackColumns(walkTokens, cfg, path.parts, schema, dialect, doc.templated?.tags, doc.text);
135
+ for (const c of fallback)
135
136
  add(c);
136
137
  }
137
138
  }
@@ -322,16 +323,45 @@ function qualifiedSourceColumns(scopes, ast, offset, qualParts, dialect, schema)
322
323
  /** The member-position twin of `fromRelationColumns` (#38): when the scope is empty (the dangling
323
324
  * dot broke the FROM parse), read `FROM/JOIN name(.name)* [AS] [alias]` off the token stream and
324
325
  * answer the columns of the ONE relation the qualifier matches — the alias when present, else the
325
- * name's own trailing parts. No match (or several) answers [] never a fabricated union. */
326
- function qualifiedFallbackColumns(walkTokens, cfg, qualParts, schema, dialect) {
326
+ * name's own trailing parts. A templated source ({{ ref() }} c) is resolved through the provider
327
+ * (relationOf), matching the qualifier to its alias — the same seam `fromRelationColumns` uses. No
328
+ * match (or several) answers [] — never a fabricated union. */
329
+ function qualifiedFallbackColumns(walkTokens, cfg, qualParts, schema, dialect, tags, text) {
327
330
  if (cfg.relationKeywordTokens.size === 0)
328
331
  return [];
329
332
  const b = resolveBehavior(dialect);
330
333
  const toks = walkTokens.filter((t) => t.channel === Token.DEFAULT_CHANNEL);
331
334
  const hits = [];
335
+ const colHits = (cols) => {
336
+ if (cols)
337
+ hits.push(cols.map((c) => ({ label: c.name, kind: "column", detail: c.type })));
338
+ };
332
339
  for (let i = 0; i + 1 < toks.length; i++) {
333
340
  if (!cfg.relationKeywordTokens.has(toks[i].type))
334
341
  continue;
342
+ // A templated source ({{ ref('customers') }} c) blanks to a channel-2 tag the filter drops, so
343
+ // the next SQL token is the ALIAS, not a relation name. Resolve the relation through the provider
344
+ // (relationOf) — the same seam fromRelationColumns uses — and match the qualifier to that alias;
345
+ // without this, the alias got read AS the relation name and columnsFor answered nothing.
346
+ const kw = toks[i];
347
+ const next = toks[i + 1];
348
+ const tag = tags?.find((t) => t.kind === "call" && t.tagSpan.start >= kw.start && t.tagSpan.start < next.start);
349
+ if (tag) {
350
+ if (schema instanceof DefaultTemplateProvider) {
351
+ let a = i + 1;
352
+ if (toks[a] && b.fold(toks[a].text) === "as")
353
+ a++;
354
+ const alias = toks[a];
355
+ if (alias &&
356
+ cfg.nameTokens.has(alias.type) &&
357
+ qualParts.length === 1 &&
358
+ b.fold(qualParts[0]) === b.fold(alias.text)) {
359
+ const rel = schema.relationOf(callOf(tag, text));
360
+ colHits(rel ? (rel.columns ?? schema.columnsFor(rel.nameParts, dialect)) : undefined);
361
+ }
362
+ }
363
+ continue; // templated source handled (or unresolvable) — never treat the alias as a relation name
364
+ }
335
365
  let j = i + 1;
336
366
  if (!toks[j] || !cfg.nameTokens.has(toks[j].type))
337
367
  continue;
@@ -352,9 +382,7 @@ function qualifiedFallbackColumns(walkTokens, cfg, qualParts, schema, dialect) {
352
382
  qualParts.every((p, k) => b.fold(p, "table") === b.fold(parts[parts.length - qualParts.length + k], "table"));
353
383
  if (!matches)
354
384
  continue;
355
- const cols = schema.columnsFor(parts, dialect);
356
- if (cols)
357
- hits.push(cols.map((c) => ({ label: c.name, kind: "column", detail: c.type })));
385
+ colHits(schema.columnsFor(parts, dialect));
358
386
  }
359
387
  return hits.length === 1 ? hits[0] : [];
360
388
  }
@@ -422,7 +422,10 @@ function relationSymbol(src, frame, dialect) {
422
422
  return {
423
423
  kind: "table",
424
424
  modifiers: ref,
425
- name: src.name.map(show).join("."),
425
+ // DISPLAY spelling, from relation.parts — NOT src.name, which is the folded identity key
426
+ // (#38). displaying the key uppercases the name on a case-folding dialect; matches the CTE
427
+ // branch's display convention and scope.d.ts's "never show name" contract.
428
+ name: src.source.relation.parts.map(show).join("."),
426
429
  span: spanOf(src.source.cst),
427
430
  frame,
428
431
  node: src.source,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqllens",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "A TypeScript SQL parser and static analyzer: parse, resolve names, infer types, and trace column lineage across many SQL dialects (Databricks, T-SQL, Snowflake, BigQuery, Redshift, PostgreSQL, DuckDB, Trino, SQLite, MySQL).",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",