sqllens 1.1.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.
Files changed (234) hide show
  1. package/README.md +2 -74
  2. package/THIRD-PARTY-NOTICES.md +50 -5
  3. package/dist/api.d.ts +5 -3
  4. package/dist/api.js +11 -4
  5. package/dist/bigquery/behavior.d.ts +2 -0
  6. package/dist/bigquery/behavior.js +20 -0
  7. package/dist/bigquery/dot-path.d.ts +0 -2
  8. package/dist/bigquery/dot-path.js +0 -1
  9. package/dist/bigquery/fold.d.ts +8 -0
  10. package/dist/bigquery/fold.js +39 -0
  11. package/dist/bigquery/index.d.ts +7 -0
  12. package/dist/bigquery/index.js +10 -0
  13. package/dist/{infer/bigquery.d.ts → bigquery/infer.d.ts} +2 -2
  14. package/dist/{infer/bigquery.js → bigquery/infer.js} +4 -3
  15. package/dist/bigquery/lower.js +35 -12
  16. package/dist/bigquery/signatures.generated.d.ts +6 -0
  17. package/dist/bigquery/signatures.generated.js +1068 -0
  18. package/dist/completion/atn-walk.d.ts +13 -2
  19. package/dist/completion/atn-walk.js +13 -10
  20. package/dist/completion/complete.d.ts +4 -3
  21. package/dist/completion/complete.js +101 -45
  22. package/dist/completion/jinja-slot.d.ts +24 -0
  23. package/dist/completion/jinja-slot.js +126 -0
  24. package/dist/completion/parser-factory.d.ts +13 -1
  25. package/dist/completion/parser-factory.js +12 -0
  26. package/dist/databricks/behavior.d.ts +2 -0
  27. package/dist/databricks/behavior.js +19 -0
  28. package/dist/databricks/fold.d.ts +8 -0
  29. package/dist/databricks/fold.js +27 -0
  30. package/dist/databricks/index.d.ts +7 -0
  31. package/dist/databricks/index.js +10 -0
  32. package/dist/databricks/infer.d.ts +6 -0
  33. package/dist/databricks/infer.js +638 -0
  34. package/dist/databricks/signatures.generated.d.ts +6 -0
  35. package/dist/databricks/signatures.generated.js +1745 -0
  36. package/dist/dialect-behavior/behavior.d.ts +26 -0
  37. package/dist/dialect-behavior/behavior.js +1 -0
  38. package/dist/dialect-behavior/carrier.d.ts +5 -0
  39. package/dist/dialect-behavior/carrier.js +5 -0
  40. package/dist/dialect-behavior/coerce-rules.d.ts +7 -0
  41. package/dist/dialect-behavior/coerce-rules.js +69 -0
  42. package/dist/dialect-behavior/public-fold.d.ts +6 -0
  43. package/dist/dialect-behavior/public-fold.js +9 -0
  44. package/dist/dialect-behavior/registry.d.ts +6 -0
  45. package/dist/dialect-behavior/registry.js +34 -0
  46. package/dist/dialect-symbols.js +16 -19
  47. package/dist/document/document.d.ts +1 -1
  48. package/dist/document/document.js +8 -7
  49. package/dist/duckdb/behavior.d.ts +2 -0
  50. package/dist/duckdb/behavior.js +21 -0
  51. package/dist/duckdb/fold.d.ts +8 -0
  52. package/dist/duckdb/fold.js +29 -0
  53. package/dist/duckdb/index.d.ts +7 -0
  54. package/dist/duckdb/index.js +10 -0
  55. package/dist/{infer/duckdb.d.ts → duckdb/infer.d.ts} +2 -2
  56. package/dist/{infer/duckdb.js → duckdb/infer.js} +4 -3
  57. package/dist/duckdb/lower.js +47 -15
  58. package/dist/duckdb/signatures.generated.d.ts +6 -0
  59. package/dist/duckdb/signatures.generated.js +1072 -0
  60. package/dist/generated/bigquery/GoogleSQLParser.js +0 -7060
  61. package/dist/generated/databricks/DatabricksParser.js +0 -4800
  62. package/dist/generated/duckdb/DuckdbParser.js +0 -9100
  63. package/dist/generated/minijinja/MinijinjaParser.js +0 -380
  64. package/dist/generated/mysql/MysqlParser.js +0 -6330
  65. package/dist/generated/postgres/PostgresParser.js +0 -8530
  66. package/dist/generated/redshift/RedshiftParser.js +0 -10970
  67. package/dist/generated/snowflake/SnowflakeParser.js +0 -7320
  68. package/dist/generated/sqlite/SqliteParser.js +0 -1150
  69. package/dist/generated/trino/TrinoParser.js +0 -3770
  70. package/dist/generated/tsql/TSqlParser.js +0 -8420
  71. package/dist/ident/fold.d.ts +24 -15
  72. package/dist/ident/fold.js +12 -194
  73. package/dist/index.d.ts +2 -2
  74. package/dist/index.js +10 -8
  75. package/dist/infer/functions.d.ts +22 -11
  76. package/dist/infer/functions.js +26 -888
  77. package/dist/infer/infer.js +16 -14
  78. package/dist/infer/nullability.js +3 -4
  79. package/dist/infer/types.d.ts +1 -1
  80. package/dist/infer/types.js +7 -7
  81. package/dist/ir/ir.d.ts +26 -17
  82. package/dist/ir/span.js +0 -2
  83. package/dist/ir/walk.js +3 -3
  84. package/dist/lineage/hops.js +13 -10
  85. package/dist/lineage/lineage.js +10 -7
  86. package/dist/minijinja/apply-tags.d.ts +14 -7
  87. package/dist/minijinja/apply-tags.js +66 -121
  88. package/dist/minijinja/parse.js +5 -5
  89. package/dist/minijinja/tag-ast.d.ts +29 -36
  90. package/dist/minijinja/tag-ast.js +210 -90
  91. package/dist/mysql/behavior.d.ts +2 -0
  92. package/dist/mysql/behavior.js +21 -0
  93. package/dist/mysql/fold.d.ts +8 -0
  94. package/dist/mysql/fold.js +49 -0
  95. package/dist/mysql/index.d.ts +7 -0
  96. package/dist/mysql/index.js +10 -0
  97. package/dist/{infer/mysql.d.ts → mysql/infer.d.ts} +2 -2
  98. package/dist/{infer/mysql.js → mysql/infer.js} +3 -2
  99. package/dist/mysql/signatures.generated.d.ts +6 -0
  100. package/dist/mysql/signatures.generated.js +508 -0
  101. package/dist/postgres/behavior.d.ts +2 -0
  102. package/dist/postgres/behavior.js +19 -0
  103. package/dist/postgres/fold.d.ts +8 -0
  104. package/dist/postgres/fold.js +30 -0
  105. package/dist/postgres/index.d.ts +7 -0
  106. package/dist/postgres/index.js +10 -0
  107. package/dist/{infer/postgres.d.ts → postgres/infer.d.ts} +2 -2
  108. package/dist/{infer/postgres.js → postgres/infer.js} +4 -3
  109. package/dist/postgres/lower.js +2 -2
  110. package/dist/postgres/signatures.generated.d.ts +6 -0
  111. package/dist/postgres/signatures.generated.js +2973 -0
  112. package/dist/qualify/check-calls.js +80 -161
  113. package/dist/qualify/qualify.js +16 -14
  114. package/dist/qualify/schema-provider.js +2 -2
  115. package/dist/qualify/schema.js +3 -3
  116. package/dist/qualify/template-provider.d.ts +45 -12
  117. package/dist/qualify/template-provider.js +69 -38
  118. package/dist/redshift/behavior.d.ts +2 -0
  119. package/dist/redshift/behavior.js +19 -0
  120. package/dist/redshift/fold.d.ts +8 -0
  121. package/dist/redshift/fold.js +31 -0
  122. package/dist/redshift/index.d.ts +7 -0
  123. package/dist/redshift/index.js +10 -0
  124. package/dist/{infer/redshift.d.ts → redshift/infer.d.ts} +2 -2
  125. package/dist/{infer/redshift.js → redshift/infer.js} +4 -3
  126. package/dist/redshift/lower.js +2 -2
  127. package/dist/redshift/signatures.generated.d.ts +6 -0
  128. package/dist/redshift/signatures.generated.js +757 -0
  129. package/dist/references/references.js +17 -12
  130. package/dist/scope/like-pattern.d.ts +2 -0
  131. package/dist/scope/like-pattern.js +15 -0
  132. package/dist/scope/scope.d.ts +5 -5
  133. package/dist/scope/scope.js +48 -42
  134. package/dist/sema/resolve.js +17 -12
  135. package/dist/session.d.ts +2 -2
  136. package/dist/signature/signature.d.ts +14 -6
  137. package/dist/signature/signature.js +30 -22
  138. package/dist/signature/signatures.d.ts +14 -12
  139. package/dist/signature/signatures.js +42 -721
  140. package/dist/snowflake/behavior.d.ts +2 -0
  141. package/dist/snowflake/behavior.js +22 -0
  142. package/dist/snowflake/fold.d.ts +8 -0
  143. package/dist/snowflake/fold.js +25 -0
  144. package/dist/snowflake/index.d.ts +7 -0
  145. package/dist/snowflake/index.js +10 -0
  146. package/dist/{infer/snowflake.d.ts → snowflake/infer.d.ts} +2 -2
  147. package/dist/{infer/snowflake.js → snowflake/infer.js} +4 -3
  148. package/dist/snowflake/lower.js +59 -19
  149. package/dist/snowflake/signatures.generated.d.ts +6 -0
  150. package/dist/snowflake/signatures.generated.js +2080 -0
  151. package/dist/sqlite/behavior.d.ts +2 -0
  152. package/dist/sqlite/behavior.js +19 -0
  153. package/dist/sqlite/fold.d.ts +8 -0
  154. package/dist/sqlite/fold.js +41 -0
  155. package/dist/sqlite/index.d.ts +7 -0
  156. package/dist/sqlite/index.js +10 -0
  157. package/dist/{infer/sqlite.d.ts → sqlite/infer.d.ts} +2 -2
  158. package/dist/{infer/sqlite.js → sqlite/infer.js} +4 -3
  159. package/dist/sqlite/signatures.generated.d.ts +6 -0
  160. package/dist/sqlite/signatures.generated.js +277 -0
  161. package/dist/symbols/symbols.js +14 -12
  162. package/dist/trino/behavior.d.ts +2 -0
  163. package/dist/trino/behavior.js +21 -0
  164. package/dist/trino/fold.d.ts +8 -0
  165. package/dist/trino/fold.js +39 -0
  166. package/dist/trino/index.d.ts +7 -0
  167. package/dist/trino/index.js +10 -0
  168. package/dist/{infer/trino.d.ts → trino/infer.d.ts} +2 -2
  169. package/dist/{infer/trino.js → trino/infer.js} +4 -3
  170. package/dist/trino/lower.js +5 -5
  171. package/dist/trino/signatures.generated.d.ts +6 -0
  172. package/dist/trino/signatures.generated.js +968 -0
  173. package/dist/tsql/behavior.d.ts +2 -0
  174. package/dist/tsql/behavior.js +20 -0
  175. package/dist/tsql/fold.d.ts +8 -0
  176. package/dist/tsql/fold.js +34 -0
  177. package/dist/tsql/index.d.ts +7 -0
  178. package/dist/tsql/index.js +10 -0
  179. package/dist/tsql/infer.d.ts +16 -0
  180. package/dist/tsql/infer.js +289 -0
  181. package/dist/tsql/lower.js +4 -4
  182. package/dist/tsql/signatures.generated.d.ts +6 -0
  183. package/dist/tsql/signatures.generated.js +640 -0
  184. package/package.json +5 -10
  185. package/dist/generated/bigquery/GoogleSQLLexer.d.ts +0 -407
  186. package/dist/generated/bigquery/GoogleSQLParser.d.ts +0 -9558
  187. package/dist/generated/bigquery/GoogleSQLParserListener.d.ts +0 -7777
  188. package/dist/generated/bigquery/GoogleSQLParserListener.js +0 -7070
  189. package/dist/generated/databricks/DatabricksLexer.d.ts +0 -566
  190. package/dist/generated/databricks/DatabricksParser.d.ts +0 -7771
  191. package/dist/generated/databricks/DatabricksParserListener.d.ts +0 -5737
  192. package/dist/generated/databricks/DatabricksParserListener.js +0 -5256
  193. package/dist/generated/duckdb/DuckdbLexer.d.ts +0 -691
  194. package/dist/generated/duckdb/DuckdbParser.d.ts +0 -13932
  195. package/dist/generated/duckdb/DuckdbParserListener.d.ts +0 -10049
  196. package/dist/generated/duckdb/DuckdbParserListener.js +0 -9138
  197. package/dist/generated/minijinja/MinijinjaLexer.d.ts +0 -108
  198. package/dist/generated/minijinja/MinijinjaParser.d.ts +0 -604
  199. package/dist/generated/minijinja/MinijinjaParserListener.d.ts +0 -449
  200. package/dist/generated/minijinja/MinijinjaParserListener.js +0 -410
  201. package/dist/generated/mysql/MysqlLexer.d.ts +0 -1198
  202. package/dist/generated/mysql/MysqlParser.d.ts +0 -10992
  203. package/dist/generated/mysql/MysqlParserListener.d.ts +0 -7592
  204. package/dist/generated/mysql/MysqlParserListener.js +0 -6958
  205. package/dist/generated/postgres/PostgresLexer.d.ts +0 -663
  206. package/dist/generated/postgres/PostgresParser.d.ts +0 -12963
  207. package/dist/generated/postgres/PostgresParserListener.d.ts +0 -9408
  208. package/dist/generated/postgres/PostgresParserListener.js +0 -8554
  209. package/dist/generated/redshift/RedshiftLexer.d.ts +0 -954
  210. package/dist/generated/redshift/RedshiftParser.d.ts +0 -16939
  211. package/dist/generated/redshift/RedshiftParserListener.d.ts +0 -12092
  212. package/dist/generated/redshift/RedshiftParserListener.js +0 -10994
  213. package/dist/generated/snowflake/SnowflakeLexer.d.ts +0 -1046
  214. package/dist/generated/snowflake/SnowflakeParser.d.ts +0 -14196
  215. package/dist/generated/snowflake/SnowflakeParserListener.d.ts +0 -8063
  216. package/dist/generated/snowflake/SnowflakeParserListener.js +0 -7330
  217. package/dist/generated/sqlite/SqliteLexer.d.ts +0 -210
  218. package/dist/generated/sqlite/SqliteParser.d.ts +0 -2115
  219. package/dist/generated/sqlite/SqliteParserListener.d.ts +0 -1276
  220. package/dist/generated/sqlite/SqliteParserListener.js +0 -1160
  221. package/dist/generated/trino/TrinoLexer.d.ts +0 -381
  222. package/dist/generated/trino/TrinoParser.d.ts +0 -5340
  223. package/dist/generated/trino/TrinoParserListener.d.ts +0 -4704
  224. package/dist/generated/trino/TrinoParserListener.js +0 -4326
  225. package/dist/generated/tsql/TSqlLexer.d.ts +0 -1278
  226. package/dist/generated/tsql/TSqlParser.d.ts +0 -17267
  227. package/dist/generated/tsql/TSqlParserListener.d.ts +0 -9697
  228. package/dist/generated/tsql/TSqlParserListener.js +0 -8854
  229. package/dist/infer/dialect.d.ts +0 -21
  230. package/dist/infer/dialect.js +0 -100
  231. package/dist/infer/literals.d.ts +0 -6
  232. package/dist/infer/literals.js +0 -44
  233. package/dist/signature/generated/tsql.d.ts +0 -3
  234. package/dist/signature/generated/tsql.js +0 -260
