supaschema 0.1.0-rc.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.
Files changed (257) hide show
  1. package/.agents/skills/supaschema/SKILL.md +61 -0
  2. package/.claude/hooks/block-generated-migration-edits.mjs +32 -0
  3. package/.claude/rules/supaschema.md +22 -0
  4. package/.claude/settings.json +16 -0
  5. package/.claude/skills/supaschema/SKILL.md +61 -0
  6. package/.codex/hooks/supaschema-tool-gate.mjs +73 -0
  7. package/.codex/hooks.json +16 -0
  8. package/.codex/rules/supaschema.rules +22 -0
  9. package/AGENTS.md +40 -0
  10. package/LICENSE +661 -0
  11. package/LICENSE-COMMERCIAL.md +35 -0
  12. package/README.md +249 -0
  13. package/benchmarks/README.md +104 -0
  14. package/benchmarks/compare.js +489 -0
  15. package/benchmarks/fixtures/additive/from.sql +8 -0
  16. package/benchmarks/fixtures/additive/manifest.json +1 -0
  17. package/benchmarks/fixtures/additive/to.sql +9 -0
  18. package/benchmarks/fixtures/functions-policies/from.sql +24 -0
  19. package/benchmarks/fixtures/functions-policies/manifest.json +5 -0
  20. package/benchmarks/fixtures/functions-policies/to.sql +24 -0
  21. package/benchmarks/plot-lib.js +234 -0
  22. package/benchmarks/plot-svg.js +339 -0
  23. package/benchmarks/plot.js +154 -0
  24. package/benchmarks/tools/bench-all.sh +49 -0
  25. package/benchmarks/tools/build-project-fixture.mjs +245 -0
  26. package/benchmarks/tools/compare-db.mjs +101 -0
  27. package/benchmarks/tools/compare-fixtures.mjs +84 -0
  28. package/benchmarks/tools/compare-report.mjs +90 -0
  29. package/benchmarks/tools/compare-supabase.mjs +67 -0
  30. package/benchmarks/tools/registry.js +266 -0
  31. package/benchmarks/tools/run-workflow.mjs +77 -0
  32. package/bin/postinstall.mjs +26 -0
  33. package/bin/supaschema +2 -0
  34. package/config-schema.json +208 -0
  35. package/corpus/supabase-style/corpus.json +6 -0
  36. package/corpus/supabase-style/migrations/20260101000000_init.sql +28 -0
  37. package/corpus/supabase-style/migrations/20260102000000_noise.sql +13 -0
  38. package/corpus/supabase-style/migrations/20260103000000_churn.sql +4 -0
  39. package/corpus/supabase-style/migrations/20260104000000_triggers.sql +17 -0
  40. package/corpus/supabase-style/roles.sql +13 -0
  41. package/corpus/supabase-style/tree/functions.sql +26 -0
  42. package/corpus/supabase-style/tree/policies.sql +4 -0
  43. package/corpus/supabase-style/tree/schema.sql +4 -0
  44. package/corpus/supabase-style/tree/tables.sql +20 -0
  45. package/corpus/supabase-style/tree/triggers.sql +3 -0
  46. package/corpus/supabase-style/tree/types.sql +2 -0
  47. package/corpus/supabase-style/tree/views.sql +6 -0
  48. package/dist/audit.d.ts +20 -0
  49. package/dist/audit.d.ts.map +1 -0
  50. package/dist/audit.js +68 -0
  51. package/dist/benchmark-db.d.ts +5 -0
  52. package/dist/benchmark-db.d.ts.map +1 -0
  53. package/dist/benchmark-db.js +71 -0
  54. package/dist/benchmark-fixtures.d.ts +10 -0
  55. package/dist/benchmark-fixtures.d.ts.map +1 -0
  56. package/dist/benchmark-fixtures.js +201 -0
  57. package/dist/benchmark.d.ts +2 -0
  58. package/dist/benchmark.d.ts.map +1 -0
  59. package/dist/benchmark.js +308 -0
  60. package/dist/catalog-comments.d.ts +9 -0
  61. package/dist/catalog-comments.d.ts.map +1 -0
  62. package/dist/catalog-comments.js +194 -0
  63. package/dist/catalog-extras.d.ts +12 -0
  64. package/dist/catalog-extras.d.ts.map +1 -0
  65. package/dist/catalog-extras.js +408 -0
  66. package/dist/catalog-foreign.d.ts +15 -0
  67. package/dist/catalog-foreign.d.ts.map +1 -0
  68. package/dist/catalog-foreign.js +114 -0
  69. package/dist/catalog-tables.d.ts +9 -0
  70. package/dist/catalog-tables.d.ts.map +1 -0
  71. package/dist/catalog-tables.js +114 -0
  72. package/dist/catalog.d.ts +8 -0
  73. package/dist/catalog.d.ts.map +1 -0
  74. package/dist/catalog.js +351 -0
  75. package/dist/check-hazards.d.ts +7 -0
  76. package/dist/check-hazards.d.ts.map +1 -0
  77. package/dist/check-hazards.js +83 -0
  78. package/dist/check-reporters.d.ts +8 -0
  79. package/dist/check-reporters.d.ts.map +1 -0
  80. package/dist/check-reporters.js +76 -0
  81. package/dist/check.d.ts +3 -0
  82. package/dist/check.d.ts.map +1 -0
  83. package/dist/check.js +229 -0
  84. package/dist/cli-defaults.d.ts +24 -0
  85. package/dist/cli-defaults.d.ts.map +1 -0
  86. package/dist/cli-defaults.js +65 -0
  87. package/dist/cli-diff.d.ts +13 -0
  88. package/dist/cli-diff.d.ts.map +1 -0
  89. package/dist/cli-diff.js +348 -0
  90. package/dist/cli-reports.d.ts +9 -0
  91. package/dist/cli-reports.d.ts.map +1 -0
  92. package/dist/cli-reports.js +90 -0
  93. package/dist/cli-tools.d.ts +17 -0
  94. package/dist/cli-tools.d.ts.map +1 -0
  95. package/dist/cli-tools.js +136 -0
  96. package/dist/cli.d.ts +2 -0
  97. package/dist/cli.d.ts.map +1 -0
  98. package/dist/cli.js +239 -0
  99. package/dist/config-schema-gen.d.ts +2 -0
  100. package/dist/config-schema-gen.d.ts.map +1 -0
  101. package/dist/config-schema-gen.js +11 -0
  102. package/dist/config.d.ts +58 -0
  103. package/dist/config.d.ts.map +1 -0
  104. package/dist/config.js +132 -0
  105. package/dist/core.d.ts +115 -0
  106. package/dist/core.d.ts.map +1 -0
  107. package/dist/core.js +1 -0
  108. package/dist/corpus.d.ts +26 -0
  109. package/dist/corpus.d.ts.map +1 -0
  110. package/dist/corpus.js +112 -0
  111. package/dist/database-url.d.ts +8 -0
  112. package/dist/database-url.d.ts.map +1 -0
  113. package/dist/database-url.js +74 -0
  114. package/dist/db-admin.d.ts +23 -0
  115. package/dist/db-admin.d.ts.map +1 -0
  116. package/dist/db-admin.js +147 -0
  117. package/dist/diagnostics.d.ts +16 -0
  118. package/dist/diagnostics.d.ts.map +1 -0
  119. package/dist/diagnostics.js +155 -0
  120. package/dist/diff-score.d.ts +12 -0
  121. package/dist/diff-score.d.ts.map +1 -0
  122. package/dist/diff-score.js +339 -0
  123. package/dist/doctor.d.ts +17 -0
  124. package/dist/doctor.d.ts.map +1 -0
  125. package/dist/doctor.js +110 -0
  126. package/dist/hash.d.ts +7 -0
  127. package/dist/hash.d.ts.map +1 -0
  128. package/dist/hash.js +34 -0
  129. package/dist/index.d.ts +24 -0
  130. package/dist/index.d.ts.map +1 -0
  131. package/dist/index.js +17 -0
  132. package/dist/lineage.d.ts +23 -0
  133. package/dist/lineage.d.ts.map +1 -0
  134. package/dist/lineage.js +61 -0
  135. package/dist/migrations-status.d.ts +35 -0
  136. package/dist/migrations-status.d.ts.map +1 -0
  137. package/dist/migrations-status.js +131 -0
  138. package/dist/plan-order.d.ts +4 -0
  139. package/dist/plan-order.d.ts.map +1 -0
  140. package/dist/plan-order.js +178 -0
  141. package/dist/planner-replace.d.ts +4 -0
  142. package/dist/planner-replace.d.ts.map +1 -0
  143. package/dist/planner-replace.js +76 -0
  144. package/dist/planner-table.d.ts +3 -0
  145. package/dist/planner-table.d.ts.map +1 -0
  146. package/dist/planner-table.js +165 -0
  147. package/dist/planner.d.ts +5 -0
  148. package/dist/planner.d.ts.map +1 -0
  149. package/dist/planner.js +385 -0
  150. package/dist/render-guards.d.ts +12 -0
  151. package/dist/render-guards.d.ts.map +1 -0
  152. package/dist/render-guards.js +159 -0
  153. package/dist/render.d.ts +7 -0
  154. package/dist/render.d.ts.map +1 -0
  155. package/dist/render.js +325 -0
  156. package/dist/selfcheck.d.ts +11 -0
  157. package/dist/selfcheck.d.ts.map +1 -0
  158. package/dist/selfcheck.js +43 -0
  159. package/dist/source-normalize.d.ts +14 -0
  160. package/dist/source-normalize.d.ts.map +1 -0
  161. package/dist/source-normalize.js +420 -0
  162. package/dist/source.d.ts +4 -0
  163. package/dist/source.d.ts.map +1 -0
  164. package/dist/source.js +233 -0
  165. package/dist/sql/ast.d.ts +42 -0
  166. package/dist/sql/ast.d.ts.map +1 -0
  167. package/dist/sql/ast.js +241 -0
  168. package/dist/sql/canonical-nodes.d.ts +5 -0
  169. package/dist/sql/canonical-nodes.d.ts.map +1 -0
  170. package/dist/sql/canonical-nodes.js +101 -0
  171. package/dist/sql/extract-helpers.d.ts +18 -0
  172. package/dist/sql/extract-helpers.d.ts.map +1 -0
  173. package/dist/sql/extract-helpers.js +127 -0
  174. package/dist/sql/extract.d.ts +13 -0
  175. package/dist/sql/extract.d.ts.map +1 -0
  176. package/dist/sql/extract.js +323 -0
  177. package/dist/sql/facts.d.ts +34 -0
  178. package/dist/sql/facts.d.ts.map +1 -0
  179. package/dist/sql/facts.js +392 -0
  180. package/dist/sql/identifiers.d.ts +13 -0
  181. package/dist/sql/identifiers.d.ts.map +1 -0
  182. package/dist/sql/identifiers.js +83 -0
  183. package/dist/sql/normalize-deparse.d.ts +25 -0
  184. package/dist/sql/normalize-deparse.d.ts.map +1 -0
  185. package/dist/sql/normalize-deparse.js +96 -0
  186. package/dist/sql/object-hash.d.ts +5 -0
  187. package/dist/sql/object-hash.d.ts.map +1 -0
  188. package/dist/sql/object-hash.js +24 -0
  189. package/dist/sql/parser.d.ts +8 -0
  190. package/dist/sql/parser.d.ts.map +1 -0
  191. package/dist/sql/parser.js +89 -0
  192. package/dist/sql/privileges.d.ts +33 -0
  193. package/dist/sql/privileges.d.ts.map +1 -0
  194. package/dist/sql/privileges.js +379 -0
  195. package/dist/sql/split.d.ts +3 -0
  196. package/dist/sql/split.d.ts.map +1 -0
  197. package/dist/sql/split.js +182 -0
  198. package/dist/sql/statements.d.ts +17 -0
  199. package/dist/sql/statements.d.ts.map +1 -0
  200. package/dist/sql/statements.js +284 -0
  201. package/dist/sql/table-constraints.d.ts +15 -0
  202. package/dist/sql/table-constraints.d.ts.map +1 -0
  203. package/dist/sql/table-constraints.js +304 -0
  204. package/dist/sql/table-shape.d.ts +38 -0
  205. package/dist/sql/table-shape.d.ts.map +1 -0
  206. package/dist/sql/table-shape.js +287 -0
  207. package/dist/sync.d.ts +27 -0
  208. package/dist/sync.d.ts.map +1 -0
  209. package/dist/sync.js +86 -0
  210. package/dist/typegen-model.d.ts +78 -0
  211. package/dist/typegen-model.d.ts.map +1 -0
  212. package/dist/typegen-model.js +338 -0
  213. package/dist/typegen-views.d.ts +7 -0
  214. package/dist/typegen-views.d.ts.map +1 -0
  215. package/dist/typegen-views.js +92 -0
  216. package/dist/typegen-zod.d.ts +3 -0
  217. package/dist/typegen-zod.d.ts.map +1 -0
  218. package/dist/typegen-zod.js +149 -0
  219. package/dist/typegen.d.ts +4 -0
  220. package/dist/typegen.d.ts.map +1 -0
  221. package/dist/typegen.js +184 -0
  222. package/dist/validators.d.ts +3 -0
  223. package/dist/validators.d.ts.map +1 -0
  224. package/dist/validators.js +104 -0
  225. package/dist/verify-environment.d.ts +5 -0
  226. package/dist/verify-environment.d.ts.map +1 -0
  227. package/dist/verify-environment.js +92 -0
  228. package/dist/verify.d.ts +3 -0
  229. package/dist/verify.d.ts.map +1 -0
  230. package/dist/verify.js +261 -0
  231. package/docs/benchmarks/additive-correctness.svg +86 -0
  232. package/docs/benchmarks/additive-latency.svg +60 -0
  233. package/docs/benchmarks/functions-policies-correctness.svg +86 -0
  234. package/docs/benchmarks/functions-policies-latency.svg +60 -0
  235. package/docs/benchmarks/realistic-correctness.svg +86 -0
  236. package/docs/benchmarks/realistic-latency.svg +60 -0
  237. package/docs/benchmarks/scaling-latency.svg +106 -0
  238. package/docs/benchmarks/workflow-latency.svg +98 -0
  239. package/docs/benchmarks/xl-correctness.svg +86 -0
  240. package/docs/benchmarks/xl-latency.svg +60 -0
  241. package/docs/benchmarks/xxl-correctness.svg +86 -0
  242. package/docs/benchmarks/xxl-latency.svg +66 -0
  243. package/docs/case-study-anilize.md +51 -0
  244. package/docs/ci-gate.md +44 -0
  245. package/docs/ci.md +68 -0
  246. package/docs/commands.md +35 -0
  247. package/docs/config.md +72 -0
  248. package/docs/corpus.md +33 -0
  249. package/docs/diagnostics.md +77 -0
  250. package/docs/hints.md +92 -0
  251. package/docs/release.md +19 -0
  252. package/docs/support-matrix.md +57 -0
  253. package/examples/postgres/schemas/001_app.sql +17 -0
  254. package/examples/supabase/schemas/001_app.sql +13 -0
  255. package/examples/supabase/schemas-next/001_app.sql +21 -0
  256. package/examples/supabase/supaschema.config.json +15 -0
  257. package/package.json +99 -0
