relq 1.0.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 (305) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +862 -0
  3. package/dist/addons/buffer.js +1869 -0
  4. package/dist/addons/pg-cursor.js +1425 -0
  5. package/dist/addons/pg-format.js +2248 -0
  6. package/dist/addons/pg.js +4790 -0
  7. package/dist/bin/relq.js +2 -0
  8. package/dist/cjs/cache/index.cjs +9 -0
  9. package/dist/cjs/cache/query-cache.cjs +311 -0
  10. package/dist/cjs/cli/commands/add.cjs +82 -0
  11. package/dist/cjs/cli/commands/commit.cjs +145 -0
  12. package/dist/cjs/cli/commands/diff.cjs +84 -0
  13. package/dist/cjs/cli/commands/export.cjs +333 -0
  14. package/dist/cjs/cli/commands/fetch.cjs +59 -0
  15. package/dist/cjs/cli/commands/generate.cjs +242 -0
  16. package/dist/cjs/cli/commands/history.cjs +165 -0
  17. package/dist/cjs/cli/commands/import.cjs +524 -0
  18. package/dist/cjs/cli/commands/init.cjs +437 -0
  19. package/dist/cjs/cli/commands/introspect.cjs +142 -0
  20. package/dist/cjs/cli/commands/log.cjs +62 -0
  21. package/dist/cjs/cli/commands/migrate.cjs +167 -0
  22. package/dist/cjs/cli/commands/pull.cjs +410 -0
  23. package/dist/cjs/cli/commands/push.cjs +165 -0
  24. package/dist/cjs/cli/commands/rollback.cjs +169 -0
  25. package/dist/cjs/cli/commands/status.cjs +110 -0
  26. package/dist/cjs/cli/commands/sync.cjs +79 -0
  27. package/dist/cjs/cli/index.cjs +275 -0
  28. package/dist/cjs/cli/utils/change-tracker.cjs +446 -0
  29. package/dist/cjs/cli/utils/commit-manager.cjs +239 -0
  30. package/dist/cjs/cli/utils/config-loader.cjs +127 -0
  31. package/dist/cjs/cli/utils/env-loader.cjs +62 -0
  32. package/dist/cjs/cli/utils/fast-introspect.cjs +398 -0
  33. package/dist/cjs/cli/utils/git-utils.cjs +404 -0
  34. package/dist/cjs/cli/utils/migration-generator.cjs +269 -0
  35. package/dist/cjs/cli/utils/relqignore.cjs +114 -0
  36. package/dist/cjs/cli/utils/repo-manager.cjs +515 -0
  37. package/dist/cjs/cli/utils/schema-comparator.cjs +313 -0
  38. package/dist/cjs/cli/utils/schema-diff.cjs +284 -0
  39. package/dist/cjs/cli/utils/schema-hash.cjs +108 -0
  40. package/dist/cjs/cli/utils/schema-introspect.cjs +455 -0
  41. package/dist/cjs/cli/utils/snapshot-manager.cjs +223 -0
  42. package/dist/cjs/cli/utils/spinner.cjs +108 -0
  43. package/dist/cjs/cli/utils/sql-generator.cjs +520 -0
  44. package/dist/cjs/cli/utils/sql-parser.cjs +999 -0
  45. package/dist/cjs/cli/utils/type-generator.cjs +2061 -0
  46. package/dist/cjs/condition/array-condition-builder.cjs +503 -0
  47. package/dist/cjs/condition/array-numeric-condition-builder.cjs +186 -0
  48. package/dist/cjs/condition/array-specialized-condition-builder.cjs +206 -0
  49. package/dist/cjs/condition/array-string-condition-builder.cjs +146 -0
  50. package/dist/cjs/condition/base-condition-builder.cjs +2 -0
  51. package/dist/cjs/condition/condition-collector.cjs +284 -0
  52. package/dist/cjs/condition/fulltext-condition-builder.cjs +61 -0
  53. package/dist/cjs/condition/geometric-condition-builder.cjs +208 -0
  54. package/dist/cjs/condition/index.cjs +25 -0
  55. package/dist/cjs/condition/jsonb-condition-builder.cjs +160 -0
  56. package/dist/cjs/condition/network-condition-builder.cjs +230 -0
  57. package/dist/cjs/condition/range-condition-builder.cjs +82 -0
  58. package/dist/cjs/config/config.cjs +190 -0
  59. package/dist/cjs/config/index.cjs +9 -0
  60. package/dist/cjs/constants/pg-values.cjs +68 -0
  61. package/dist/cjs/copy/copy-builder.cjs +316 -0
  62. package/dist/cjs/copy/index.cjs +6 -0
  63. package/dist/cjs/core/query-builder.cjs +440 -0
  64. package/dist/cjs/core/relq-client.cjs +1831 -0
  65. package/dist/cjs/core/typed-kuery-client.cjs +2 -0
  66. package/dist/cjs/count/count-builder.cjs +88 -0
  67. package/dist/cjs/count/index.cjs +5 -0
  68. package/dist/cjs/cte/cte-builder.cjs +89 -0
  69. package/dist/cjs/cte/index.cjs +5 -0
  70. package/dist/cjs/ddl/function.cjs +48 -0
  71. package/dist/cjs/ddl/index.cjs +7 -0
  72. package/dist/cjs/ddl/sql.cjs +54 -0
  73. package/dist/cjs/delete/delete-builder.cjs +135 -0
  74. package/dist/cjs/delete/index.cjs +5 -0
  75. package/dist/cjs/errors/relq-errors.cjs +329 -0
  76. package/dist/cjs/examples/fulltext-search-test.cjs +122 -0
  77. package/dist/cjs/explain/explain-builder.cjs +99 -0
  78. package/dist/cjs/explain/index.cjs +5 -0
  79. package/dist/cjs/function/create-function-builder.cjs +196 -0
  80. package/dist/cjs/function/index.cjs +6 -0
  81. package/dist/cjs/functions/advanced-functions.cjs +241 -0
  82. package/dist/cjs/functions/case-builder.cjs +66 -0
  83. package/dist/cjs/functions/geometric-functions.cjs +104 -0
  84. package/dist/cjs/functions/index.cjs +184 -0
  85. package/dist/cjs/functions/network-functions.cjs +86 -0
  86. package/dist/cjs/functions/sql-functions.cjs +431 -0
  87. package/dist/cjs/index.cjs +164 -0
  88. package/dist/cjs/indexing/create-index-builder.cjs +187 -0
  89. package/dist/cjs/indexing/drop-index-builder.cjs +89 -0
  90. package/dist/cjs/indexing/index-types.cjs +2 -0
  91. package/dist/cjs/indexing/index.cjs +8 -0
  92. package/dist/cjs/insert/conflict-builder.cjs +173 -0
  93. package/dist/cjs/insert/index.cjs +5 -0
  94. package/dist/cjs/insert/insert-builder.cjs +254 -0
  95. package/dist/cjs/introspect/index.cjs +229 -0
  96. package/dist/cjs/maintenance/index.cjs +6 -0
  97. package/dist/cjs/maintenance/vacuum-builder.cjs +166 -0
  98. package/dist/cjs/pubsub/index.cjs +7 -0
  99. package/dist/cjs/pubsub/listen-notify-builder.cjs +57 -0
  100. package/dist/cjs/pubsub/listener-connection.cjs +180 -0
  101. package/dist/cjs/raw/index.cjs +5 -0
  102. package/dist/cjs/raw/raw-query-builder.cjs +27 -0
  103. package/dist/cjs/schema/index.cjs +15 -0
  104. package/dist/cjs/schema/schema-builder.cjs +1167 -0
  105. package/dist/cjs/schema-builder.cjs +21 -0
  106. package/dist/cjs/schema-definition/column-types.cjs +829 -0
  107. package/dist/cjs/schema-definition/index.cjs +62 -0
  108. package/dist/cjs/schema-definition/introspection.cjs +620 -0
  109. package/dist/cjs/schema-definition/partitions.cjs +129 -0
  110. package/dist/cjs/schema-definition/pg-enum.cjs +76 -0
  111. package/dist/cjs/schema-definition/pg-function.cjs +91 -0
  112. package/dist/cjs/schema-definition/pg-sequence.cjs +56 -0
  113. package/dist/cjs/schema-definition/pg-trigger.cjs +108 -0
  114. package/dist/cjs/schema-definition/relations.cjs +98 -0
  115. package/dist/cjs/schema-definition/sql-expressions.cjs +202 -0
  116. package/dist/cjs/schema-definition/table-definition.cjs +636 -0
  117. package/dist/cjs/select/aggregate-builder.cjs +179 -0
  118. package/dist/cjs/select/index.cjs +5 -0
  119. package/dist/cjs/select/select-builder.cjs +233 -0
  120. package/dist/cjs/sequence/index.cjs +7 -0
  121. package/dist/cjs/sequence/sequence-builder.cjs +264 -0
  122. package/dist/cjs/table/alter-table-builder.cjs +146 -0
  123. package/dist/cjs/table/constraint-builder.cjs +102 -0
  124. package/dist/cjs/table/create-table-builder.cjs +248 -0
  125. package/dist/cjs/table/index.cjs +17 -0
  126. package/dist/cjs/table/partition-builder.cjs +131 -0
  127. package/dist/cjs/table/truncate-builder.cjs +70 -0
  128. package/dist/cjs/transaction/index.cjs +6 -0
  129. package/dist/cjs/transaction/transaction-builder.cjs +78 -0
  130. package/dist/cjs/trigger/create-trigger-builder.cjs +174 -0
  131. package/dist/cjs/trigger/index.cjs +6 -0
  132. package/dist/cjs/types/aggregate-types.cjs +2 -0
  133. package/dist/cjs/types/config-types.cjs +40 -0
  134. package/dist/cjs/types/inference-types.cjs +18 -0
  135. package/dist/cjs/types/pagination-types.cjs +7 -0
  136. package/dist/cjs/types/result-types.cjs +2 -0
  137. package/dist/cjs/types/schema-types.cjs +2 -0
  138. package/dist/cjs/types/subscription-types.cjs +2 -0
  139. package/dist/cjs/types.cjs +2 -0
  140. package/dist/cjs/update/array-update-builder.cjs +205 -0
  141. package/dist/cjs/update/index.cjs +13 -0
  142. package/dist/cjs/update/update-builder.cjs +195 -0
  143. package/dist/cjs/utils/case-converter.cjs +58 -0
  144. package/dist/cjs/utils/environment-detection.cjs +120 -0
  145. package/dist/cjs/utils/index.cjs +10 -0
  146. package/dist/cjs/utils/pool-defaults.cjs +106 -0
  147. package/dist/cjs/utils/type-coercion.cjs +118 -0
  148. package/dist/cjs/view/create-view-builder.cjs +180 -0
  149. package/dist/cjs/view/index.cjs +7 -0
  150. package/dist/cjs/window/index.cjs +5 -0
  151. package/dist/cjs/window/window-builder.cjs +80 -0
  152. package/dist/config.cjs +1 -0
  153. package/dist/config.d.ts +655 -0
  154. package/dist/config.js +1 -0
  155. package/dist/esm/cache/index.js +1 -0
  156. package/dist/esm/cache/query-cache.js +303 -0
  157. package/dist/esm/cli/commands/add.js +78 -0
  158. package/dist/esm/cli/commands/commit.js +109 -0
  159. package/dist/esm/cli/commands/diff.js +81 -0
  160. package/dist/esm/cli/commands/export.js +297 -0
  161. package/dist/esm/cli/commands/fetch.js +56 -0
  162. package/dist/esm/cli/commands/generate.js +206 -0
  163. package/dist/esm/cli/commands/history.js +129 -0
  164. package/dist/esm/cli/commands/import.js +488 -0
  165. package/dist/esm/cli/commands/init.js +401 -0
  166. package/dist/esm/cli/commands/introspect.js +106 -0
  167. package/dist/esm/cli/commands/log.js +59 -0
  168. package/dist/esm/cli/commands/migrate.js +131 -0
  169. package/dist/esm/cli/commands/pull.js +374 -0
  170. package/dist/esm/cli/commands/push.js +129 -0
  171. package/dist/esm/cli/commands/rollback.js +133 -0
  172. package/dist/esm/cli/commands/status.js +107 -0
  173. package/dist/esm/cli/commands/sync.js +76 -0
  174. package/dist/esm/cli/index.js +240 -0
  175. package/dist/esm/cli/utils/change-tracker.js +405 -0
  176. package/dist/esm/cli/utils/commit-manager.js +191 -0
  177. package/dist/esm/cli/utils/config-loader.js +86 -0
  178. package/dist/esm/cli/utils/env-loader.js +57 -0
  179. package/dist/esm/cli/utils/fast-introspect.js +362 -0
  180. package/dist/esm/cli/utils/git-utils.js +347 -0
  181. package/dist/esm/cli/utils/migration-generator.js +263 -0
  182. package/dist/esm/cli/utils/relqignore.js +74 -0
  183. package/dist/esm/cli/utils/repo-manager.js +444 -0
  184. package/dist/esm/cli/utils/schema-comparator.js +307 -0
  185. package/dist/esm/cli/utils/schema-diff.js +276 -0
  186. package/dist/esm/cli/utils/schema-hash.js +69 -0
  187. package/dist/esm/cli/utils/schema-introspect.js +418 -0
  188. package/dist/esm/cli/utils/snapshot-manager.js +179 -0
  189. package/dist/esm/cli/utils/spinner.js +101 -0
  190. package/dist/esm/cli/utils/sql-generator.js +504 -0
  191. package/dist/esm/cli/utils/sql-parser.js +992 -0
  192. package/dist/esm/cli/utils/type-generator.js +2058 -0
  193. package/dist/esm/condition/array-condition-builder.js +495 -0
  194. package/dist/esm/condition/array-numeric-condition-builder.js +182 -0
  195. package/dist/esm/condition/array-specialized-condition-builder.js +200 -0
  196. package/dist/esm/condition/array-string-condition-builder.js +142 -0
  197. package/dist/esm/condition/base-condition-builder.js +1 -0
  198. package/dist/esm/condition/condition-collector.js +275 -0
  199. package/dist/esm/condition/fulltext-condition-builder.js +53 -0
  200. package/dist/esm/condition/geometric-condition-builder.js +200 -0
  201. package/dist/esm/condition/index.js +7 -0
  202. package/dist/esm/condition/jsonb-condition-builder.js +152 -0
  203. package/dist/esm/condition/network-condition-builder.js +222 -0
  204. package/dist/esm/condition/range-condition-builder.js +74 -0
  205. package/dist/esm/config/config.js +150 -0
  206. package/dist/esm/config/index.js +1 -0
  207. package/dist/esm/constants/pg-values.js +63 -0
  208. package/dist/esm/copy/copy-builder.js +308 -0
  209. package/dist/esm/copy/index.js +1 -0
  210. package/dist/esm/core/query-builder.js +426 -0
  211. package/dist/esm/core/relq-client.js +1791 -0
  212. package/dist/esm/core/typed-kuery-client.js +1 -0
  213. package/dist/esm/count/count-builder.js +81 -0
  214. package/dist/esm/count/index.js +1 -0
  215. package/dist/esm/cte/cte-builder.js +82 -0
  216. package/dist/esm/cte/index.js +1 -0
  217. package/dist/esm/ddl/function.js +45 -0
  218. package/dist/esm/ddl/index.js +2 -0
  219. package/dist/esm/ddl/sql.js +51 -0
  220. package/dist/esm/delete/delete-builder.js +128 -0
  221. package/dist/esm/delete/index.js +1 -0
  222. package/dist/esm/errors/relq-errors.js +310 -0
  223. package/dist/esm/examples/fulltext-search-test.js +117 -0
  224. package/dist/esm/explain/explain-builder.js +95 -0
  225. package/dist/esm/explain/index.js +1 -0
  226. package/dist/esm/function/create-function-builder.js +188 -0
  227. package/dist/esm/function/index.js +1 -0
  228. package/dist/esm/functions/advanced-functions.js +231 -0
  229. package/dist/esm/functions/case-builder.js +58 -0
  230. package/dist/esm/functions/geometric-functions.js +97 -0
  231. package/dist/esm/functions/index.js +171 -0
  232. package/dist/esm/functions/network-functions.js +79 -0
  233. package/dist/esm/functions/sql-functions.js +421 -0
  234. package/dist/esm/index.js +34 -0
  235. package/dist/esm/indexing/create-index-builder.js +180 -0
  236. package/dist/esm/indexing/drop-index-builder.js +81 -0
  237. package/dist/esm/indexing/index-types.js +1 -0
  238. package/dist/esm/indexing/index.js +2 -0
  239. package/dist/esm/insert/conflict-builder.js +162 -0
  240. package/dist/esm/insert/index.js +1 -0
  241. package/dist/esm/insert/insert-builder.js +247 -0
  242. package/dist/esm/introspect/index.js +224 -0
  243. package/dist/esm/maintenance/index.js +1 -0
  244. package/dist/esm/maintenance/vacuum-builder.js +158 -0
  245. package/dist/esm/pubsub/index.js +1 -0
  246. package/dist/esm/pubsub/listen-notify-builder.js +48 -0
  247. package/dist/esm/pubsub/listener-connection.js +173 -0
  248. package/dist/esm/raw/index.js +1 -0
  249. package/dist/esm/raw/raw-query-builder.js +20 -0
  250. package/dist/esm/schema/index.js +1 -0
  251. package/dist/esm/schema/schema-builder.js +1150 -0
  252. package/dist/esm/schema-builder.js +2 -0
  253. package/dist/esm/schema-definition/column-types.js +738 -0
  254. package/dist/esm/schema-definition/index.js +10 -0
  255. package/dist/esm/schema-definition/introspection.js +614 -0
  256. package/dist/esm/schema-definition/partitions.js +123 -0
  257. package/dist/esm/schema-definition/pg-enum.js +70 -0
  258. package/dist/esm/schema-definition/pg-function.js +85 -0
  259. package/dist/esm/schema-definition/pg-sequence.js +50 -0
  260. package/dist/esm/schema-definition/pg-trigger.js +102 -0
  261. package/dist/esm/schema-definition/relations.js +90 -0
  262. package/dist/esm/schema-definition/sql-expressions.js +193 -0
  263. package/dist/esm/schema-definition/table-definition.js +630 -0
  264. package/dist/esm/select/aggregate-builder.js +172 -0
  265. package/dist/esm/select/index.js +1 -0
  266. package/dist/esm/select/select-builder.js +226 -0
  267. package/dist/esm/sequence/index.js +1 -0
  268. package/dist/esm/sequence/sequence-builder.js +255 -0
  269. package/dist/esm/table/alter-table-builder.js +138 -0
  270. package/dist/esm/table/constraint-builder.js +95 -0
  271. package/dist/esm/table/create-table-builder.js +241 -0
  272. package/dist/esm/table/index.js +5 -0
  273. package/dist/esm/table/partition-builder.js +121 -0
  274. package/dist/esm/table/truncate-builder.js +63 -0
  275. package/dist/esm/transaction/index.js +1 -0
  276. package/dist/esm/transaction/transaction-builder.js +70 -0
  277. package/dist/esm/trigger/create-trigger-builder.js +166 -0
  278. package/dist/esm/trigger/index.js +1 -0
  279. package/dist/esm/types/aggregate-types.js +1 -0
  280. package/dist/esm/types/config-types.js +36 -0
  281. package/dist/esm/types/inference-types.js +12 -0
  282. package/dist/esm/types/pagination-types.js +4 -0
  283. package/dist/esm/types/result-types.js +1 -0
  284. package/dist/esm/types/schema-types.js +1 -0
  285. package/dist/esm/types/subscription-types.js +1 -0
  286. package/dist/esm/types.js +1 -0
  287. package/dist/esm/update/array-update-builder.js +192 -0
  288. package/dist/esm/update/index.js +2 -0
  289. package/dist/esm/update/update-builder.js +188 -0
  290. package/dist/esm/utils/case-converter.js +55 -0
  291. package/dist/esm/utils/environment-detection.js +113 -0
  292. package/dist/esm/utils/index.js +2 -0
  293. package/dist/esm/utils/pool-defaults.js +100 -0
  294. package/dist/esm/utils/type-coercion.js +110 -0
  295. package/dist/esm/view/create-view-builder.js +171 -0
  296. package/dist/esm/view/index.js +1 -0
  297. package/dist/esm/window/index.js +1 -0
  298. package/dist/esm/window/window-builder.js +73 -0
  299. package/dist/index.cjs +1 -0
  300. package/dist/index.d.ts +10341 -0
  301. package/dist/index.js +1 -0
  302. package/dist/schema-builder.cjs +1 -0
  303. package/dist/schema-builder.d.ts +2272 -0
  304. package/dist/schema-builder.js +1 -0
  305. package/package.json +55 -0