@@ -1,32 +1,29 @@
1
1
  // ---------------------------------------------------------------------------
2
- // R3 — templated ref/source as first-class TableSource nodes (inc2).
2
+ // R3: a templated call in a FROM slot as a first-class TableSource node (inc2).
3
3
  //
4
- // `{{ ref('x') }}` / `{{ source('a','b') }}` in a FROM/JOIN slot lowers, via the
5
- // placeholder mechanism, to an ordinary `TableSource` whose `name` is the raw
6
- // placeholder identifier (PLACEHOLDER_CHAR-filled runs like `jjj…` from segment.ts).
7
- // This POST-LOWER transform rewrites those sources so `name` carries the dbt-logical
8
- // model/source names, and attaches a `template` marker — so scope/qualify/lineage
9
- // see the model, not the placeholder (scope binds a TableSource purely by `name`,
10
- // so the whole downstream pipeline works UNCHANGED).
4
+ // `{{ ref('x') }}` / `{{ source('a','b') }}` / a macro call in a FROM/JOIN slot lowers, via the
5
+ // placeholder mechanism, to an ordinary `TableSource` whose `name` is the raw placeholder identifier
6
+ // (PLACEHOLDER_CHAR-filled runs like `jjj…` from segment.ts). This POST-LOWER transform rewrites those
7
+ // sources so `name` carries the provider-resolved relation name and attaches a `template` marker, so
8
+ // scope/qualify/lineage see the relation, not the placeholder (scope binds a TableSource purely by
9
+ // `name`, so the whole downstream pipeline works UNCHANGED).
11
10
  //
