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,182 @@
1
+ export function splitSqlStatements(sql) {
2
+ const statements = [];
3
+ let start = 0;
4
+ let dollarTag = "";
5
+ let inSingleQuote = false;
6
+ let inDoubleQuote = false;
7
+ let inLineComment = false;
8
+ let inBlockComment = false;
9
+ for (let index = 0; index < sql.length; index += 1) {
10
+ const char = sql[index] ?? "";
11
+ const next = sql[index + 1] ?? "";
12
+ if (inLineComment) {
13
+ if (char === "\n") {
14
+ inLineComment = false;
15
+ }
16
+ continue;
17
+ }
18
+ if (inBlockComment) {
19
+ if (char === "*" && next === "/") {
20
+ inBlockComment = false;
21
+ index += 1;
22
+ }
23
+ continue;
24
+ }
25
+ if (dollarTag) {
26
+ if (sql.startsWith(dollarTag, index)) {
27
+ index += dollarTag.length - 1;
28
+ dollarTag = "";
29
+ }
30
+ continue;
31
+ }
32
+ if (inSingleQuote) {
33
+ if (char === "'" && next === "'") {
34
+ index += 1;
35
+ continue;
36
+ }
37
+ if (char === "'") {
38
+ inSingleQuote = false;
39
+ }
40
+ continue;
41
+ }
42
+ if (inDoubleQuote) {
43
+ if (char === '"' && next === '"') {
44
+ index += 1;
45
+ continue;
46
+ }
47
+ if (char === '"') {
48
+ inDoubleQuote = false;
49
+ }
50
+ continue;
51
+ }
52
+ if (char === "-" && next === "-") {
53
+ inLineComment = true;
54
+ index += 1;
55
+ continue;
56
+ }
57
+ if (char === "/" && next === "*") {
58
+ inBlockComment = true;
59
+ index += 1;
60
+ continue;
61
+ }
62
+ if (char === "'") {
63
+ inSingleQuote = true;
64
+ continue;
65
+ }
66
+ if (char === '"') {
67
+ inDoubleQuote = true;
68
+ continue;
69
+ }
70
+ const tag = readDollarTag(sql, index);
71
+ if (tag) {
72
+ dollarTag = tag;
73
+ index += tag.length - 1;
74
+ continue;
75
+ }
76
+ if (char === ";") {
77
+ const statement = sql.slice(start, index).trim();
78
+ if (statement.length > 0) {
79
+ statements.push(statement);
80
+ }
81
+ start = index + 1;
82
+ }
83
+ }
84
+ const trailing = sql.slice(start).trim();
85
+ if (trailing.length > 0) {
86
+ statements.push(trailing);
87
+ }
88
+ return statements;
89
+ }
90
+ function isTagStartChar(char) {
91
+ return (char >= "a" && char <= "z") || (char >= "A" && char <= "Z") || char === "_";
92
+ }
93
+ function isTagChar(char) {
94
+ return isTagStartChar(char) || (char >= "0" && char <= "9");
95
+ }
96
+ function readDollarTag(sql, index) {
97
+ if (sql[index] !== "$") {
98
+ return undefined;
99
+ }
100
+ if (sql[index + 1] === "$") {
101
+ return "$$";
102
+ }
103
+ let cursor = index + 1;
104
+ if (!isTagStartChar(sql[cursor] ?? "")) {
105
+ return undefined;
106
+ }
107
+ cursor += 1;
108
+ while (isTagChar(sql[cursor] ?? "")) {
109
+ cursor += 1;
110
+ }
111
+ return sql[cursor] === "$" ? sql.slice(index, cursor + 1) : undefined;
112
+ }
113
+ export function splitTopLevel(input, separator = ",") {
114
+ const parts = [];
115
+ let start = 0;
116
+ let depth = 0;
117
+ let dollarTag = "";
118
+ let inSingleQuote = false;
119
+ let inDoubleQuote = false;
120
+ for (let index = 0; index < input.length; index += 1) {
121
+ const char = input[index] ?? "";
122
+ const next = input[index + 1] ?? "";
123
+ if (dollarTag) {
124
+ if (input.startsWith(dollarTag, index)) {
125
+ index += dollarTag.length - 1;
126
+ dollarTag = "";
127
+ }
128
+ continue;
129
+ }
130
+ if (inSingleQuote) {
131
+ if (char === "'" && next === "'") {
132
+ index += 1;
133
+ continue;
134
+ }
135
+ if (char === "'") {
136
+ inSingleQuote = false;
137
+ }
138
+ continue;
139
+ }
140
+ if (inDoubleQuote) {
141
+ if (char === '"' && next === '"') {
142
+ index += 1;
143
+ continue;
144
+ }
145
+ if (char === '"') {
146
+ inDoubleQuote = false;
147
+ }
148
+ continue;
149
+ }
150
+ if (char === "'") {
151
+ inSingleQuote = true;
152
+ continue;
153
+ }
154
+ if (char === '"') {
155
+ inDoubleQuote = true;
156
+ continue;
157
+ }
158
+ const tag = readDollarTag(input, index);
159
+ if (tag) {
160
+ dollarTag = tag;
161
+ index += tag.length - 1;
162
+ continue;
163
+ }
164
+ if (char === "(") {
165
+ depth += 1;
166
+ continue;
167
+ }
168
+ if (char === ")") {
169
+ depth = Math.max(0, depth - 1);
170
+ continue;
171
+ }
172
+ if (char === separator && depth === 0) {
173
+ parts.push(input.slice(start, index).trim());
174
+ start = index + 1;
175
+ }
176
+ }
177
+ const trailing = input.slice(start).trim();
178
+ if (trailing.length > 0) {
179
+ parts.push(trailing);
180
+ }
181
+ return parts;
182
+ }
@@ -0,0 +1,17 @@
1
+ import type { ObjectRef, SchemaObject } from "../core.js";
2
+ import type { AstNode } from "./ast.js";
3
+ export declare function makeObject(ref: ObjectRef, statement: string, ordinal: number, file?: string, metadata?: Record<string, unknown>): SchemaObject;
4
+ export interface TableElement {
5
+ end: number;
6
+ isColumn: boolean;
7
+ node: AstNode;
8
+ start: number;
9
+ }
10
+ export declare function toByteString(sql: string): string;
11
+ export declare function fromByteString(bytes: string): string;
12
+ export declare function tableMetadataFromAst(createStmt: AstNode, sql: string, byteOffset?: number): Record<string, unknown>;
13
+ export declare function tableElements(createStmt: AstNode, sql: string, byteOffset?: number): TableElement[];
14
+ export declare function elementText(sql: string, element: TableElement): string;
15
+ export declare function findCharOutsideQuotes(input: string, target: string, from: number): number;
16
+ export declare function findMatchingParen(input: string, openIndex: number): number;
17
+ //# sourceMappingURL=statements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statements.d.ts","sourceRoot":"","sources":["../../src/sql/statements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAe,MAAM,YAAY,CAAC;AAEvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAIxC,wBAAgB,UAAU,CACxB,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,YAAY,CAiBd;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,OAAO,EACnB,GAAG,EAAE,MAAM,EACX,UAAU,SAAI,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmCzB;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,YAAY,EAAE,CAmC9F;AAsED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAMtE;AAmCD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAuCzF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA8C1E"}
@@ -0,0 +1,284 @@
1
+ import { sha256, stableJson } from "../hash.js";
2
+ import { asRecord, columnFacts, readArray, readNumber, readString } from "./ast.js";
3
+ import { normalizeSql, objectKey } from "./identifiers.js";
4
+ export function makeObject(ref, statement, ordinal, file, metadata = {}) {
5
+ const normalizedSql = normalizeSql(statement);
6
+ const key = objectKey(ref);
7
+ const object = {
8
+ dependencies: [],
9
+ hash: sha256(stableJson({ key, normalizedSql, ref })),
10
+ key,
11
+ metadata,
12
+ normalizedSql,
13
+ ordinal,
14
+ ref,
15
+ sql: normalizedSql,
16
+ };
17
+ if (file) {
18
+ object.file = file;
19
+ }
20
+ return object;
21
+ }
22
+ export function toByteString(sql) {
23
+ return Buffer.from(sql, "utf8").toString("latin1");
24
+ }
25
+ export function fromByteString(bytes) {
26
+ return Buffer.from(bytes, "latin1").toString("utf8");
27
+ }
28
+ export function tableMetadataFromAst(createStmt, sql, byteOffset = 0) {
29
+ const bytes = toByteString(sql);
30
+ const elements = tableElements(createStmt, bytes, byteOffset);
31
+ const columns = [];
32
+ const constraintFragments = [];
33
+ for (const element of elements) {
34
+ const text = fromByteString(elementText(bytes, element));
35
+ if (!element.isColumn) {
36
+ constraintFragments.push(normalizeSql(text));
37
+ continue;
38
+ }
39
+ const facts = columnFacts({ ColumnDef: element.node });
40
+ if (!facts) {
41
+ continue;
42
+ }
43
+ const column = {
44
+ definition: normalizeSql(stripLeadingIdentifier(text)),
45
+ generated: facts.generated,
46
+ hasDefault: facts.hasDefault,
47
+ hasInlineConstraint: facts.hasInlineConstraint,
48
+ identity: facts.identity,
49
+ name: facts.name,
50
+ notNull: facts.notNull,
51
+ type: facts.type,
52
+ };
53
+ const defaultExpression = columnDefaultExpression(element, bytes, byteOffset);
54
+ if (defaultExpression !== undefined) {
55
+ column.defaultExpression = fromByteString(defaultExpression);
56
+ }
57
+ columns.push(column);
58
+ }
59
+ return {
60
+ columns,
61
+ constraintFragments: constraintFragments.sort((left, right) => left.localeCompare(right)),
62
+ };
63
+ }
64
+ export function tableElements(createStmt, sql, byteOffset = 0) {
65
+ const relationLocation = (readNumber(asRecord(createStmt.relation)?.location) ?? 0) - byteOffset;
66
+ const open = findCharOutsideQuotes(sql, "(", Math.max(relationLocation, 0));
67
+ if (open === -1) {
68
+ return [];
69
+ }
70
+ const close = findMatchingParen(sql, open);
71
+ if (close === -1) {
72
+ return [];
73
+ }
74
+ const located = [];
75
+ for (const item of readArray(createStmt.tableElts)) {
76
+ const columnDef = asRecord(asRecord(item)?.ColumnDef);
77
+ if (columnDef) {
78
+ const location = readNumber(columnDef.location);
79
+ if (location !== undefined) {
80
+ located.push({ isColumn: true, location: location - byteOffset, node: columnDef });
81
+ }
82
+ continue;
83
+ }
84
+ const constraint = asRecord(asRecord(item)?.Constraint);
85
+ if (constraint) {
86
+ const location = readNumber(constraint.location);
87
+ if (location !== undefined) {
88
+ located.push({ isColumn: false, location: location - byteOffset, node: constraint });
89
+ }
90
+ }
91
+ }
92
+ located.sort((left, right) => left.location - right.location);
93
+ return located.map((element, index) => ({
94
+ end: located[index + 1]?.location ?? close,
95
+ isColumn: element.isColumn,
96
+ node: element.node,
97
+ start: element.location,
98
+ }));
99
+ }
100
+ function columnDefaultExpression(element, sql, byteOffset = 0) {
101
+ const constraints = readArray(element.node.constraints)
102
+ .map((item) => asRecord(asRecord(item)?.Constraint))
103
+ .filter((item) => item !== undefined);
104
+ const located = constraints
105
+ .map((constraint) => ({
106
+ constraint,
107
+ location: (readNumber(constraint.location) ?? -1) - byteOffset,
108
+ }))
109
+ .filter((item) => item.location >= 0)
110
+ .sort((left, right) => left.location - right.location);
111
+ for (const [index, item] of located.entries()) {
112
+ if (readString(item.constraint.contype) !== "CONSTR_DEFAULT") {
113
+ continue;
114
+ }
115
+ const expressionStart = expressionLocation(item.constraint.raw_expr);
116
+ if (expressionStart === undefined) {
117
+ return undefined;
118
+ }
119
+ const end = located[index + 1]?.location ?? element.end;
120
+ let text = sql.slice(expressionStart - byteOffset, end).trim();
121
+ if (text.endsWith(",")) {
122
+ text = text.slice(0, -1).trimEnd();
123
+ }
124
+ return text.length > 0 ? text : undefined;
125
+ }
126
+ return undefined;
127
+ }
128
+ function expressionLocation(expression) {
129
+ const record = asRecord(expression);
130
+ if (!record) {
131
+ return undefined;
132
+ }
133
+ let earliest;
134
+ const visit = (value) => {
135
+ if (Array.isArray(value)) {
136
+ for (const item of value) {
137
+ visit(item);
138
+ }
139
+ return;
140
+ }
141
+ const node = asRecord(value);
142
+ if (!node) {
143
+ return;
144
+ }
145
+ const location = readNumber(node.location);
146
+ if (location !== undefined &&
147
+ location >= 0 &&
148
+ (earliest === undefined || location < earliest)) {
149
+ earliest = location;
150
+ }
151
+ for (const child of Object.values(node)) {
152
+ if (child && typeof child === "object") {
153
+ visit(child);
154
+ }
155
+ }
156
+ };
157
+ visit(record);
158
+ return earliest;
159
+ }
160
+ export function elementText(sql, element) {
161
+ let text = sql.slice(element.start, element.end).trim();
162
+ if (text.endsWith(",")) {
163
+ text = text.slice(0, -1).trimEnd();
164
+ }
165
+ return text;
166
+ }
167
+ function stripLeadingIdentifier(text) {
168
+ if (text.startsWith('"')) {
169
+ let index = 1;
170
+ while (index < text.length) {
171
+ if (text[index] === '"' && text[index + 1] === '"') {
172
+ index += 2;
173
+ continue;
174
+ }
175
+ if (text[index] === '"') {
176
+ index += 1;
177
+ break;
178
+ }
179
+ index += 1;
180
+ }
181
+ return text.slice(index).trim();
182
+ }
183
+ let length = 0;
184
+ while (length < text.length) {
185
+ const char = text[length] ?? "";
186
+ const isWordChar = (char >= "a" && char <= "z") ||
187
+ (char >= "A" && char <= "Z") ||
188
+ (char >= "0" && char <= "9" && length > 0) ||
189
+ char === "_" ||
190
+ (char === "$" && length > 0);
191
+ if (!isWordChar) {
192
+ break;
193
+ }
194
+ length += 1;
195
+ }
196
+ return length > 0 ? text.slice(length).trim() : text;
197
+ }
198
+ export function findCharOutsideQuotes(input, target, from) {
199
+ let inSingleQuote = false;
200
+ let inDoubleQuote = false;
201
+ for (let index = from; index < input.length; index += 1) {
202
+ const char = input[index] ?? "";
203
+ const next = input[index + 1] ?? "";
204
+ if (inSingleQuote) {
205
+ if (char === "'" && next === "'") {
206
+ index += 1;
207
+ continue;
208
+ }
209
+ if (char === "'") {
210
+ inSingleQuote = false;
211
+ }
212
+ continue;
213
+ }
214
+ if (inDoubleQuote) {
215
+ if (char === '"' && next === '"') {
216
+ index += 1;
217
+ continue;
218
+ }
219
+ if (char === '"') {
220
+ inDoubleQuote = false;
221
+ }
222
+ continue;
223
+ }
224
+ if (char === "'") {
225
+ inSingleQuote = true;
226
+ continue;
227
+ }
228
+ if (char === '"') {
229
+ inDoubleQuote = true;
230
+ continue;
231
+ }
232
+ if (char === target) {
233
+ return index;
234
+ }
235
+ }
236
+ return -1;
237
+ }
238
+ export function findMatchingParen(input, openIndex) {
239
+ let depth = 0;
240
+ let inSingleQuote = false;
241
+ let inDoubleQuote = false;
242
+ for (let index = openIndex; index < input.length; index += 1) {
243
+ const char = input[index] ?? "";
244
+ const next = input[index + 1] ?? "";
245
+ if (inSingleQuote) {
246
+ if (char === "'" && next === "'") {
247
+ index += 1;
248
+ continue;
249
+ }
250
+ if (char === "'") {
251
+ inSingleQuote = false;
252
+ }
253
+ continue;
254
+ }
255
+ if (inDoubleQuote) {
256
+ if (char === '"' && next === '"') {
257
+ index += 1;
258
+ continue;
259
+ }
260
+ if (char === '"') {
261
+ inDoubleQuote = false;
262
+ }
263
+ continue;
264
+ }
265
+ if (char === "'") {
266
+ inSingleQuote = true;
267
+ continue;
268
+ }
269
+ if (char === '"') {
270
+ inDoubleQuote = true;
271
+ continue;
272
+ }
273
+ if (char === "(") {
274
+ depth += 1;
275
+ }
276
+ if (char === ")") {
277
+ depth -= 1;
278
+ if (depth === 0) {
279
+ return index;
280
+ }
281
+ }
282
+ }
283
+ return -1;
284
+ }
@@ -0,0 +1,15 @@
1
+ import type { AstNode } from "./ast.js";
2
+ export interface SynthesizedConstraint {
3
+ name: string;
4
+ sql: string;
5
+ }
6
+ export declare function tableConstraintSyntheses(createStmt: AstNode, sql: string, byteOffset?: number): SynthesizedConstraint[];
7
+ /**
8
+ * Rebuilds the CREATE TABLE statement without its declared constraints so the
9
+ * table object's SQL matches the columns-only shape the catalog lane emits.
10
+ * Raw-apply consumers (verify, parity tests) then apply the table once and
11
+ * each constraint once instead of creating hoisted constraints twice. Returns
12
+ * undefined when the statement declares no hoistable constraints.
13
+ */
14
+ export declare function stripDeclaredConstraints(createStmt: AstNode, sql: string, byteOffset?: number): string | undefined;
15
+ //# sourceMappingURL=table-constraints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-constraints.d.ts","sourceRoot":"","sources":["../../src/sql/table-constraints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAaxC,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,OAAO,EACnB,GAAG,EAAE,MAAM,EACX,UAAU,SAAI,GACb,qBAAqB,EAAE,CA0BzB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,OAAO,EACnB,GAAG,EAAE,MAAM,EACX,UAAU,SAAI,GACb,MAAM,GAAG,SAAS,CAgDpB"}