@@ -0,0 +1,323 @@
1
+ import { resolveConfig } from "../config.js";
2
+ import { diagnostic } from "../diagnostics.js";
3
+ import { asRecord, astStatements, collectReferences, functionIdentity, qualifiedName, rangeVarName, readArray, readBoolean, readString, stringList, } from "./ast.js";
4
+ import { alterTableObjects, extensionSchemaOption, sequenceOwnedByOption, supabaseViewSecurityDiagnostics, withManagedSchemaDiagnostics, } from "./extract-helpers.js";
5
+ import { finalizeObjects } from "./facts.js";
6
+ import { parseSqlAst } from "./parser.js";
7
+ import { commentObjectFromAst, defaultPrivilegesFromAst, grantObjectsFromAst, } from "./privileges.js";
8
+ import { makeObject, tableMetadataFromAst } from "./statements.js";
9
+ import { stripDeclaredConstraints, tableConstraintSyntheses } from "./table-constraints.js";
10
+ const sideEffectTags = new Set([
11
+ "CallStmt",
12
+ "DeleteStmt",
13
+ "DoStmt",
14
+ "InsertStmt",
15
+ "RefreshMatViewStmt",
16
+ "SelectStmt",
17
+ "UpdateStmt",
18
+ ]);
19
+ const ignoredTags = new Set(["TransactionStmt"]);
20
+ export async function extractObjectsFromSql(sql, options = {}) {
21
+ const config = resolveConfig(options.config);
22
+ const parsedSql = await parseSqlAst(sql, options.file);
23
+ const diagnostics = [...parsedSql.diagnostics];
24
+ const objects = [];
25
+ let ordinal = options.startOrdinal ?? 0;
26
+ if (parsedSql.ast === undefined) {
27
+ if (!diagnostics.some((item) => item.severity === "error")) {
28
+ diagnostics.push(diagnostic("SUPA_EXTRACT_PARSER_REQUIRED", "error", "AST extraction requires the libpg-query parser", { file: options.file }));
29
+ }
30
+ return { diagnostics, nextOrdinal: ordinal, objects };
31
+ }
32
+ for (const statement of astStatements(parsedSql.ast, sql)) {
33
+ if (statement.text.length === 0 || ignoredTags.has(statement.tag)) {
34
+ continue;
35
+ }
36
+ const parsed = parseStatement(statement, ordinal, config, options.file);
37
+ diagnostics.push(...parsed.diagnostics);
38
+ if (parsed.objects.length > 0) {
39
+ const references = await statementReferences(statement);
40
+ for (const object of parsed.objects) {
41
+ object.dependencies = [...references]
42
+ .filter((reference) => reference !== objectIdentity(object.ref))
43
+ .sort((left, right) => left.localeCompare(right));
44
+ }
45
+ diagnostics.push(...(await finalizeObjects(parsed.objects, { normalize: config.normalize === "deparse" })));
46
+ diagnostics.push(...supabaseViewSecurityDiagnostics(parsed.objects, config));
47
+ }
48
+ objects.push(...parsed.objects);
49
+ ordinal += parsed.objects.length;
50
+ }
51
+ return {
52
+ diagnostics,
53
+ nextOrdinal: ordinal,
54
+ objects,
55
+ };
56
+ }
57
+ function parseStatement(statement, ordinal, config, file) {
58
+ if (sideEffectTags.has(statement.tag)) {
59
+ return {
60
+ diagnostics: [
61
+ diagnostic("SUPA_EXTRACT_SIDE_EFFECT_UNSUPPORTED", "error", "side-effect statements are not schema objects and cannot be rendered as replay-safe migrations", {
62
+ file,
63
+ hint: "Keep data/control-plane side effects in an explicit reviewed migration.",
64
+ schemas: referencedSchemas(statement),
65
+ statement: statement.text,
66
+ }),
67
+ ],
68
+ objects: [],
69
+ };
70
+ }
71
+ const objects = buildObjects(statement, ordinal, file);
72
+ if (objects === undefined) {
73
+ const head = statement.text.split("\n", 1)[0]?.slice(0, 100) ?? "";
74
+ return {
75
+ diagnostics: [
76
+ diagnostic("SUPA_EXTRACT_UNSUPPORTED", "error", `unsupported or ambiguous DDL statement (${statement.tag}): ${head}`, {
77
+ file,
78
+ hint: "Add support for this object kind or keep the change hand-authored.",
79
+ schemas: referencedSchemas(statement),
80
+ statement: statement.text,
81
+ }),
82
+ ],
83
+ objects: [],
84
+ };
85
+ }
86
+ return withManagedSchemaDiagnostics(objects, statement.text, config, file);
87
+ }
88
+ function buildObjects(statement, ordinal, file) {
89
+ const node = asRecord(statement.node[statement.tag]);
90
+ if (!node) {
91
+ return undefined;
92
+ }
93
+ switch (statement.tag) {
94
+ case "CreateSchemaStmt": {
95
+ const name = readString(node.schemaname);
96
+ return name
97
+ ? [makeObject({ kind: "schema", name }, statement.text, ordinal, file)]
98
+ : undefined;
99
+ }
100
+ case "CreateExtensionStmt": {
101
+ const name = readString(node.extname);
102
+ if (!name) {
103
+ return undefined;
104
+ }
105
+ const schema = extensionSchemaOption(node.options);
106
+ return [
107
+ makeObject({ kind: "extension", name }, statement.text, ordinal, file, schema ? { schema } : {}),
108
+ ];
109
+ }
110
+ case "CreateEnumStmt": {
111
+ const name = qualifiedName(node.typeName);
112
+ if (!name) {
113
+ return undefined;
114
+ }
115
+ const values = stringList(node.vals);
116
+ return [makeObject({ kind: "enum", ...name }, statement.text, ordinal, file, { values })];
117
+ }
118
+ case "CreateDomainStmt": {
119
+ const name = qualifiedName(node.domainname);
120
+ return name
121
+ ? [makeObject({ kind: "domain", ...name }, statement.text, ordinal, file)]
122
+ : undefined;
123
+ }
124
+ case "CompositeTypeStmt": {
125
+ const name = rangeVarName(node.typevar);
126
+ return name
127
+ ? [makeObject({ kind: "type", ...name }, statement.text, ordinal, file)]
128
+ : undefined;
129
+ }
130
+ case "CreateRangeStmt": {
131
+ const name = qualifiedName(node.typeName);
132
+ return name
133
+ ? [makeObject({ kind: "type", ...name }, statement.text, ordinal, file)]
134
+ : undefined;
135
+ }
136
+ case "CreateStmt": {
137
+ const name = rangeVarName(node.relation);
138
+ if (!name) {
139
+ return undefined;
140
+ }
141
+ const tableSql = stripDeclaredConstraints(node, statement.text, statement.byteStart) ?? statement.text;
142
+ const objects = [
143
+ makeObject({ kind: "table", ...name }, tableSql, ordinal, file, tableMetadataFromAst(node, statement.text, statement.byteStart)),
144
+ ];
145
+ // In-CREATE constraints surface as their own constraint objects so table
146
+ // identity stays independent of where a constraint is declared; the
147
+ // table object's SQL is rebuilt columns-only to match the catalog lane.
148
+ for (const [index, synthesized] of tableConstraintSyntheses(node, statement.text, statement.byteStart).entries()) {
149
+ objects.push(makeObject({ kind: "constraint", name: synthesized.name, schema: name.schema, table: name.name }, synthesized.sql, ordinal + 1 + index, file));
150
+ }
151
+ return objects;
152
+ }
153
+ case "CreateSeqStmt": {
154
+ const name = rangeVarName(node.sequence);
155
+ return name
156
+ ? [makeObject({ kind: "sequence", ...name }, statement.text, ordinal, file)]
157
+ : undefined;
158
+ }
159
+ case "AlterSeqStmt": {
160
+ const name = rangeVarName(node.sequence);
161
+ if (!name) {
162
+ return undefined;
163
+ }
164
+ const ownedBy = sequenceOwnedByOption(node.options);
165
+ if (ownedBy === undefined) {
166
+ return undefined;
167
+ }
168
+ // Folded into the owning sequence's canonical shape at model assembly.
169
+ return [
170
+ makeObject({ kind: "sequence", ...name }, statement.text, ordinal, file, {
171
+ sequenceOwnedByAmendment: { ownedBy },
172
+ }),
173
+ ];
174
+ }
175
+ case "CreateFdwStmt": {
176
+ const name = readString(node.fdwname);
177
+ return name
178
+ ? [makeObject({ kind: "foreign-data-wrapper", name }, statement.text, ordinal, file)]
179
+ : undefined;
180
+ }
181
+ case "CreateForeignServerStmt": {
182
+ const name = readString(node.servername);
183
+ return name
184
+ ? [makeObject({ kind: "foreign-server", name }, statement.text, ordinal, file)]
185
+ : undefined;
186
+ }
187
+ case "CreateForeignTableStmt": {
188
+ const name = rangeVarName(asRecord(node.base)?.relation);
189
+ if (!name) {
190
+ return undefined;
191
+ }
192
+ const server = readString(node.servername);
193
+ return [
194
+ makeObject({ kind: "foreign-table", ...name }, statement.text, ordinal, file, server ? { server } : {}),
195
+ ];
196
+ }
197
+ case "IndexStmt": {
198
+ const tableName = rangeVarName(node.relation);
199
+ const indexName = readString(node.idxname);
200
+ if (!tableName || !indexName) {
201
+ return undefined;
202
+ }
203
+ return [
204
+ makeObject({
205
+ kind: "index",
206
+ name: indexName,
207
+ schema: tableName.schema,
208
+ table: tableName.name,
209
+ }, statement.text, ordinal, file, { concurrent: readBoolean(node.concurrent) }),
210
+ ];
211
+ }
212
+ case "CreateFunctionStmt": {
213
+ const identity = functionIdentity(node.funcname, node.parameters);
214
+ if (!identity) {
215
+ return undefined;
216
+ }
217
+ const kind = readBoolean(node.is_procedure) ? "procedure" : "function";
218
+ return [makeObject({ kind, ...identity }, statement.text, ordinal, file)];
219
+ }
220
+ case "ViewStmt": {
221
+ const name = rangeVarName(node.view);
222
+ return name
223
+ ? [makeObject({ kind: "view", ...name }, statement.text, ordinal, file)]
224
+ : undefined;
225
+ }
226
+ case "CreateTableAsStmt": {
227
+ if (readString(node.objtype) !== "OBJECT_MATVIEW") {
228
+ return undefined;
229
+ }
230
+ const into = asRecord(node.into);
231
+ const name = rangeVarName(into?.rel);
232
+ return name
233
+ ? [
234
+ makeObject({ kind: "materialized-view", ...name }, statement.text, ordinal, file, {
235
+ withNoData: readBoolean(into?.skipData),
236
+ }),
237
+ ]
238
+ : undefined;
239
+ }
240
+ case "CreateTrigStmt": {
241
+ const tableName = rangeVarName(node.relation);
242
+ const triggerName = readString(node.trigname);
243
+ if (!tableName || !triggerName) {
244
+ return undefined;
245
+ }
246
+ return [
247
+ makeObject({
248
+ kind: "trigger",
249
+ name: triggerName,
250
+ schema: tableName.schema,
251
+ table: tableName.name,
252
+ }, statement.text, ordinal, file),
253
+ ];
254
+ }
255
+ case "CreatePolicyStmt": {
256
+ const tableName = rangeVarName(node.table);
257
+ const policyName = readString(node.policy_name);
258
+ if (!tableName || !policyName) {
259
+ return undefined;
260
+ }
261
+ return [
262
+ makeObject({
263
+ kind: "policy",
264
+ name: policyName,
265
+ schema: tableName.schema,
266
+ table: tableName.name,
267
+ }, statement.text, ordinal, file),
268
+ ];
269
+ }
270
+ case "AlterTableStmt":
271
+ return alterTableObjects(node, statement.text, ordinal, file);
272
+ case "GrantStmt":
273
+ return grantObjectsFromAst(node, statement.text, ordinal, file);
274
+ case "AlterDefaultPrivilegesStmt":
275
+ return defaultPrivilegesFromAst(node, statement.text, ordinal, file);
276
+ case "CommentStmt": {
277
+ const object = commentObjectFromAst(node, statement.text, ordinal, file);
278
+ return object ? [object] : undefined;
279
+ }
280
+ default:
281
+ return undefined;
282
+ }
283
+ }
284
+ function referencedSchemas(statement) {
285
+ const schemas = new Set();
286
+ for (const reference of collectReferences(statement.node)) {
287
+ const separator = reference.indexOf(".");
288
+ if (separator > 0) {
289
+ schemas.add(reference.slice(0, separator));
290
+ }
291
+ }
292
+ return [...schemas].sort((left, right) => left.localeCompare(right));
293
+ }
294
+ async function statementReferences(statement) {
295
+ const references = collectReferences(statement.node);
296
+ if (statement.tag === "CreateFunctionStmt") {
297
+ const node = asRecord(statement.node.CreateFunctionStmt);
298
+ for (const body of functionBodyStrings(node?.options)) {
299
+ const parsedBody = await parseSqlAst(body);
300
+ if (parsedBody.ast !== undefined) {
301
+ collectReferences(parsedBody.ast, references);
302
+ }
303
+ }
304
+ }
305
+ return references;
306
+ }
307
+ function functionBodyStrings(options) {
308
+ const bodies = [];
309
+ for (const item of readArray(options)) {
310
+ const defElem = asRecord(asRecord(item)?.DefElem);
311
+ if (readString(defElem?.defname) !== "as") {
312
+ continue;
313
+ }
314
+ bodies.push(...stringList(defElem?.arg));
315
+ }
316
+ return bodies;
317
+ }
318
+ function objectIdentity(ref) {
319
+ if (ref.kind === "schema") {
320
+ return ref.name;
321
+ }
322
+ return `${ref.schema ?? "public"}.${ref.name}`;
323
+ }
@@ -0,0 +1,34 @@
1
+ import type { Diagnostic, SchemaObject } from "../core.js";
2
+ import type { AstNode } from "./ast.js";
3
+ export interface RenderGuardFacts {
4
+ guard: "ifNotExists" | "orReplace";
5
+ offset?: number;
6
+ present: boolean;
7
+ }
8
+ export interface RoutineReturnFacts {
9
+ setof: boolean;
10
+ type: string;
11
+ }
12
+ export interface RoutineOutParamFacts {
13
+ mode: string;
14
+ name: string;
15
+ type: string;
16
+ }
17
+ type KeywordStep = {
18
+ optional?: boolean;
19
+ words: string[];
20
+ };
21
+ export interface FinalizeOptions {
22
+ normalize?: boolean;
23
+ }
24
+ export declare function finalizeObjects(objects: SchemaObject[], options?: FinalizeOptions): Promise<Diagnostic[]>;
25
+ export declare function finalizeObject(object: SchemaObject, options?: FinalizeOptions): Promise<Diagnostic[]>;
26
+ export declare function statementFacts(tag: string, statementNode: AstNode, sql: string): Record<string, unknown>;
27
+ /**
28
+ * Walks the leading keyword sequence of a statement (skipping whitespace and
29
+ * comments) and returns the offset of the token that follows it — the splice
30
+ * point for a replay guard. Character scanning only; classification stays AST.
31
+ */
32
+ export declare function keywordOffset(sql: string, steps: KeywordStep[]): number | undefined;
33
+ export {};
34
+ //# sourceMappingURL=facts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../src/sql/facts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAsBxC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,KAAK,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AA+B3D,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,UAAU,EAAE,CAAC,CAUvB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,UAAU,EAAE,CAAC,CAiCvB;AAsHD,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,OAAO,EACtB,GAAG,EAAE,MAAM,GACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAuJD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,SAAS,CAcnF"}