12
- // Correlation is by CONTAINMENT, not equality: a `TableSource` correlates with a
13
- // tag when the char offset of its FIRST NAME TOKEN (`cst.start.start`) lies inside
14
- // the tag's `tagSpan` [start, end). Containment (not equality) because a multi-line
15
- // expr tag fills ONE placeholder identifier per line — the name token covers only
16
- // the first line, but its offset still sits inside the whole-tag span.
11
+ // Correlation is by CONTAINMENT, not equality: a `TableSource` correlates with a tag when the char
12
+ // offset of its FIRST NAME TOKEN (`cst.start.start`) lies inside the tag's `tagSpan` [start, end).
13
+ // Containment (not equality) because a multi-line expr tag fills ONE placeholder identifier per line,
14
+ // so the name token covers only the first line but its offset still sits inside the whole-tag span.
17
15
  //
18
- // Substitution is LITERAL-ONLY (never-wrong): inc1's `directStringToken` guard
19
- // already guarantees a ref/source TagNode carries only literal names, so a `ref`
20
- // node's `model` and a `source` node's `sourceName`/`tableName` are real literals.
21
- // A macro (or computed) call in a FROM slot keeps its placeholder name and gets
22
- // `opaque: true` — its physical relation is undeterminable without the catalog;
23
- // we NEVER fabricate a name for it.
16
+ // The tag AST is NEUTRAL (a call is a call), and so is the naming: this transform asks the
17
+ // TemplateProvider for a call's relation (`provider.relationOf`) and carries no ref/source vocabulary
18
+ // itself. The NEUTRAL provider answers nothing, so a bare parse leaves calls opaque; a
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.
24
22
  //