@@ -0,0 +1,999 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseSqlFile = parseSqlFile;
4
+ exports.parseFunctions = parseFunctions;
5
+ exports.parseTriggers = parseTriggers;
6
+ exports.parseProcedures = parseProcedures;
7
+ exports.parseComments = parseComments;
8
+ function parseSqlFile(sqlContent) {
9
+ const tables = [];
10
+ const enums = [];
11
+ const domains = [];
12
+ const compositeTypes = [];
13
+ const sequences = [];
14
+ const collations = [];
15
+ const foreignTables = [];
16
+ const extensions = [];
17
+ const partitions = [];
18
+ const cleanedSql = removeComments(sqlContent);
19
+ parseExtensions(cleanedSql, extensions);
20
+ parseEnums(cleanedSql, enums);
21
+ parseDomains(cleanedSql, domains);
22
+ parseCompositeTypes(cleanedSql, compositeTypes);
23
+ parseSequences(cleanedSql, sequences);
24
+ parseCollations(cleanedSql, collations);
25
+ parseTables(cleanedSql, tables, partitions, enums, domains);
26
+ parseIndexes(cleanedSql, tables);
27
+ parseForeignTables(cleanedSql, foreignTables);
28
+ return { tables, enums, domains, compositeTypes, sequences, collations, foreignTables, extensions, partitions };
29
+ }
30
+ function removeComments(sql) {
31
+ let result = sql.replace(/\/\*[\s\S]*?\*\//g, '');
32
+ result = result.replace(/--[^\n]*/g, '');
33
+ return result;
34
+ }
35
+ function parseExtensions(sql, extensions) {
36
+ const regex = /CREATE\s+EXTENSION\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:"([^"]+)"|'([^']+)'|([a-zA-Z_][a-zA-Z0-9_-]*))/gi;
37
+ let match;
38
+ while ((match = regex.exec(sql)) !== null) {
39
+ const extName = match[1] || match[2] || match[3];
40
+ if (extName && !extensions.includes(extName)) {
41
+ extensions.push(extName);
42
+ }
43
+ }
44
+ }
45
+ function parseEnums(sql, enums) {
46
+ const regex = /CREATE\s+TYPE\s+(\w+)\s+AS\s+ENUM\s*\(\s*([\s\S]*?)\s*\)/gi;
47
+ let match;
48
+ while ((match = regex.exec(sql)) !== null) {
49
+ const name = match[1];
50
+ const valuesStr = match[2];
51
+ const values = valuesStr.match(/'([^']+)'/g)?.map(v => v.replace(/'/g, '')) || [];
52
+ enums.push({ name, values });
53
+ }
54
+ }
55
+ function parseDomains(sql, domains) {
56
+ const regex = /CREATE\s+DOMAIN\s+(\w+)\s+AS\s+(\w+(?:\([^)]+\))?)([\s\S]*?);/gi;
57
+ let match;
58
+ while ((match = regex.exec(sql)) !== null) {
59
+ const name = match[1];
60
+ const baseType = match[2];
61
+ const modifiers = match[3] || '';
62
+ let check;
63
+ const checkMatch = modifiers.match(/CHECK\s*\(\s*([\s\S]*?)\s*\)(?:\s*;|\s*$|\s+(?:DEFAULT|NOT\s+NULL))/i);
64
+ if (checkMatch) {
65
+ check = checkMatch[1].trim();
66
+ }
67
+ else {
68
+ const simpleCheck = modifiers.match(/CHECK\s*\(\s*([\s\S]+?)\s*\)\s*$/i);
69
+ if (simpleCheck) {
70
+ check = simpleCheck[1].trim();
71
+ }
72
+ }
73
+ let defaultValue;
74
+ const defaultMatch = modifiers.match(/DEFAULT\s+([^;\s]+(?:\([^)]*\))?)/i);
75
+ if (defaultMatch) {
76
+ defaultValue = defaultMatch[1].trim();
77
+ }
78
+ const notNull = /NOT\s+NULL/i.test(modifiers);
79
+ domains.push({
80
+ name,
81
+ baseType,
82
+ check,
83
+ default: defaultValue,
84
+ notNull: notNull || undefined,
85
+ });
86
+ }
87
+ }
88
+ function parseCompositeTypes(sql, compositeTypes) {
89
+ const regex = /CREATE\s+TYPE\s+(\w+)\s+AS\s*\(/gi;
90
+ let match;
91
+ while ((match = regex.exec(sql)) !== null) {
92
+ const name = match[1];
93
+ const startIdx = match.index + match[0].length;
94
+ let depth = 1;
95
+ let endIdx = startIdx;
96
+ for (let i = startIdx; i < sql.length && depth > 0; i++) {
97
+ if (sql[i] === '(')
98
+ depth++;
99
+ else if (sql[i] === ')')
100
+ depth--;
101
+ if (depth === 0)
102
+ endIdx = i;
103
+ }
104
+ if (endIdx > startIdx) {
105
+ const body = sql.substring(startIdx, endIdx);
106
+ const attributes = parseCompositeTypeBody(body);
107
+ if (attributes.length > 0) {
108
+ compositeTypes.push({ name, attributes });
109
+ }
110
+ }
111
+ }
112
+ }
113
+ function parseCompositeTypeBody(body) {
114
+ const attributes = [];
115
+ const lines = splitByComma(body);
116
+ for (const line of lines) {
117
+ const trimmed = line.trim();
118
+ if (!trimmed)
119
+ continue;
120
+ const attrMatch = trimmed.match(/^(\w+)\s+(.+)$/);
121
+ if (attrMatch) {
122
+ attributes.push({
123
+ name: attrMatch[1],
124
+ type: attrMatch[2].trim().replace(/,\s*$/, ''),
125
+ });
126
+ }
127
+ }
128
+ return attributes;
129
+ }
130
+ function parseSequences(sql, sequences) {
131
+ const regex = /CREATE\s+SEQUENCE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)([\s\S]*?);/gi;
132
+ let match;
133
+ while ((match = regex.exec(sql)) !== null) {
134
+ const name = match[1];
135
+ const options = match[2] || '';
136
+ const seq = { name };
137
+ const asMatch = options.match(/\bAS\s+(SMALLINT|INTEGER|BIGINT)/i);
138
+ if (asMatch) {
139
+ seq.dataType = asMatch[1].toLowerCase();
140
+ }
141
+ const startMatch = options.match(/START\s+(?:WITH\s+)?(\d+)/i);
142
+ if (startMatch) {
143
+ seq.start = parseInt(startMatch[1], 10);
144
+ }
145
+ const incrementMatch = options.match(/INCREMENT\s+(?:BY\s+)?(\d+)/i);
146
+ if (incrementMatch) {
147
+ seq.increment = parseInt(incrementMatch[1], 10);
148
+ }
149
+ const minMatch = options.match(/MINVALUE\s+(\d+)/i);
150
+ if (minMatch) {
151
+ seq.minValue = parseInt(minMatch[1], 10);
152
+ }
153
+ const maxMatch = options.match(/MAXVALUE\s+(\d+)/i);
154
+ if (maxMatch) {
155
+ seq.maxValue = parseInt(maxMatch[1], 10);
156
+ }
157
+ const cacheMatch = options.match(/CACHE\s+(\d+)/i);
158
+ if (cacheMatch) {
159
+ seq.cache = parseInt(cacheMatch[1], 10);
160
+ }
161
+ if (/\bCYCLE\b/i.test(options) && !/\bNO\s+CYCLE\b/i.test(options)) {
162
+ seq.cycle = true;
163
+ }
164
+ else if (/\bNO\s+CYCLE\b/i.test(options)) {
165
+ seq.cycle = false;
166
+ }
167
+ const ownedMatch = options.match(/OWNED\s+BY\s+(\w+\.\w+)/i);
168
+ if (ownedMatch) {
169
+ seq.ownedBy = ownedMatch[1];
170
+ }
171
+ sequences.push(seq);
172
+ }
173
+ }
174
+ function parseCollations(sql, collations) {
175
+ const regex = /CREATE\s+COLLATION\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(\w+)\.)?(\w+)\s*\(([^)]+)\)/gi;
176
+ let match;
177
+ while ((match = regex.exec(sql)) !== null) {
178
+ const schema = match[1] || 'public';
179
+ const name = match[2];
180
+ const optionsStr = match[3];
181
+ const collation = { name, schema };
182
+ const localeMatch = optionsStr.match(/LOCALE\s*=\s*'([^']+)'/i);
183
+ if (localeMatch) {
184
+ collation.locale = localeMatch[1];
185
+ }
186
+ const providerMatch = optionsStr.match(/PROVIDER\s*=\s*(\w+)/i);
187
+ if (providerMatch) {
188
+ collation.provider = providerMatch[1].toLowerCase();
189
+ }
190
+ const lcCollateMatch = optionsStr.match(/LC_COLLATE\s*=\s*'([^']+)'/i);
191
+ if (lcCollateMatch) {
192
+ collation.lcCollate = lcCollateMatch[1];
193
+ }
194
+ const lcCtypeMatch = optionsStr.match(/LC_CTYPE\s*=\s*'([^']+)'/i);
195
+ if (lcCtypeMatch) {
196
+ collation.lcCtype = lcCtypeMatch[1];
197
+ }
198
+ const deterministicMatch = optionsStr.match(/DETERMINISTIC\s*=\s*(TRUE|FALSE)/i);
199
+ if (deterministicMatch) {
200
+ collation.deterministic = deterministicMatch[1].toUpperCase() === 'TRUE';
201
+ }
202
+ collations.push(collation);
203
+ }
204
+ }
205
+ function parseForeignTables(sql, foreignTables) {
206
+ const regex = /CREATE\s+FOREIGN\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(\w+)\.)?(\w+)\s*\(/gi;
207
+ let match;
208
+ while ((match = regex.exec(sql)) !== null) {
209
+ const schema = match[1] || 'public';
210
+ const name = match[2];
211
+ const startIdx = match.index + match[0].length;
212
+ const columnsBody = extractBalancedParens(sql, startIdx);
213
+ if (!columnsBody)
214
+ continue;
215
+ const afterColumns = sql.substring(startIdx + columnsBody.length + 1, startIdx + columnsBody.length + 500);
216
+ const serverMatch = afterColumns.match(/SERVER\s+(\w+)/i);
217
+ if (!serverMatch)
218
+ continue;
219
+ const serverName = serverMatch[1];
220
+ const columns = [];
221
+ const columnParts = splitByComma(columnsBody);
222
+ for (const part of columnParts) {
223
+ const trimmed = part.trim();
224
+ if (!trimmed)
225
+ continue;
226
+ const colMatch = trimmed.match(/^(\w+)\s+(\w+(?:\([^)]+\))?)\s*(.*)/i);
227
+ if (colMatch) {
228
+ const colName = colMatch[1];
229
+ const colType = colMatch[2];
230
+ const modifiers = colMatch[3] || '';
231
+ columns.push({
232
+ name: colName,
233
+ type: colType,
234
+ nullable: !modifiers.toUpperCase().includes('NOT NULL'),
235
+ });
236
+ }
237
+ }
238
+ const optionsMatch = afterColumns.match(/OPTIONS\s*\(([^)]+)\)/i);
239
+ let options;
240
+ if (optionsMatch) {
241
+ options = {};
242
+ const optPairs = optionsMatch[1].split(',');
243
+ for (const pair of optPairs) {
244
+ const kv = pair.trim().match(/(\w+)\s+'([^']+)'/);
245
+ if (kv) {
246
+ options[kv[1]] = kv[2];
247
+ }
248
+ }
249
+ }
250
+ foreignTables.push({ name, schema, serverName, columns, options });
251
+ }
252
+ }
253
+ function parseTables(sql, tables, partitions, enums, domains) {
254
+ const partitionOfRegex = /CREATE\s+TABLE\s+(\w+)\s+PARTITION\s+OF\s+(\w+)\s+(?:FOR\s+VALUES\s+([\s\S]*?)|DEFAULT)\s*(?:;|$)/gi;
255
+ let partMatch;
256
+ while ((partMatch = partitionOfRegex.exec(sql)) !== null) {
257
+ const childName = partMatch[1];
258
+ const parentTable = partMatch[2];
259
+ const boundClause = partMatch[3] ? partMatch[3].trim().replace(/;$/, '') : 'DEFAULT';
260
+ partitions.push({
261
+ name: childName,
262
+ parentTable,
263
+ partitionType: detectPartitionType(boundClause),
264
+ partitionKey: [],
265
+ partitionBound: boundClause,
266
+ });
267
+ }
268
+ const createTablePrefix = /CREATE\s+(?:UNLOGGED\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s*\(/gi;
269
+ let createMatch;
270
+ while ((createMatch = createTablePrefix.exec(sql)) !== null) {
271
+ const tableName = createMatch[1];
272
+ const startIndex = createMatch.index + createMatch[0].length;
273
+ const precedingText = sql.substring(createMatch.index, startIndex + 20);
274
+ if (/PARTITION\s+OF/i.test(precedingText)) {
275
+ continue;
276
+ }
277
+ const tableBody = extractBalancedParens(sql, startIndex);
278
+ if (!tableBody)
279
+ continue;
280
+ const afterTableIdx = startIndex + tableBody.length + 1;
281
+ const afterTableText = sql.substring(afterTableIdx, afterTableIdx + 100);
282
+ const partitionMatch = afterTableText.match(/^\s*(PARTITION\s+BY\s+(RANGE|LIST|HASH)\s*\(([^)]+)\))/i);
283
+ let isPartitioned = false;
284
+ let partitionType;
285
+ let partitionKey;
286
+ if (partitionMatch) {
287
+ isPartitioned = true;
288
+ partitionType = partitionMatch[2].toUpperCase();
289
+ partitionKey = partitionMatch[3].split(',').map(k => k.trim().replace(/[()]/g, ''));
290
+ }
291
+ const { columns, constraints, primaryKey } = parseTableBody(tableBody, enums, domains);
292
+ const childPartitions = partitions.filter(p => p.parentTable === tableName);
293
+ tables.push({
294
+ name: tableName,
295
+ schema: 'public',
296
+ columns,
297
+ indexes: [],
298
+ constraints,
299
+ rowCount: 0,
300
+ isPartitioned,
301
+ partitionType,
302
+ partitionKey,
303
+ childPartitions: childPartitions.length > 0 ? childPartitions : undefined,
304
+ });
305
+ }
306
+ }
307
+ function extractBalancedParens(str, startIndex) {
308
+ let depth = 1;
309
+ let current = '';
310
+ let inQuote = false;
311
+ let quoteChar = '';
312
+ for (let i = startIndex; i < str.length && depth > 0; i++) {
313
+ const char = str[i];
314
+ if ((char === "'" || char === '"') && !inQuote) {
315
+ inQuote = true;
316
+ quoteChar = char;
317
+ }
318
+ else if (char === quoteChar && inQuote) {
319
+ if (i + 1 < str.length && str[i + 1] === quoteChar) {
320
+ current += char;
321
+ i++;
322
+ current += str[i];
323
+ continue;
324
+ }
325
+ inQuote = false;
326
+ }
327
+ if (!inQuote) {
328
+ if (char === '(')
329
+ depth++;
330
+ else if (char === ')') {
331
+ depth--;
332
+ if (depth === 0) {
333
+ return current;
334
+ }
335
+ }
336
+ }
337
+ current += char;
338
+ }
339
+ return depth === 0 ? current : null;
340
+ }
341
+ function detectPartitionType(boundClause) {
342
+ if (boundClause.toUpperCase().includes('IN ('))
343
+ return 'LIST';
344
+ if (boundClause.toUpperCase().includes('FROM ('))
345
+ return 'RANGE';
346
+ if (boundClause.toUpperCase().includes('MODULUS'))
347
+ return 'HASH';
348
+ return 'LIST';
349
+ }
350
+ function parsePartitionClause(clause) {
351
+ const match = clause.match(/PARTITION\s+BY\s+(RANGE|LIST|HASH)\s*\(\s*([^)]+)\s*\)/i);
352
+ if (!match) {
353
+ return { type: 'LIST', keys: [] };
354
+ }
355
+ const type = match[1].toUpperCase();
356
+ const keysStr = match[2];
357
+ const keys = keysStr.split(',').map(k => k.trim().replace(/[()]/g, ''));
358
+ return { type, keys };
359
+ }
360
+ function parseTableBody(body, enums, domains) {
361
+ const columns = [];
362
+ const constraints = [];
363
+ const primaryKey = [];
364
+ const parts = splitByComma(body);
365
+ for (const part of parts) {
366
+ const trimmed = part.trim();
367
+ if (!trimmed)
368
+ continue;
369
+ if (trimmed.toUpperCase().startsWith('CONSTRAINT') ||
370
+ trimmed.toUpperCase().startsWith('PRIMARY KEY') ||
371
+ trimmed.toUpperCase().startsWith('FOREIGN KEY') ||
372
+ trimmed.toUpperCase().startsWith('UNIQUE') ||
373
+ trimmed.toUpperCase().startsWith('CHECK') ||
374
+ trimmed.toUpperCase().startsWith('EXCLUDE')) {
375
+ const constraint = parseConstraint(trimmed);
376
+ if (constraint) {
377
+ constraints.push(constraint);
378
+ if (constraint.type === 'PRIMARY KEY' && constraint.columns) {
379
+ primaryKey.push(...constraint.columns);
380
+ }
381
+ }
382
+ }
383
+ else {
384
+ const column = parseColumnDef(trimmed, enums, domains);
385
+ if (column) {
386
+ columns.push(column);
387
+ const inlineCheckMatch = trimmed.match(/\bCHECK\s*\(/i);
388
+ if (inlineCheckMatch && inlineCheckMatch.index !== undefined) {
389
+ const checkStart = inlineCheckMatch.index;
390
+ const afterCheck = trimmed.substring(checkStart + 'CHECK'.length).trim();
391
+ if (afterCheck.startsWith('(')) {
392
+ const checkExpr = extractBalancedParens(afterCheck, 1);
393
+ if (checkExpr) {
394
+ constraints.push({
395
+ name: `check_${column.name}`,
396
+ type: 'CHECK',
397
+ columns: [column.name],
398
+ definition: `CHECK (${checkExpr})`,
399
+ });
400
+ }
401
+ }
402
+ }
403
+ }
404
+ }
405
+ }
406
+ for (const pkCol of primaryKey) {
407
+ const col = columns.find(c => c.name.toLowerCase() === pkCol.toLowerCase());
408
+ if (col) {
409
+ col.isPrimaryKey = true;
410
+ }
411
+ }
412
+ return { columns, constraints, primaryKey };
413
+ }
414
+ function splitByComma(str) {
415
+ const parts = [];
416
+ let current = '';
417
+ let depth = 0;
418
+ let inQuote = false;
419
+ let quoteChar = '';
420
+ for (let i = 0; i < str.length; i++) {
421
+ const char = str[i];
422
+ if ((char === "'" || char === '"') && !inQuote) {
423
+ inQuote = true;
424
+ quoteChar = char;
425
+ }
426
+ else if (char === quoteChar && inQuote) {
427
+ inQuote = false;
428
+ }
429
+ if (!inQuote) {
430
+ if (char === '(' || char === '[')
431
+ depth++;
432
+ else if (char === ')' || char === ']')
433
+ depth--;
434
+ else if (char === ',' && depth === 0) {
435
+ parts.push(current.trim());
436
+ current = '';
437
+ continue;
438
+ }
439
+ }
440
+ current += char;
441
+ }
442
+ if (current.trim()) {
443
+ parts.push(current.trim());
444
+ }
445
+ return parts;
446
+ }
447
+ function parseColumnDef(def, enums, domains) {
448
+ const match = def.match(/^(?:"([^"]+)"|(\w+))\s+(.+)$/is);
449
+ if (!match)
450
+ return null;
451
+ const name = match[1] || match[2];
452
+ const rest = match[3];
453
+ const typeMatch = rest.match(/^([A-Za-z_][\w]*(?:\s+(?:VARYING|PRECISION|WITHOUT|WITH|TIME|ZONE))*(?:\s*\([^)]+\))?(?:\s*\[\])?)/i);
454
+ if (!typeMatch)
455
+ return null;
456
+ let dataType = typeMatch[1].trim();
457
+ const modifiers = rest.substring(typeMatch[0].length);
458
+ const isNullable = !modifiers.toUpperCase().includes('NOT NULL');
459
+ const isPrimaryKey = modifiers.toUpperCase().includes('PRIMARY KEY');
460
+ const isUnique = modifiers.toUpperCase().includes('UNIQUE');
461
+ let defaultValue = null;
462
+ const defaultIdx = modifiers.toUpperCase().indexOf('DEFAULT');
463
+ if (defaultIdx !== -1) {
464
+ const afterDefault = modifiers.substring(defaultIdx + 7).trim();
465
+ let end = 0;
466
+ let depth = 0;
467
+ let inQuote = false;
468
+ let quoteChar = '';
469
+ for (let i = 0; i < afterDefault.length; i++) {
470
+ const ch = afterDefault[i];
471
+ if (!inQuote) {
472
+ if (ch === "'" || ch === '"') {
473
+ inQuote = true;
474
+ quoteChar = ch;
475
+ }
476
+ else if (ch === '(' || ch === '[') {
477
+ depth++;
478
+ }
479
+ else if (ch === ')' || ch === ']') {
480
+ depth--;
481
+ }
482
+ else if (depth === 0 && (ch === ',' || /\s/.test(ch))) {
483
+ if (i > 0) {
484
+ end = i;
485
+ break;
486
+ }
487
+ }
488
+ }
489
+ else if (ch === quoteChar && afterDefault[i - 1] !== '\\') {
490
+ inQuote = false;
491
+ }
492
+ end = i + 1;
493
+ }
494
+ if (end > 0) {
495
+ defaultValue = afterDefault.substring(0, end).trim();
496
+ if (defaultValue.endsWith(',')) {
497
+ defaultValue = defaultValue.slice(0, -1).trim();
498
+ }
499
+ }
500
+ }
501
+ let identityGeneration = null;
502
+ const identityMatch = modifiers.match(/GENERATED\s+(ALWAYS|BY\s+DEFAULT)\s+AS\s+IDENTITY/i);
503
+ if (identityMatch) {
504
+ identityGeneration = identityMatch[1].toUpperCase().replace(/\s+/g, ' ');
505
+ }
506
+ const isGenerated = !identityGeneration && /GENERATED\s+ALWAYS\s+AS\s*\(/i.test(modifiers);
507
+ let generationExpression = null;
508
+ if (isGenerated) {
509
+ const genStartMatch = modifiers.match(/GENERATED\s+ALWAYS\s+AS\s*\(/i);
510
+ if (genStartMatch && genStartMatch.index !== undefined) {
511
+ const startIdx = genStartMatch.index + genStartMatch[0].length;
512
+ let depth = 1;
513
+ let endIdx = startIdx;
514
+ for (let i = startIdx; i < modifiers.length && depth > 0; i++) {
515
+ if (modifiers[i] === '(')
516
+ depth++;
517
+ else if (modifiers[i] === ')')
518
+ depth--;
519
+ if (depth === 0)
520
+ endIdx = i;
521
+ }
522
+ if (endIdx > startIdx) {
523
+ generationExpression = modifiers.substring(startIdx, endIdx).trim();
524
+ }
525
+ }
526
+ }
527
+ let references = null;
528
+ const refMatch = modifiers.match(/REFERENCES\s+(\w+)(?:\s*\(\s*(\w+)\s*\))?/i);
529
+ if (refMatch) {
530
+ references = {
531
+ table: refMatch[1],
532
+ column: refMatch[2] || 'id',
533
+ };
534
+ const onDeleteMatch = modifiers.match(/ON\s+DELETE\s+(CASCADE|SET\s+NULL|SET\s+DEFAULT|RESTRICT|NO\s+ACTION)/i);
535
+ if (onDeleteMatch) {
536
+ references.onDelete = onDeleteMatch[1].toUpperCase().replace(/\s+/g, ' ');
537
+ }
538
+ const onUpdateMatch = modifiers.match(/ON\s+UPDATE\s+(CASCADE|SET\s+NULL|SET\s+DEFAULT|RESTRICT|NO\s+ACTION)/i);
539
+ if (onUpdateMatch) {
540
+ references.onUpdate = onUpdateMatch[1].toUpperCase().replace(/\s+/g, ' ');
541
+ }
542
+ }
543
+ return {
544
+ name,
545
+ dataType: normalizeDataType(dataType),
546
+ isNullable: isNullable && !isPrimaryKey && !identityGeneration,
547
+ defaultValue: (isGenerated || identityGeneration) ? null : defaultValue,
548
+ isPrimaryKey,
549
+ isUnique,
550
+ maxLength: extractMaxLength(dataType),
551
+ precision: null,
552
+ scale: null,
553
+ references,
554
+ comment: undefined,
555
+ isGenerated,
556
+ generationExpression,
557
+ identityGeneration,
558
+ };
559
+ }
560
+ function normalizeDataType(type) {
561
+ const lower = type.toLowerCase().trim();
562
+ if (lower.endsWith('[]')) {
563
+ return normalizeDataType(lower.slice(0, -2)) + '[]';
564
+ }
565
+ const paramMatch = lower.match(/^([a-z_][a-z0-9_\s]*?)(?:\s*\(([^)]+)\))?$/i);
566
+ if (!paramMatch)
567
+ return lower;
568
+ const baseType = paramMatch[1].trim();
569
+ const params = paramMatch[2];
570
+ const mappings = {
571
+ 'character varying': 'varchar',
572
+ 'character': 'char',
573
+ 'timestamp without time zone': 'timestamp',
574
+ 'timestamp with time zone': 'timestamptz',
575
+ 'time without time zone': 'time',
576
+ 'time with time zone': 'timetz',
577
+ 'double precision': 'float8',
578
+ 'real': 'float4',
579
+ 'int': 'int4',
580
+ 'integer': 'int4',
581
+ 'bigint': 'int8',
582
+ 'smallint': 'int2',
583
+ 'boolean': 'bool',
584
+ 'bytea': 'bytea',
585
+ 'serial': 'serial',
586
+ 'bigserial': 'bigserial',
587
+ 'smallserial': 'smallserial',
588
+ 'text': 'text',
589
+ 'uuid': 'uuid',
590
+ 'json': 'json',
591
+ 'jsonb': 'jsonb',
592
+ 'numeric': 'numeric',
593
+ 'decimal': 'numeric',
594
+ 'varchar': 'varchar',
595
+ 'char': 'char',
596
+ 'date': 'date',
597
+ 'time': 'time',
598
+ 'timetz': 'timetz',
599
+ 'timestamp': 'timestamp',
600
+ 'timestamptz': 'timestamptz',
601
+ 'interval': 'interval',
602
+ 'inet': 'inet',
603
+ 'cidr': 'cidr',
604
+ 'macaddr': 'macaddr',
605
+ 'tsvector': 'tsvector',
606
+ 'tsquery': 'tsquery',
607
+ 'vector': 'vector',
608
+ };
609
+ const normalized = mappings[baseType] || baseType;
610
+ if (params) {
611
+ return `${normalized}(${params})`;
612
+ }
613
+ return normalized;
614
+ }
615
+ function extractMaxLength(type) {
616
+ const match = type.match(/\((\d+)\)/);
617
+ return match ? parseInt(match[1], 10) : null;
618
+ }
619
+ function parseConstraint(def) {
620
+ const upper = def.toUpperCase();
621
+ const namedMatch = def.match(/CONSTRAINT\s+(\w+)\s+(.+)/i);
622
+ const constraintDef = namedMatch ? namedMatch[2] : def;
623
+ const constraintName = namedMatch ? namedMatch[1] : '';
624
+ const constraintUpper = constraintDef.toUpperCase();
625
+ if (constraintUpper.startsWith('PRIMARY KEY')) {
626
+ const colsMatch = constraintDef.match(/PRIMARY\s+KEY\s*\(\s*([^)]+)\s*\)/i);
627
+ const columns = colsMatch ? colsMatch[1].split(',').map(c => c.trim()) : [];
628
+ return {
629
+ name: constraintName || 'pk',
630
+ type: 'PRIMARY KEY',
631
+ columns,
632
+ definition: def,
633
+ };
634
+ }
635
+ if (constraintUpper.startsWith('UNIQUE')) {
636
+ const colsMatch = constraintDef.match(/UNIQUE\s*\(\s*([^)]+)\s*\)/i);
637
+ const columns = colsMatch ? colsMatch[1].split(',').map(c => c.trim()) : [];
638
+ return {
639
+ name: constraintName || 'unique',
640
+ type: 'UNIQUE',
641
+ columns,
642
+ definition: def,
643
+ };
644
+ }
645
+ if (constraintUpper.startsWith('CHECK') || constraintUpper.includes('CHECK')) {
646
+ return {
647
+ name: constraintName || 'check',
648
+ type: 'CHECK',
649
+ columns: [],
650
+ definition: def,
651
+ };
652
+ }
653
+ if (constraintUpper.startsWith('FOREIGN KEY') || constraintUpper.includes('REFERENCES')) {
654
+ return {
655
+ name: constraintName || 'fk',
656
+ type: 'FOREIGN KEY',
657
+ columns: [],
658
+ definition: def,
659
+ };
660
+ }
661
+ if (constraintUpper.startsWith('EXCLUDE')) {
662
+ return {
663
+ name: constraintName || 'exclude',
664
+ type: 'EXCLUDE',
665
+ columns: [],
666
+ definition: def,
667
+ };
668
+ }
669
+ return null;
670
+ }
671
+ function parseIndexes(sql, tables) {
672
+ const indexRegex = /CREATE\s+(UNIQUE\s+)?INDEX\s+(?:CONCURRENTLY\s+)?(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s+ON\s+(?:ONLY\s+)?(\w+)(?:\s+USING\s+(\w+))?\s*\(/gi;
673
+ let match;
674
+ while ((match = indexRegex.exec(sql)) !== null) {
675
+ const isUnique = !!match[1];
676
+ const indexName = match[2];
677
+ const tableName = match[3];
678
+ const indexType = match[4] || 'btree';
679
+ const startIdx = match.index + match[0].length;
680
+ const indexContent = extractBalancedParensForIndex(sql, startIdx);
681
+ if (!indexContent)
682
+ continue;
683
+ const afterColumns = sql.substring(startIdx + indexContent.length + 1, startIdx + indexContent.length + 500);
684
+ const whereMatch = afterColumns.match(/^\s*WHERE\s+(.+?)(?:;|$)/i);
685
+ const whereClause = whereMatch ? whereMatch[1].trim().replace(/;$/, '') : null;
686
+ const { columns, expression } = parseIndexColumns(indexContent);
687
+ const endIdx = whereMatch
688
+ ? startIdx + indexContent.length + 1 + whereMatch[0].length
689
+ : startIdx + indexContent.length + 1;
690
+ const fullDefinition = sql.substring(match.index, endIdx).trim().replace(/;$/, '');
691
+ const table = tables.find(t => t.name.toLowerCase() === tableName.toLowerCase());
692
+ if (table) {
693
+ table.indexes.push({
694
+ name: indexName,
695
+ columns,
696
+ isUnique,
697
+ isPrimary: false,
698
+ type: indexType,
699
+ definition: fullDefinition,
700
+ whereClause,
701
+ expression: expression || undefined,
702
+ });
703
+ }
704
+ }
705
+ }
706
+ function extractBalancedParensForIndex(str, startIndex) {
707
+ let depth = 1;
708
+ let current = '';
709
+ for (let i = startIndex; i < str.length && depth > 0; i++) {
710
+ const char = str[i];
711
+ if (char === '(')
712
+ depth++;
713
+ else if (char === ')') {
714
+ depth--;
715
+ if (depth === 0)
716
+ return current;
717
+ }
718
+ current += char;
719
+ }
720
+ return depth === 0 ? current : null;
721
+ }
722
+ function parseIndexColumns(content) {
723
+ const columns = [];
724
+ const operatorClasses = [];
725
+ let hasExpression = false;
726
+ const knownOpClasses = new Set([
727
+ 'gin_trgm_ops', 'gist_trgm_ops',
728
+ 'jsonb_ops', 'jsonb_path_ops',
729
+ 'array_ops', 'tsvector_ops',
730
+ 'inet_ops', 'range_ops',
731
+ 'bpchar_pattern_ops', 'text_pattern_ops', 'varchar_pattern_ops',
732
+ ]);
733
+ const parts = splitByComma(content);
734
+ for (const part of parts) {
735
+ const trimmed = part.trim();
736
+ let cleaned = trimmed.replace(/\s+(ASC|DESC|NULLS\s+(FIRST|LAST))$/gi, '').trim();
737
+ const opClassMatch = cleaned.match(/\s+([a-z_][a-z0-9_]*_ops)\s*$/i);
738
+ if (opClassMatch && knownOpClasses.has(opClassMatch[1].toLowerCase())) {
739
+ operatorClasses.push(opClassMatch[1]);
740
+ cleaned = cleaned.replace(/\s+[a-z_][a-z0-9_]*_ops\s*$/i, '').trim();
741
+ }
742
+ if (/^[a-z_][a-z0-9_]*$/i.test(cleaned)) {
743
+ columns.push(cleaned);
744
+ }
745
+ else {
746
+ hasExpression = true;
747
+ const colMatches = cleaned.match(/\b([a-z_][a-z0-9_]*)\b(?=\s*[,):]|$)/gi);
748
+ if (colMatches) {
749
+ const sqlFunctions = new Set(['lower', 'upper', 'coalesce', 'concat', 'trim', 'to_tsvector',
750
+ 'to_tsquery', 'substring', 'length', 'position', 'replace', 'now', 'current_timestamp',
751
+ 'array', 'nullif', 'greatest', 'least', 'text', 'varchar', 'int', 'integer', 'boolean']);
752
+ for (const col of colMatches) {
753
+ if (!sqlFunctions.has(col.toLowerCase()) && !columns.includes(col)) {
754
+ columns.push(col);
755
+ }
756
+ }
757
+ }
758
+ }
759
+ }
760
+ return {
761
+ columns,
762
+ expression: hasExpression ? content : null,
763
+ operatorClasses,
764
+ };
765
+ }
766
+ function parseFunctions(sql) {
767
+ const functions = [];
768
+ const funcStartRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(\w+)\s*\(/gi;
769
+ let startMatch;
770
+ while ((startMatch = funcStartRegex.exec(sql)) !== null) {
771
+ const name = startMatch[1];
772
+ const argsStartIdx = startMatch.index + startMatch[0].length;
773
+ const argsBody = extractBalancedParens(sql, argsStartIdx);
774
+ if (argsBody === null)
775
+ continue;
776
+ const afterArgsIdx = argsStartIdx + argsBody.length + 1;
777
+ const afterArgsPreview = sql.substring(afterArgsIdx, afterArgsIdx + 500);
778
+ const afterArgsFull = sql.substring(afterArgsIdx);
779
+ const returnsMatch = afterArgsPreview.match(/^\s*RETURNS\s+((?:SETOF\s+)?(?:TABLE\s*\([^)]+\)|\w+(?:\s+(?:ZONE|PRECISION|VARYING|WITH(?:OUT)?|TIME))*(?:\s*\[\])?))/i);
780
+ let returnType = 'void';
781
+ if (returnsMatch) {
782
+ returnType = returnsMatch[1].trim();
783
+ }
784
+ const bodyDelimMatch = afterArgsFull.match(/(\$\w*\$)/);
785
+ if (!bodyDelimMatch)
786
+ continue;
787
+ const bodyDelim = bodyDelimMatch[1];
788
+ const bodyStartRelative = afterArgsFull.indexOf(bodyDelim) + bodyDelim.length;
789
+ const bodyEndRelative = afterArgsFull.indexOf(bodyDelim, bodyStartRelative);
790
+ if (bodyEndRelative === -1)
791
+ continue;
792
+ const beforeBody = afterArgsFull.substring(0, bodyStartRelative);
793
+ const afterBody = afterArgsFull.substring(bodyEndRelative, bodyEndRelative + 100);
794
+ const langMatchBefore = beforeBody.match(/LANGUAGE\s+(\w+)/i);
795
+ const langMatchAfter = afterBody.match(/LANGUAGE\s+(\w+)/i);
796
+ const language = langMatchBefore ? langMatchBefore[1] :
797
+ langMatchAfter ? langMatchAfter[1] : 'plpgsql';
798
+ const argTypes = [];
799
+ const argParts = splitByComma(argsBody);
800
+ for (const arg of argParts) {
801
+ const trimmedArg = arg.trim();
802
+ if (!trimmedArg)
803
+ continue;
804
+ if (/^\s*OUT\s+/i.test(trimmedArg) || /^\s*INOUT\s+/i.test(trimmedArg)) {
805
+ continue;
806
+ }
807
+ let argStr = trimmedArg.replace(/^\s*IN\s+/i, '').replace(/^\s*VARIADIC\s+/i, 'VARIADIC ');
808
+ const defaultIdx = argStr.toUpperCase().indexOf(' DEFAULT ');
809
+ if (defaultIdx !== -1) {
810
+ argStr = argStr.substring(0, defaultIdx);
811
+ }
812
+ const parts = argStr.trim().split(/\s+/);
813
+ if (parts.length >= 2) {
814
+ argTypes.push(parts.slice(1).join(' '));
815
+ }
816
+ else if (parts.length === 1 && parts[0]) {
817
+ argTypes.push(parts[0]);
818
+ }
819
+ }
820
+ const fullDefEndIdx = afterArgsIdx + bodyEndRelative + bodyDelim.length;
821
+ const definition = sql.substring(startMatch.index, fullDefEndIdx);
822
+ functions.push({
823
+ name,
824
+ schema: 'public',
825
+ returnType,
826
+ argTypes,
827
+ language,
828
+ definition,
829
+ isAggregate: false,
830
+ volatility: 'VOLATILE',
831
+ });
832
+ }
833
+ return functions;
834
+ }
835
+ function parseTriggers(sql) {
836
+ const triggers = [];
837
+ const sqlNoComments = sql.replace(/--[^\n]*$/gm, '');
838
+ const triggerRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?(?:CONSTRAINT\s+)?TRIGGER\s+(\w+)([\s\S]*?)EXECUTE\s+(?:FUNCTION|PROCEDURE)\s+(\w+)\s*\([^)]*\)/gi;
839
+ let match;
840
+ while ((match = triggerRegex.exec(sqlNoComments)) !== null) {
841
+ const name = match[1];
842
+ const body = match[2];
843
+ const functionName = match[3];
844
+ const timingMatch = body.match(/\b(BEFORE|AFTER|INSTEAD\s+OF)\b/i);
845
+ const timing = timingMatch ? timingMatch[1].replace(/\s+/g, ' ').toUpperCase() : 'AFTER';
846
+ const eventsMatch = body.match(/(?:BEFORE|AFTER|INSTEAD\s+OF)\s+((?:INSERT|UPDATE|DELETE|TRUNCATE)(?:\s+OR\s+(?:INSERT|UPDATE|DELETE|TRUNCATE))*)/i);
847
+ let events = [];
848
+ if (eventsMatch) {
849
+ events = eventsMatch[1].split(/\s+OR\s+/i).map(e => e.trim().toUpperCase());
850
+ }
851
+ const updateOfMatch = body.match(/UPDATE\s+OF\s+([\w\s,]+?)(?:\s+ON\s+|\s+OR\s+)/i);
852
+ let updateColumns;
853
+ if (updateOfMatch) {
854
+ updateColumns = updateOfMatch[1].split(',').map(c => c.trim());
855
+ }
856
+ const tableMatch = body.match(/\bON\s+(\w+)/i);
857
+ const tableName = tableMatch ? tableMatch[1] : '';
858
+ const forEachMatch = body.match(/FOR\s+EACH\s+(ROW|STATEMENT)/i);
859
+ const forEach = forEachMatch ? forEachMatch[1].toUpperCase() : 'STATEMENT';
860
+ const whenMatch = body.match(/WHEN\s*\((.+?)\)\s*(?:EXECUTE|$)/is);
861
+ const condition = whenMatch ? whenMatch[1].trim() : undefined;
862
+ const refOldMatch = body.match(/REFERENCING\s+.*?OLD\s+TABLE\s+AS\s+(\w+)/i);
863
+ const refNewMatch = body.match(/REFERENCING\s+.*?NEW\s+TABLE\s+AS\s+(\w+)/i);
864
+ const referencingOld = refOldMatch ? refOldMatch[1] : undefined;
865
+ const referencingNew = refNewMatch ? refNewMatch[1] : undefined;
866
+ const isConstraint = /CREATE\s+(?:OR\s+REPLACE\s+)?CONSTRAINT\s+TRIGGER/i.test(match[0]);
867
+ const isDeferrable = /\bDEFERRABLE\b/i.test(body);
868
+ const isInitiallyDeferred = /\bINITIALLY\s+DEFERRED\b/i.test(body);
869
+ if (tableName) {
870
+ triggers.push({
871
+ name,
872
+ tableName,
873
+ timing,
874
+ event: events[0] || 'INSERT',
875
+ functionName,
876
+ definition: match[0],
877
+ isEnabled: true,
878
+ });
879
+ }
880
+ }
881
+ return triggers;
882
+ }
883
+ function parseProcedures(sql) {
884
+ const procedures = [];
885
+ const procRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?PROCEDURE\s+(\w+)\s*\(([^)]*)\)\s+(?:LANGUAGE\s+(\w+)\s+)?(?:AS\s+)?(?:\$\w*\$|\$\$)([\s\S]*?)(?:\$\w*\$|\$\$)/gi;
886
+ let match;
887
+ while ((match = procRegex.exec(sql)) !== null) {
888
+ const name = match[1];
889
+ const argsStr = match[2];
890
+ const language = match[3] || 'plpgsql';
891
+ const argTypes = argsStr.split(',')
892
+ .map(arg => arg.trim())
893
+ .filter(Boolean)
894
+ .map(arg => {
895
+ const parts = arg.split(/\s+/);
896
+ return parts.length > 1 ? parts.slice(1).join(' ') : parts[0];
897
+ });
898
+ procedures.push({
899
+ name,
900
+ schema: 'public',
901
+ returnType: 'void',
902
+ argTypes,
903
+ language,
904
+ definition: match[0],
905
+ isAggregate: false,
906
+ volatility: 'VOLATILE',
907
+ });
908
+ }
909
+ return procedures;
910
+ }
911
+ function parseComments(sql) {
912
+ const comments = [];
913
+ const tableCommentRegex = /COMMENT\s+ON\s+TABLE\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
914
+ let match;
915
+ while ((match = tableCommentRegex.exec(sql)) !== null) {
916
+ comments.push({
917
+ objectType: 'TABLE',
918
+ objectName: match[1],
919
+ comment: match[2].replace(/''/g, "'"),
920
+ });
921
+ }
922
+ const columnCommentRegex = /COMMENT\s+ON\s+COLUMN\s+(?:(?:\w+)\.)?(\w+)\.(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
923
+ while ((match = columnCommentRegex.exec(sql)) !== null) {
924
+ comments.push({
925
+ objectType: 'COLUMN',
926
+ objectName: match[1],
927
+ subObjectName: match[2],
928
+ comment: match[3].replace(/''/g, "'"),
929
+ });
930
+ }
931
+ const indexCommentRegex = /COMMENT\s+ON\s+INDEX\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
932
+ while ((match = indexCommentRegex.exec(sql)) !== null) {
933
+ comments.push({
934
+ objectType: 'INDEX',
935
+ objectName: match[1],
936
+ comment: match[2].replace(/''/g, "'"),
937
+ });
938
+ }
939
+ const functionCommentRegex = /COMMENT\s+ON\s+FUNCTION\s+(?:(?:\w+)\.)?(\w+)(?:\s*\([^)]*\))?\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
940
+ while ((match = functionCommentRegex.exec(sql)) !== null) {
941
+ comments.push({
942
+ objectType: 'FUNCTION',
943
+ objectName: match[1],
944
+ comment: match[2].replace(/''/g, "'"),
945
+ });
946
+ }
947
+ const procedureCommentRegex = /COMMENT\s+ON\s+PROCEDURE\s+(?:(?:\w+)\.)?(\w+)(?:\s*\([^)]*\))?\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
948
+ while ((match = procedureCommentRegex.exec(sql)) !== null) {
949
+ comments.push({
950
+ objectType: 'PROCEDURE',
951
+ objectName: match[1],
952
+ comment: match[2].replace(/''/g, "'"),
953
+ });
954
+ }
955
+ const triggerCommentRegex = /COMMENT\s+ON\s+TRIGGER\s+(\w+)\s+ON\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
956
+ while ((match = triggerCommentRegex.exec(sql)) !== null) {
957
+ comments.push({
958
+ objectType: 'TRIGGER',
959
+ objectName: match[1],
960
+ subObjectName: match[2],
961
+ comment: match[3].replace(/''/g, "'"),
962
+ });
963
+ }
964
+ const constraintCommentRegex = /COMMENT\s+ON\s+CONSTRAINT\s+(\w+)\s+ON\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
965
+ while ((match = constraintCommentRegex.exec(sql)) !== null) {
966
+ comments.push({
967
+ objectType: 'CONSTRAINT',
968
+ objectName: match[1],
969
+ subObjectName: match[2],
970
+ comment: match[3].replace(/''/g, "'"),
971
+ });
972
+ }
973
+ const sequenceCommentRegex = /COMMENT\s+ON\s+SEQUENCE\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
974
+ while ((match = sequenceCommentRegex.exec(sql)) !== null) {
975
+ comments.push({
976
+ objectType: 'SEQUENCE',
977
+ objectName: match[1],
978
+ comment: match[2].replace(/''/g, "'"),
979
+ });
980
+ }
981
+ const domainCommentRegex = /COMMENT\s+ON\s+DOMAIN\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
982
+ while ((match = domainCommentRegex.exec(sql)) !== null) {
983
+ comments.push({
984
+ objectType: 'DOMAIN',
985
+ objectName: match[1],
986
+ comment: match[2].replace(/''/g, "'"),
987
+ });
988
+ }
989
+ const typeCommentRegex = /COMMENT\s+ON\s+TYPE\s+(?:(?:\w+)\.)?(\w+)\s+IS\s+'((?:[^']|'')*?)'\s*;/gi;
990
+ while ((match = typeCommentRegex.exec(sql)) !== null) {
991
+ comments.push({
992
+ objectType: 'TYPE',
993
+ objectName: match[1],
994
+ comment: match[2].replace(/''/g, "'"),
995
+ });
996
+ }
997
+ return comments;
998
+ }
999
+ exports.default = parseSqlFile;