25
- // The IR is frozen after lower(); this transform REBUILDS with STRUCTURAL SHARING
26
- // (new objects only on changed paths — an unchanged subtree keeps its original,
27
- // already-frozen reference) and re-freezes the rebuilt tree. It NEVER mutates a
28
- // frozen node. Total: never throws — on any internal surprise it returns the input
29
- // `ast` unchanged.
23
+ // The IR is frozen after lower(); this transform REBUILDS with STRUCTURAL SHARING (new objects only on
24
+ // changed paths, an unchanged subtree keeps its original already-frozen reference) and re-freezes the
25
+ // rebuilt tree. It NEVER mutates a frozen node. Total: never throws, on any internal surprise it
26
+ // returns the input `ast` unchanged.
30
27
  // ---------------------------------------------------------------------------
31
28
  import { freezeIR } from "../ir/freeze.js";
32
29
  /** Record a freshly built node's correlation to the tag it came from, then return it unchanged
@@ -43,27 +40,23 @@ function attach(ctx, node, tag) {
43
40
  return node;
44
41
  }
45
42
  /**
46
- * Rewrite templated FROM/JOIN sources in `ast` to carry their dbt-logical name +
47
- * a `template` marker, correlating each source to a tag by span containment.
48
- * Returns the SAME `ast` reference when nothing correlates (structural sharing);
49
- * returns a re-frozen rebuilt tree otherwise. Total — never throws; the
50
- * correlation maps are empty (not absent) on the no-op and error paths.
43
+ * Rewrite templated FROM/JOIN sources in `ast` to carry their provider-resolved name (when the
44
+ * provider resolves the call) plus a `template` marker, correlating each source to a tag by span
45
+ * containment. Returns the SAME `ast` reference when nothing correlates (structural sharing);
46
+ * returns a re-frozen rebuilt tree otherwise. Total, never throws; the correlation maps are empty
47
+ * (not absent) on the no-op and error paths.
51
48
  */
52
- export function applyTemplateTags(ast, tags, text) {
49
+ export function applyTemplateTags(ast, tags, text, provider) {
53
50
  const byNode = new WeakMap();
54
51
  const byTag = new Map();
55
52
  try {
56
- // config is a no-output tag (whitespace-filled) — it can never yield a table
57
- // source, so it stays out of the correlation set even though ExprTag admits it.
58
- const relTags = tags.filter((t) => t.kind === "ref" ||
59
- t.kind === "source" ||
60
- t.kind === "macro" ||
61
- t.kind === "var" ||
62
- t.kind === "env_var" ||
63
- t.kind === "other");
53
+ // config is a no-output tag (whitespace-filled), so it can never yield a table
54
+ // source and stays out of the correlation set even though ExprTag admits it.
55
+ // An incomplete/mid-typing call (`{{ ref('cu`) is NOT a resolved source, so skip it.
56
+ const relTags = tags.filter((t) => (t.kind === "call" && !t.incomplete) || t.kind === "other");
64
57
  if (relTags.length === 0)
65
58
  return { ast, byNode, byTag };
66
- const ctx = { relTags, sets: resolveSets(tags, text), text, byNode, byTag };
59
+ const ctx = { relTags, sets: resolveSets(tags, text, provider), text, provider, byNode, byTag };
67
60
  const next = transformQuery(ast, ctx);
68
61
  // Scalar-slot marking: every column-shaped node whose token is a tag's placeholder
69
62
  // fill gets a `template` marker (span + provider key), so inference resolves it
@@ -79,23 +72,12 @@ export function applyTemplateTags(ast, tags, text) {
79
72
  * its provider key — ref/source from their literal names, macro from its call fields,
80
73
  * var/env_var lexically, a bare set-bound variable from its RHS call. */
81
74
  function exprInfoOf(tag, ctx) {
82
- if (tag.kind === "ref")
83
- return { span: tag.tagSpan, call: { name: "ref", args: [tag.model] } };
84
- if (tag.kind === "source") {
85
- return { span: tag.tagSpan, call: { name: "source", args: [tag.sourceName, tag.tableName] } };
86
- }
87
- if (tag.kind === "macro")
75
+ // A call tag (ref/source/var/env_var/a macro) carries its provider key straight off the call,
76
+ // callOf reads name + literal args from the source, uniform across every callee.
77
+ if (tag.kind === "call")
88
78
  return { span: tag.tagSpan, call: callOf(tag, ctx.text) };
89
- if (tag.kind === "var" || tag.kind === "env_var") {
90
- const m = VALUE_CALL_TAG.exec(sliceSpan(ctx.text, tag.tagSpan));
91
- if (m) {
92
- const args = [m[3]];
93
- if (m[4] !== undefined)
94
- args.push(null); // further args present but not extracted — computed
95
- return { span: tag.tagSpan, call: { name: m[1], args } };
96
- }
97
- return { span: tag.tagSpan };
98
- }
79
+ // A non-call `other` tag: a bare `{{ t }}` resolving through a single-call `{% set t = … %}`
80
+ // carries that RHS call; anything else is opaque.
99
81
  const ident = bareIdentOf(tag, ctx.text);
100
82
  const resolved = ident !== undefined ? ctx.sets.get(ident) : undefined;
101
83
  if (resolved)
@@ -175,14 +157,6 @@ const LITERAL_ARG = /^(['"])([^'"\\]*)\1$/;
175
157
  function sliceSpan(text, span) {
176
158
  return text.slice(span.start, span.end);
177
159
  }
178
- /** The literal string value of a MacroCall argument, or undefined when computed. */
179
- function literalArg(text, call, i) {
180
- const arg = call.args[i];
181
- if (!arg)
182
- return undefined;
183
- const m = LITERAL_ARG.exec(sliceSpan(text, arg.span).trim());
184
- return m ? m[2] : undefined;
185
- }
186
160
  /** A kwarg's raw text split: `name = rest`, with `rest`'s literal (or null when computed). */
187
161
  const KWARG_RE = /^([A-Za-z_][A-Za-z0-9_]*)\s*=(?!=)([\s\S]*)$/;
188
162
  /**
@@ -190,7 +164,7 @@ const KWARG_RE = /^([A-Za-z_][A-Za-z0-9_]*)\s*=(?!=)([\s\S]*)$/;
190
164
  * carried separately (the channel-agreed TemplateCall contract: quote-stripped, escapes
191
165
  * NOT resolved, computed arguments null, kwargs never dropped).
192
166
  */
193
- function callOf(mc, text) {
167
+ export function callOf(mc, text) {
194
168
  const args = [];
195
169
  const kwargs = [];
196
170
  for (const arg of mc.args) {
@@ -217,11 +191,11 @@ function callOf(mc, text) {
217
191
  * Requires the tag's call list to be exactly one call that IS the ENTIRE RHS: the
218
192
  * text between the declared name and the call is exactly `=`, and the text after
219
193
  * the call runs straight to the tag close (whitespace + optional `-%}`).
220
- * `ref('a') ~ '_x'` or `ref('a').identifier` therefore do not resolve. A literal
221
- * ref/source RHS resolves with its NAME (scope binds the real model); any other
222
- * single-call RHS resolves as a macro identity (the provider may know its relation).
194
+ * `ref('a') ~ '_x'` or `ref('a').identifier` therefore do not resolve. When the provider resolves the
195
+ * call's relation, the set carries that NAME (scope binds the real relation); otherwise it carries the
196
+ * call identity only, and the provider may still resolve its relation at the use site.
223
197
  */
224
- function setResolution(tag, text) {
198
+ function setResolution(tag, text, provider) {
225
199
  if (tag.calls.length !== 1 || !tag.nameSpan)
226
200
  return undefined;
227
201
  const call = tag.calls[0];
@@ -234,19 +208,11 @@ function setResolution(tag, text) {
234
208
  if (!/^\s*-?%\}$/.test(text.slice(callEnd, tag.tagSpan.end)))
235
209
  return undefined;
236
210
  const tCall = callOf(call, text);
237
- if (call.packageName === undefined && call.name === "ref" && call.args.length === 1) {
238
- const model = literalArg(text, call, 0);
239
- return model !== undefined ? { kind: "ref", name: [model], call: tCall } : undefined;
240
- }
241
- if (call.packageName === undefined && call.name === "source" && call.args.length === 2) {
242
- const src = literalArg(text, call, 0);
243
- const tbl = literalArg(text, call, 1);
244
- return src !== undefined && tbl !== undefined ? { kind: "source", name: [src, tbl], call: tCall } : undefined;
245
- }
246
- return { kind: "macro", call: tCall };
211
+ const rel = provider.relationOf(tCall);
212
+ return rel ? { name: [...rel.nameParts], call: tCall } : { call: tCall };
247
213
  }
248
214
  /** The template-wide map of resolvable set variables (empty when any guard trips globally). */
249
- function resolveSets(tags, text) {
215
+ function resolveSets(tags, text, provider) {
250
216
  const empty = new Map();
251
217
  const controls = tags.filter((t) => t.kind === "control");
252
218
  if (controls.some((c) => c.keyword === "macro"))
@@ -258,7 +224,7 @@ function resolveSets(tags, text) {
258
224
  if (c.keyword !== "set" || !c.name)
259
225
  continue;
260
226
  counts.set(c.name, (counts.get(c.name) ?? 0) + 1);
261
- const r = setResolution(c, text);
227
+ const r = setResolution(c, text, provider);
262
228
  if (r)
263
229
  out.set(c.name, r);
264
230
  }
@@ -433,51 +399,30 @@ function transformTableSource(src, ctx) {
433
399
  const aliasTok = src.aliasCst?.start;
434
400
  const base = aliasTok != null && inSpan(aliasTok.start, tag.tagSpan) ? withoutAlias(src) : src;
435
401
  // NOTE: `template.span` intentionally aliases `tag.tagSpan` BY REFERENCE. freezeIR
436
- // therefore also freezes the TagNode.tagSpan object returned in `.tags` — benign,
437
- // since spans are read-only. Every marker with a resolvable identity carries its
438
- // `call` — the provider key the semantic layer consults (relation-columns.ts).
439
- if (tag.kind === "ref") {
440
- const call = { name: "ref", args: [tag.model] };
441
- const template = { kind: "ref", span: tag.tagSpan, call };
442
- return attach(ctx, { ...base, name: [tag.model], template }, tag);
443
- }
444
- if (tag.kind === "source") {
445
- const call = { name: "source", args: [tag.sourceName, tag.tableName] };
446
- const template = { kind: "source", span: tag.tagSpan, call };
447
- return attach(ctx, { ...base, name: [tag.sourceName, tag.tableName], template }, tag);
402
+ // therefore also freezes the TagNode.tagSpan object returned in `.tags`, benign
403
+ // since spans are read-only. Every call marker carries its `call`, the provider key
404
+ // the semantic layer resolves the relation and its columns through (relation-columns.ts).
405
+ if (tag.kind === "call") {
406
+ const call = callOf(tag, ctx.text);
407
+ const rel = ctx.provider.relationOf(call);
408
+ // 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).
410
+ // Either way the `call` keeps it consultable, so an unresolved call is not a dead end: a
411
+ // 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;
413
+ const template = { kind: "call", span: tag.tagSpan, call };
414
+ return attach(ctx, { ...named, template }, tag);
448
415
  }
449
- if (tag.kind === "macro") {
450
- // A macro in a FROM slot keeps the placeholder name (never fabricated) but is no
451
- // longer a dead end: its `call` lets a provider resolve the relation it produces
452
- // (a TVF-like macro). Without a provider answer it behaves exactly like the old
453
- // opaque marker (exemption, no diagnostics).
454
- const template = { kind: "macro", span: tag.tagSpan, call: callOf(tag, ctx.text) };
455
- return attach(ctx, { ...base, template }, tag);
456
- }
457
- // Non-call expression tag (var / env_var / other) in a FROM slot. A bare `{{ t }}`
458
- // resolving through a `{% set t = … %}` single-call RHS binds the real model
459
- // (ref/source) or carries the call identity (macro); everything else gets the opaque
460
- // "expr" marker — either way the placeholder name stops posing as a real table.
416
+ // Non-call expression tag (var / env_var / other) in a FROM slot. A bare `{{ t }}` resolving
417
+ // through a `{% set t = … %}` single-call RHS carries the resolved relation name (when the
418
+ // 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.
461
420
  const ident = tag.kind === "other" ? bareIdentOf(tag, ctx.text) : undefined;
462
421
  const resolved = ident !== undefined ? ctx.sets.get(ident) : undefined;
463
- if (resolved && resolved.kind !== "macro") {
464
- const template = {
465
- kind: resolved.kind,
466
- span: tag.tagSpan,
467
- indirect: true,
468
- call: resolved.call,
469
- };
470
- return attach(ctx, { ...base, name: [...resolved.name], template }, tag);
471
- }
472
422
  if (resolved) {
473
- // set-var bound to a non-ref/source call: macro semantics at the use site.
474
- const template = {
475
- kind: "macro",
476
- span: tag.tagSpan,
477
- indirect: true,
478
- call: resolved.call,
479
- };
480
- return attach(ctx, { ...base, template }, tag);
423
+ const named = resolved.name ? { ...base, name: [...resolved.name] } : base;
424
+ const template = { kind: "call", span: tag.tagSpan, indirect: true, call: resolved.call };
425
+ return attach(ctx, { ...named, template }, tag);
481
426
  }
482
427
  const template = { kind: "expr", span: tag.tagSpan, opaque: true };
483
428
  return attach(ctx, { ...base, template }, tag);
@@ -282,7 +282,7 @@ function build(text, dialect, provider) {
282
282
  jinjaTokens.push(mapSliceToken(tok));
283
283
  }
284
284
  const { tree, diagnostics } = parseSliceTag(slice);
285
- const tag = tagNodesOf(seg, tree);
285
+ const tag = tagNodesOf(seg, tree, slice);
286
286
  if (tag) {
287
287
  tags.push(tag);
288
288
  segToTag.set(seg, tag);
@@ -292,12 +292,12 @@ function build(text, dialect, provider) {
292
292
  jinjaDiagnostics.push(...diagnostics);
293
293
  }
294
294
  // Step 5b (R3): rewrite templated FROM/JOIN sources onto first-class TableSource
295
- // nodes carrying the dbt-logical model/source name + a `template` marker, so
296
- // scope/qualify/lineage bind the real model rather than the `jjj…` placeholder.
295
+ // nodes carrying the provider-resolved relation name and a `template` marker, so
296
+ // scope/qualify/lineage bind the real relation rather than the `jjj…` placeholder.
297
297
  // Total (returns the input ast on any surprise); the reassignment stays inside
298
298
  // build()'s caller try/catch so parseTemplated's totality holds. `correlation`
299
- // carries the Task 10 tag↔IR-node join collected while rebuilding.
300
- const correlation = applyTemplateTags(sql.ast, tags, text);
299
+ // carries the Task 10 tag to IR-node join collected while rebuilding.
300
+ const correlation = applyTemplateTags(sql.ast, tags, text, provider);
301
301
  const sqlResult = { ...sql, ast: correlation.ast };
302
302
  // Step 4c: merge into one source-ordered stream. SQL and jinja token spans are
303
303
  // disjoint (tag-contained SQL tokens were dropped), so a stable sort by start
@@ -1,14 +1,27 @@
1
- import { ParserRuleContext } from "antlr4ng";
2
- import { CallExprContext } from "../generated/minijinja/MinijinjaParser.js";
1
+ import { ParserRuleContext, Token as AntlrToken } from "antlr4ng";
3
2
  import type { PartSpan } from "../ir/part-span.js";
4
3
  import { type Segment } from "./segment.js";
5
4
  /** A tag segment (the `kind: "tag"` arm of Segment). */
6
5
  type TagSegment = Extract<Segment, {
7
6
  kind: "tag";
8
7
  }>;
8
+ /**
9
+ * One argument of a call, in the same spirit as a SQL function-call's args. `span` is the WHOLE
10
+ * argument (quote-included: `'orders'`, or `model='orders'` for a kwarg). `value` is the literal
11
+ * value quote-stripped (`orders`), or `null` for a computed arg (`ref(var('x'))`, `'a' ~ b`), never
12
+ * fabricated. `valueSpan` is the quote-EXCLUDED span of that literal value (covers `orders`, not
13
+ * `'orders'`), present only when there's a literal value. So a consumer that knows a call's arg roles
14
+ * (e.g. dbt: `ref`'s model is the last arg) reads the value + valueSpan straight off here, exactly
15
+ * how the old `ref.model`/`modelSpan` read.
16
+ */
17
+ export interface TagArg {
18
+ value: string | null;
19
+ span: PartSpan;
20
+ valueSpan?: PartSpan;
21
+ }
9
22
  /**
10
23
  * The reusable call fields the extension consumes for signature-help / hover — the
11
- * macro TagNode's fields minus `kind`/`tagSpan`. A `{{ }}` macro node IS a
24
+ * call TagNode's fields minus `kind`/`tagSpan`. A `{{ }}` call node IS a
12
25
  * MacroCall + kind/tagSpan; a `{% %}` control tag carries an array of them
13
26
  * (`calls`). Every field comes only from real identifier tokens (never-wrong): a
14
27
  * computed / dynamic callee yields no MacroCall.
@@ -19,9 +32,7 @@ export interface MacroCall {
19
32
  packageName?: string;
20
33
  packageSpan?: PartSpan;
21
34
  argsSpan?: PartSpan;
22
- args: {
23
- span: PartSpan;
24
- }[];
35
+ args: TagArg[];
25
36
  }
26
37
  /**
27
38
  * R2 tag-AST node. The ref/source/macro arms carry the span contract the
@@ -30,31 +41,16 @@ export interface MacroCall {
30
41
  * 0-based offsets — sqllens convention).
31
42
  */
32
43
  export type TagNode = {
33
- kind: "ref";
34
- model: string;
35
- modelSpan: PartSpan;
36
- callSpan: PartSpan;
37
- tagSpan: PartSpan;
38
- } | {
39
- kind: "source";
40
- sourceName: string;
41
- tableName: string;
42
- sourceNameSpan: PartSpan;
43
- tableNameSpan: PartSpan;
44
- /** Span of the whole `source(…)` call — the sibling of `ref`'s `callSpan`; the extension hit-tests the bare `source` identifier / call on it. */
45
- callSpan: PartSpan;
46
- tagSpan: PartSpan;
47
- } | {
48
- kind: "macro";
44
+ kind: "call";
49
45
  name: string;
50
46
  nameSpan: PartSpan;
51
47
  packageName?: string;
52
48
  packageSpan?: PartSpan;
49
+ /** The whole call `name(args)` (name/package through the close paren). */
50
+ callSpan: PartSpan;
53
51
  tagSpan: PartSpan;
54
52
  argsSpan?: PartSpan;
55
- args: {
56
- span: PartSpan;
57
- }[];
53
+ args: TagArg[];
58
54
  /**
59
55
  * Every macro CALL in the expression, in source order, EACH as its own
60
56
  * MacroCall — symmetric to `control.calls` (C1). A NESTED `outer(inner())`
@@ -66,6 +62,12 @@ export type TagNode = {
66
62
  * uniformly for control AND macro nodes.
67
63
  */
68
64
  calls: MacroCall[];
65
+ /** Present (true) when the call is UNCLOSED / mid-typing (`{{ ref('cu`) and was recovered
66
+ * from the raw tokens because the parser could not build a complete call node. Its args are
67
+ * best-effort (a partial string arg has `value: null`); a consumer knows not to treat it as
68
+ * a confirmed reference, and completion uses it to place the caret's arg slot. Absent on a
69
+ * normally-parsed (closed) call. */
70
+ incomplete?: true;
69
71
  } | {
70
72
  kind: "control";
71
73
  tagSpan: PartSpan;
@@ -85,18 +87,9 @@ export type TagNode = {
85
87
  */
86
88
  calls: MacroCall[];
87
89
  } | {
88
- kind: "var" | "env_var" | "config" | "other";
90
+ kind: "other";
89
91
  tagSpan: PartSpan;
90
92
  };
91
- /**
92
- * Extract a CallExprContext into the reusable MacroCall fields — name/nameSpan +
93
- * optional package via `decomposeCallee`, args[] + argsSpan via `argInfo`. Returns
94
- * undefined when the callee is not a real, locatable identifier (decomposeCallee
95
- * undefined, or no nameSpan): a computed / dynamic callee is skipped, never
96
- * fabricated (never-wrong). The `{{ }}` macro node and the `{% %}` control tag both
97
- * build from this, so a call surfaces identically wherever it appears.
98
- */
99
- export declare function callToMacroCall(call: CallExprContext): MacroCall | undefined;
100
93
  /**
101
94
  * Build the R2 tag-AST node for ONE tag. `seg` carries the tag's document range
102
95
  * (`seg.start`/`seg.end`, used for `tagSpan`'s offsets — its own line/column come
@@ -105,5 +98,5 @@ export declare function callToMacroCall(call: CallExprContext): MacroCall | unde
105
98
  * offset/anchor composition anywhere in this file). Returns undefined only for an
106
99
  * empty/degenerate tree (never throws).
107
100
  */
108
- export declare function tagNodesOf(seg: TagSegment, tree: ParserRuleContext): TagNode | undefined;
101
+ export declare function tagNodesOf(seg: TagSegment, tree: ParserRuleContext, tokens?: readonly AntlrToken[]): TagNode | undefined;
109
102
  export {};