@adecore/database 0.0.1 → 0.16.0-beta.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 (400) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -1
  3. package/dist/DatabaseProvider.d.ts +13 -0
  4. package/dist/DatabaseProvider.js +17 -0
  5. package/dist/actions.d.ts +40 -0
  6. package/dist/actions.js +1 -0
  7. package/dist/client/client.d.ts +5 -0
  8. package/dist/client/client.js +245 -0
  9. package/dist/client/index.d.ts +2 -0
  10. package/dist/client/index.js +2 -0
  11. package/dist/client/types.d.ts +97 -0
  12. package/dist/client/types.js +13 -0
  13. package/dist/client-context.d.ts +16 -0
  14. package/dist/client-context.js +17 -0
  15. package/dist/connections/ConnectionForm.d.ts +10 -0
  16. package/dist/connections/ConnectionForm.js +145 -0
  17. package/dist/connections/ConnectionManager.d.ts +12 -0
  18. package/dist/connections/ConnectionManager.js +93 -0
  19. package/dist/connections/EngineIcon.d.ts +9 -0
  20. package/dist/connections/EngineIcon.js +7 -0
  21. package/dist/connections/connection-config.d.ts +16 -0
  22. package/dist/connections/connection-config.js +107 -0
  23. package/dist/connections/docker.d.ts +9 -0
  24. package/dist/connections/docker.js +38 -0
  25. package/dist/connections/engine-icons.d.ts +3 -0
  26. package/dist/connections/engine-icons.js +3 -0
  27. package/dist/connections/use-docker-containers.d.ts +17 -0
  28. package/dist/connections/use-docker-containers.js +26 -0
  29. package/dist/console/DestructiveDialog.d.ts +9 -0
  30. package/dist/console/DestructiveDialog.js +9 -0
  31. package/dist/console/HistoryPanel.d.ts +10 -0
  32. package/dist/console/HistoryPanel.js +15 -0
  33. package/dist/console/QueryConsole.d.ts +14 -0
  34. package/dist/console/QueryConsole.js +261 -0
  35. package/dist/console/SchemaPicker.d.ts +11 -0
  36. package/dist/console/SchemaPicker.js +8 -0
  37. package/dist/console/SqlEditor.d.ts +12 -0
  38. package/dist/console/SqlEditor.js +61 -0
  39. package/dist/console/StatementResultView.d.ts +18 -0
  40. package/dist/console/StatementResultView.js +32 -0
  41. package/dist/console/TransactionControls.d.ts +13 -0
  42. package/dist/console/TransactionControls.js +12 -0
  43. package/dist/console/destructive.d.ts +7 -0
  44. package/dist/console/destructive.js +43 -0
  45. package/dist/console/editing.d.ts +12 -0
  46. package/dist/console/editing.js +60 -0
  47. package/dist/console/history.d.ts +13 -0
  48. package/dist/console/history.js +43 -0
  49. package/dist/console/labels.d.ts +1 -0
  50. package/dist/console/labels.js +6 -0
  51. package/dist/console/outcome.d.ts +6 -0
  52. package/dist/console/outcome.js +11 -0
  53. package/dist/console/schemas.d.ts +2 -0
  54. package/dist/console/schemas.js +5 -0
  55. package/dist/console/shortcuts.d.ts +2 -0
  56. package/dist/console/shortcuts.js +3 -0
  57. package/dist/console/useConsoleHistory.d.ts +7 -0
  58. package/dist/console/useConsoleHistory.js +22 -0
  59. package/dist/ddl/alter-mysql.d.ts +4 -0
  60. package/dist/ddl/alter-mysql.js +79 -0
  61. package/dist/ddl/alter-sqlite.d.ts +4 -0
  62. package/dist/ddl/alter-sqlite.js +80 -0
  63. package/dist/ddl/alter.d.ts +4 -0
  64. package/dist/ddl/alter.js +24 -0
  65. package/dist/ddl/column.d.ts +9 -0
  66. package/dist/ddl/column.js +62 -0
  67. package/dist/ddl/create.d.ts +12 -0
  68. package/dist/ddl/create.js +74 -0
  69. package/dist/ddl/dialect.d.ts +12 -0
  70. package/dist/ddl/dialect.js +34 -0
  71. package/dist/ddl/diff.d.ts +34 -0
  72. package/dist/ddl/diff.js +111 -0
  73. package/dist/ddl/draft.d.ts +46 -0
  74. package/dist/ddl/draft.js +70 -0
  75. package/dist/ddl/index.d.ts +10 -0
  76. package/dist/ddl/index.js +10 -0
  77. package/dist/ddl/parse.d.ts +17 -0
  78. package/dist/ddl/parse.js +131 -0
  79. package/dist/ddl/statements.d.ts +6 -0
  80. package/dist/ddl/statements.js +12 -0
  81. package/dist/ddl/types.d.ts +6 -0
  82. package/dist/ddl/types.js +52 -0
  83. package/dist/ddl/validate.d.ts +7 -0
  84. package/dist/ddl/validate.js +38 -0
  85. package/dist/designer/ColumnsEditor.d.ts +2 -0
  86. package/dist/designer/ColumnsEditor.js +24 -0
  87. package/dist/designer/DesignerView.d.ts +25 -0
  88. package/dist/designer/DesignerView.js +33 -0
  89. package/dist/designer/ForeignKeysEditor.d.ts +7 -0
  90. package/dist/designer/ForeignKeysEditor.js +24 -0
  91. package/dist/designer/IndexesEditor.d.ts +2 -0
  92. package/dist/designer/IndexesEditor.js +13 -0
  93. package/dist/designer/NamePicker.d.ts +11 -0
  94. package/dist/designer/NamePicker.js +13 -0
  95. package/dist/designer/OptionsEditor.d.ts +2 -0
  96. package/dist/designer/OptionsEditor.js +17 -0
  97. package/dist/designer/SqlPreview.d.ts +12 -0
  98. package/dist/designer/SqlPreview.js +17 -0
  99. package/dist/designer/TableDesigner.d.ts +10 -0
  100. package/dist/designer/TableDesigner.js +103 -0
  101. package/dist/designer/edit.d.ts +17 -0
  102. package/dist/designer/edit.js +115 -0
  103. package/dist/designer/editor-props.d.ts +10 -0
  104. package/dist/designer/editor-props.js +1 -0
  105. package/dist/designer/plan.d.ts +9 -0
  106. package/dist/designer/plan.js +13 -0
  107. package/dist/designer/recover.d.ts +2 -0
  108. package/dist/designer/recover.js +18 -0
  109. package/dist/designer/useReferenceColumns.d.ts +3 -0
  110. package/dist/designer/useReferenceColumns.js +36 -0
  111. package/dist/explorer/DatabaseExplorer.d.ts +13 -0
  112. package/dist/explorer/DatabaseExplorer.js +264 -0
  113. package/dist/explorer/RowMenu.d.ts +11 -0
  114. package/dist/explorer/RowMenu.js +89 -0
  115. package/dist/explorer/TableDialog.d.ts +15 -0
  116. package/dist/explorer/TableDialog.js +50 -0
  117. package/dist/explorer/expansion.d.ts +7 -0
  118. package/dist/explorer/expansion.js +31 -0
  119. package/dist/explorer/table-statements.d.ts +5 -0
  120. package/dist/explorer/table-statements.js +12 -0
  121. package/dist/explorer/tree.d.ts +125 -0
  122. package/dist/explorer/tree.js +295 -0
  123. package/dist/explorer/use-explorer-loads.d.ts +12 -0
  124. package/dist/explorer/use-explorer-loads.js +109 -0
  125. package/dist/explorer/use-stored-expansion.d.ts +9 -0
  126. package/dist/explorer/use-stored-expansion.js +47 -0
  127. package/dist/grid/CellEditor.d.ts +9 -0
  128. package/dist/grid/CellEditor.js +29 -0
  129. package/dist/grid/CopyAsMenu.d.ts +7 -0
  130. package/dist/grid/CopyAsMenu.js +10 -0
  131. package/dist/grid/DataGrid.d.ts +36 -0
  132. package/dist/grid/DataGrid.js +512 -0
  133. package/dist/grid/EnumPicker.d.ts +17 -0
  134. package/dist/grid/EnumPicker.js +53 -0
  135. package/dist/grid/GridHeaderCell.d.ts +36 -0
  136. package/dist/grid/GridHeaderCell.js +69 -0
  137. package/dist/grid/RecordView.d.ts +20 -0
  138. package/dist/grid/RecordView.js +126 -0
  139. package/dist/grid/ValueDock.d.ts +11 -0
  140. package/dist/grid/ValueDock.js +28 -0
  141. package/dist/grid/aggregates.d.ts +22 -0
  142. package/dist/grid/aggregates.js +62 -0
  143. package/dist/grid/cell-range.d.ts +14 -0
  144. package/dist/grid/cell-range.js +11 -0
  145. package/dist/grid/column-layout.d.ts +18 -0
  146. package/dist/grid/column-layout.js +49 -0
  147. package/dist/grid/column-selection.d.ts +11 -0
  148. package/dist/grid/column-selection.js +28 -0
  149. package/dist/grid/column-view.d.ts +10 -0
  150. package/dist/grid/column-view.js +23 -0
  151. package/dist/grid/copy-formats.d.ts +15 -0
  152. package/dist/grid/copy-formats.js +80 -0
  153. package/dist/grid/display.d.ts +20 -0
  154. package/dist/grid/display.js +81 -0
  155. package/dist/grid/edit-value.d.ts +5 -0
  156. package/dist/grid/edit-value.js +55 -0
  157. package/dist/grid/enum-type.d.ts +7 -0
  158. package/dist/grid/enum-type.js +52 -0
  159. package/dist/grid/focused-value.d.ts +4 -0
  160. package/dist/grid/focused-value.js +19 -0
  161. package/dist/grid/layout.d.ts +22 -0
  162. package/dist/grid/layout.js +74 -0
  163. package/dist/grid/navigation.d.ts +15 -0
  164. package/dist/grid/navigation.js +52 -0
  165. package/dist/grid/row-selection.d.ts +9 -0
  166. package/dist/grid/row-selection.js +24 -0
  167. package/dist/grid/sort.d.ts +7 -0
  168. package/dist/grid/sort.js +7 -0
  169. package/dist/grid/types.d.ts +30 -0
  170. package/dist/grid/types.js +9 -0
  171. package/dist/host/helper-path.d.ts +7 -0
  172. package/dist/host/helper-path.js +15 -0
  173. package/dist/host/host.d.ts +2 -0
  174. package/dist/host/host.js +343 -0
  175. package/dist/host/index.d.ts +5 -0
  176. package/dist/host/index.js +4 -0
  177. package/dist/host/spawn.d.ts +7 -0
  178. package/dist/host/spawn.js +98 -0
  179. package/dist/host/types.d.ts +19 -0
  180. package/dist/host/types.js +1 -0
  181. package/dist/host/validate.d.ts +10 -0
  182. package/dist/host/validate.js +339 -0
  183. package/dist/index.d.ts +14 -0
  184. package/dist/index.js +13 -0
  185. package/dist/locales/en.json +577 -0
  186. package/dist/locales/nl.json +577 -0
  187. package/dist/locales.d.ts +4 -0
  188. package/dist/locales.js +14 -0
  189. package/dist/protocol/connection.d.ts +56 -0
  190. package/dist/protocol/connection.js +1 -0
  191. package/dist/protocol/errors.d.ts +7 -0
  192. package/dist/protocol/errors.js +1 -0
  193. package/dist/protocol/index.d.ts +5 -0
  194. package/dist/protocol/index.js +3 -0
  195. package/dist/protocol/messages.d.ts +250 -0
  196. package/dist/protocol/messages.js +2 -0
  197. package/dist/protocol/schema.d.ts +53 -0
  198. package/dist/protocol/schema.js +1 -0
  199. package/dist/protocol/values.d.ts +21 -0
  200. package/dist/protocol/values.js +15 -0
  201. package/dist/sql-split.d.ts +15 -0
  202. package/dist/sql-split.js +201 -0
  203. package/dist/sql.d.ts +29 -0
  204. package/dist/sql.js +104 -0
  205. package/dist/structure/StructureView.d.ts +14 -0
  206. package/dist/structure/StructureView.js +99 -0
  207. package/dist/structure/structure-text.d.ts +2 -0
  208. package/dist/structure/structure-text.js +2 -0
  209. package/dist/table/CommandField.d.ts +14 -0
  210. package/dist/table/CommandField.js +157 -0
  211. package/dist/table/ImportDialog.d.ts +27 -0
  212. package/dist/table/ImportDialog.js +28 -0
  213. package/dist/table/TableStatusBar.d.ts +19 -0
  214. package/dist/table/TableStatusBar.js +22 -0
  215. package/dist/table/TableToolbar.d.ts +43 -0
  216. package/dist/table/TableToolbar.js +16 -0
  217. package/dist/table/TableView.d.ts +13 -0
  218. package/dist/table/TableView.js +508 -0
  219. package/dist/table/command-field.d.ts +57 -0
  220. package/dist/table/command-field.js +203 -0
  221. package/dist/table/grid-rows.d.ts +23 -0
  222. package/dist/table/grid-rows.js +55 -0
  223. package/dist/table/import-mapping.d.ts +7 -0
  224. package/dist/table/import-mapping.js +28 -0
  225. package/dist/table/layout-store.d.ts +13 -0
  226. package/dist/table/layout-store.js +43 -0
  227. package/dist/table/paging.d.ts +12 -0
  228. package/dist/table/paging.js +20 -0
  229. package/dist/table/pending.d.ts +32 -0
  230. package/dist/table/pending.js +94 -0
  231. package/dist/table/references.d.ts +12 -0
  232. package/dist/table/references.js +21 -0
  233. package/dist/table/useLoaded.d.ts +7 -0
  234. package/dist/table/useLoaded.js +36 -0
  235. package/dist/table/useStoredLayout.d.ts +8 -0
  236. package/dist/table/useStoredLayout.js +39 -0
  237. package/dist/table/useTableTransfer.d.ts +51 -0
  238. package/dist/table/useTableTransfer.js +125 -0
  239. package/dist/testing/fake.d.ts +21 -0
  240. package/dist/testing/fake.js +372 -0
  241. package/dist/testing/index.d.ts +1 -0
  242. package/dist/testing/index.js +1 -0
  243. package/dist/use-popup-press.d.ts +1 -0
  244. package/dist/use-popup-press.js +24 -0
  245. package/dist/use-schema-change.d.ts +1 -0
  246. package/dist/use-schema-change.js +13 -0
  247. package/dist/use-stable-callback.d.ts +1 -0
  248. package/dist/use-stable-callback.js +9 -0
  249. package/dist/value/ValuePanel.d.ts +14 -0
  250. package/dist/value/ValuePanel.js +103 -0
  251. package/dist/value/draft.d.ts +33 -0
  252. package/dist/value/draft.js +44 -0
  253. package/dist/value/hex.d.ts +14 -0
  254. package/dist/value/hex.js +46 -0
  255. package/dist/value/json.d.ts +9 -0
  256. package/dist/value/json.js +71 -0
  257. package/dist/value/views.d.ts +22 -0
  258. package/dist/value/views.js +54 -0
  259. package/dist/workbench/DatabaseWorkbench.d.ts +9 -0
  260. package/dist/workbench/DatabaseWorkbench.js +179 -0
  261. package/dist/workbench/DesignerTab.d.ts +10 -0
  262. package/dist/workbench/DesignerTab.js +22 -0
  263. package/dist/workbench/TableTab.d.ts +10 -0
  264. package/dist/workbench/TableTab.js +8 -0
  265. package/dist/workbench/tabs.d.ts +42 -0
  266. package/dist/workbench/tabs.js +139 -0
  267. package/package.json +79 -3
  268. package/src/DatabaseProvider.tsx +37 -0
  269. package/src/actions.ts +34 -0
  270. package/src/client/client.ts +324 -0
  271. package/src/client/index.ts +18 -0
  272. package/src/client/types.ts +161 -0
  273. package/src/client-context.ts +35 -0
  274. package/src/connections/ConnectionForm.tsx +474 -0
  275. package/src/connections/ConnectionManager.tsx +224 -0
  276. package/src/connections/EngineIcon.tsx +18 -0
  277. package/src/connections/connection-config.ts +143 -0
  278. package/src/connections/docker.ts +50 -0
  279. package/src/connections/engine-icons.ts +5 -0
  280. package/src/connections/use-docker-containers.ts +41 -0
  281. package/src/console/DestructiveDialog.tsx +40 -0
  282. package/src/console/HistoryPanel.tsx +67 -0
  283. package/src/console/QueryConsole.tsx +439 -0
  284. package/src/console/SchemaPicker.tsx +32 -0
  285. package/src/console/SqlEditor.tsx +95 -0
  286. package/src/console/StatementResultView.tsx +176 -0
  287. package/src/console/TransactionControls.tsx +53 -0
  288. package/src/console/destructive.ts +60 -0
  289. package/src/console/editing.ts +77 -0
  290. package/src/console/history.ts +62 -0
  291. package/src/console/labels.ts +7 -0
  292. package/src/console/outcome.ts +14 -0
  293. package/src/console/schemas.ts +7 -0
  294. package/src/console/shortcuts.ts +4 -0
  295. package/src/console/useConsoleHistory.ts +39 -0
  296. package/src/ddl/alter-mysql.ts +90 -0
  297. package/src/ddl/alter-sqlite.ts +93 -0
  298. package/src/ddl/alter.ts +29 -0
  299. package/src/ddl/column.ts +74 -0
  300. package/src/ddl/create.ts +92 -0
  301. package/src/ddl/dialect.ts +53 -0
  302. package/src/ddl/diff.ts +164 -0
  303. package/src/ddl/draft.ts +136 -0
  304. package/src/ddl/index.ts +10 -0
  305. package/src/ddl/parse.ts +161 -0
  306. package/src/ddl/statements.ts +20 -0
  307. package/src/ddl/types.ts +60 -0
  308. package/src/ddl/validate.ts +47 -0
  309. package/src/designer/ColumnsEditor.tsx +204 -0
  310. package/src/designer/DesignerView.tsx +184 -0
  311. package/src/designer/ForeignKeysEditor.tsx +132 -0
  312. package/src/designer/IndexesEditor.tsx +63 -0
  313. package/src/designer/NamePicker.tsx +44 -0
  314. package/src/designer/OptionsEditor.tsx +101 -0
  315. package/src/designer/SqlPreview.tsx +56 -0
  316. package/src/designer/TableDesigner.tsx +186 -0
  317. package/src/designer/edit.ts +137 -0
  318. package/src/designer/editor-props.ts +13 -0
  319. package/src/designer/plan.ts +25 -0
  320. package/src/designer/recover.ts +20 -0
  321. package/src/designer/useReferenceColumns.ts +54 -0
  322. package/src/explorer/DatabaseExplorer.tsx +450 -0
  323. package/src/explorer/RowMenu.tsx +134 -0
  324. package/src/explorer/TableDialog.tsx +80 -0
  325. package/src/explorer/expansion.ts +47 -0
  326. package/src/explorer/table-statements.ts +18 -0
  327. package/src/explorer/tree.ts +453 -0
  328. package/src/explorer/use-explorer-loads.ts +157 -0
  329. package/src/explorer/use-stored-expansion.ts +66 -0
  330. package/src/grid/CellEditor.tsx +53 -0
  331. package/src/grid/CopyAsMenu.tsx +33 -0
  332. package/src/grid/DataGrid.tsx +871 -0
  333. package/src/grid/EnumPicker.tsx +108 -0
  334. package/src/grid/GridHeaderCell.tsx +243 -0
  335. package/src/grid/RecordView.tsx +293 -0
  336. package/src/grid/ValueDock.tsx +52 -0
  337. package/src/grid/aggregates.ts +94 -0
  338. package/src/grid/cell-range.ts +26 -0
  339. package/src/grid/column-layout.ts +71 -0
  340. package/src/grid/column-selection.ts +42 -0
  341. package/src/grid/column-view.ts +35 -0
  342. package/src/grid/copy-formats.ts +108 -0
  343. package/src/grid/display.ts +109 -0
  344. package/src/grid/edit-value.ts +60 -0
  345. package/src/grid/enum-type.ts +62 -0
  346. package/src/grid/focused-value.ts +23 -0
  347. package/src/grid/layout.ts +91 -0
  348. package/src/grid/navigation.ts +74 -0
  349. package/src/grid/row-selection.ts +35 -0
  350. package/src/grid/sort.ts +12 -0
  351. package/src/grid/types.ts +59 -0
  352. package/src/host/helper-path.ts +41 -0
  353. package/src/host/host.ts +456 -0
  354. package/src/host/index.ts +5 -0
  355. package/src/host/spawn.ts +124 -0
  356. package/src/host/types.ts +46 -0
  357. package/src/host/validate.ts +402 -0
  358. package/src/index.ts +37 -0
  359. package/src/locales/en.json +577 -0
  360. package/src/locales/nl.json +577 -0
  361. package/src/locales.ts +18 -0
  362. package/src/protocol/connection.ts +86 -0
  363. package/src/protocol/errors.ts +38 -0
  364. package/src/protocol/index.ts +33 -0
  365. package/src/protocol/messages.ts +168 -0
  366. package/src/protocol/schema.ts +76 -0
  367. package/src/protocol/values.ts +52 -0
  368. package/src/sql-split.ts +220 -0
  369. package/src/sql.ts +152 -0
  370. package/src/structure/StructureView.tsx +269 -0
  371. package/src/structure/structure-text.ts +4 -0
  372. package/src/table/CommandField.tsx +318 -0
  373. package/src/table/ImportDialog.tsx +168 -0
  374. package/src/table/TableStatusBar.tsx +91 -0
  375. package/src/table/TableToolbar.tsx +217 -0
  376. package/src/table/TableView.tsx +880 -0
  377. package/src/table/command-field.ts +285 -0
  378. package/src/table/grid-rows.ts +80 -0
  379. package/src/table/import-mapping.ts +37 -0
  380. package/src/table/layout-store.ts +62 -0
  381. package/src/table/paging.ts +33 -0
  382. package/src/table/pending.ts +143 -0
  383. package/src/table/references.ts +38 -0
  384. package/src/table/useLoaded.ts +58 -0
  385. package/src/table/useStoredLayout.ts +53 -0
  386. package/src/table/useTableTransfer.ts +187 -0
  387. package/src/testing/fake.ts +533 -0
  388. package/src/testing/index.ts +1 -0
  389. package/src/use-popup-press.ts +27 -0
  390. package/src/use-schema-change.ts +19 -0
  391. package/src/use-stable-callback.ts +12 -0
  392. package/src/value/ValuePanel.tsx +254 -0
  393. package/src/value/draft.ts +72 -0
  394. package/src/value/hex.ts +62 -0
  395. package/src/value/json.ts +74 -0
  396. package/src/value/views.ts +68 -0
  397. package/src/workbench/DatabaseWorkbench.tsx +348 -0
  398. package/src/workbench/DesignerTab.tsx +37 -0
  399. package/src/workbench/TableTab.tsx +27 -0
  400. package/src/workbench/tabs.ts +184 -0
@@ -0,0 +1,161 @@
1
+ import type { TableStructure } from '../protocol/index.ts';
2
+
3
+ const CLOSERS: Readonly<Record<string, string>> = { "'": "'", '"': '"', '`': '`', '[': ']' };
4
+
5
+ interface Spot {
6
+ readonly index: number;
7
+ readonly char: string;
8
+ /* The number of parentheses open around it; a parenthesis counts for the level it sits in. */
9
+ readonly depth: number;
10
+ }
11
+
12
+ /* The characters of SQL that are not inside a quote, for a scan that has to ignore a comma in a default or a comment. */
13
+ function* unquoted(text: string, backslash: boolean): Generator<Spot> {
14
+ let depth = 0;
15
+ let closer: string | null = null;
16
+ for (let i = 0; i < text.length; i++) {
17
+ const char = text[i]!;
18
+ if (closer !== null) {
19
+ if (backslash && char === '\\' && closer === "'") {
20
+ i++;
21
+ } else if (char === closer) {
22
+ if (closer !== ']' && text[i + 1] === closer) {
23
+ i++;
24
+ } else {
25
+ closer = null;
26
+ }
27
+ }
28
+ continue;
29
+ }
30
+ if (char in CLOSERS) {
31
+ closer = CLOSERS[char]!;
32
+ continue;
33
+ }
34
+ if (char === ')') {
35
+ depth--;
36
+ }
37
+ yield { index: i, char, depth };
38
+ if (char === '(') {
39
+ depth++;
40
+ }
41
+ }
42
+ }
43
+
44
+ export interface Definition {
45
+ /* The column and constraint definitions, each as written. */
46
+ readonly items: readonly string[];
47
+ /* What follows the closing parenthesis: the table options. */
48
+ readonly tail: string;
49
+ }
50
+
51
+ /* Splits a `CREATE TABLE` into its definitions and its table options, or `null` when it holds no parenthesis. */
52
+ export const definitionOf = (ddl: string | null): Definition | null => {
53
+ if (ddl === null) {
54
+ return null;
55
+ }
56
+ // Only a MySQL statement reads a backslash as an escape, and only a MySQL statement quotes with backticks.
57
+ const backslash = ddl.includes('`');
58
+ let open = -1;
59
+ let close = -1;
60
+ const commas: number[] = [];
61
+ for (const { index, char, depth } of unquoted(ddl, backslash)) {
62
+ if (open < 0) {
63
+ if (char === '(' && depth === 0) {
64
+ open = index;
65
+ }
66
+ } else if (close < 0) {
67
+ if (char === ')' && depth === 0) {
68
+ close = index;
69
+ } else if (char === ',' && depth === 1) {
70
+ commas.push(index);
71
+ }
72
+ }
73
+ }
74
+ if (open < 0 || close < 0) {
75
+ return null;
76
+ }
77
+ const bounds = [open, ...commas, close];
78
+ return {
79
+ items: bounds.slice(1).map((end, at) => ddl.slice(bounds[at]! + 1, end).trim()),
80
+ tail: ddl.slice(close + 1)
81
+ };
82
+ };
83
+
84
+ /* The name a definition starts with, unquoted. */
85
+ const leadingName = (item: string): string | null => {
86
+ const mark = item[0];
87
+ if (mark === '`' || mark === '"') {
88
+ const end = item.indexOf(mark, 1);
89
+ return end < 0 ? null : item.slice(1, end).replaceAll(mark + mark, mark);
90
+ }
91
+ if (mark === '[') {
92
+ const end = item.indexOf(']');
93
+ return end < 0 ? null : item.slice(1, end);
94
+ }
95
+ return /^[^\s(]+/.exec(item)?.[0] ?? null;
96
+ };
97
+
98
+ const AS_OPEN = /\b(?:GENERATED\s+ALWAYS\s+)?AS\s*\(/i;
99
+ const AS_SUFFIX = /^\s*(?:VIRTUAL|STORED|PERSISTENT)\b/i;
100
+
101
+ /* The `GENERATED ALWAYS AS (...) STORED` clause of a column definition, or `null` when it has none. */
102
+ const generatedClauseOf = (item: string): string | null => {
103
+ // A default or a comment may hold the words, so the strings are blanked before the search.
104
+ const masked = item.replace(/'(?:[^']|'')*'/g, (text) => ' '.repeat(text.length));
105
+ const start = AS_OPEN.exec(masked);
106
+ if (start === null) {
107
+ return null;
108
+ }
109
+ const open = start.index + start[0].length - 1;
110
+ let end = -1;
111
+ for (const { index, char, depth } of unquoted(masked.slice(open), false)) {
112
+ if (char === ')' && depth === 0) {
113
+ end = open + index + 1;
114
+ break;
115
+ }
116
+ }
117
+ if (end < 0) {
118
+ return null;
119
+ }
120
+ const suffix = AS_SUFFIX.exec(masked.slice(end));
121
+ return item.slice(start.index, end + (suffix?.[0].length ?? 0)).trim();
122
+ };
123
+
124
+ /* The generated clause of every generated column in a table's DDL, by column name. */
125
+ export const generatedClausesOf = (ddl: string | null): ReadonlyMap<string, string> => {
126
+ const clauses = new Map<string, string>();
127
+ for (const item of definitionOf(ddl)?.items ?? []) {
128
+ const name = leadingName(item);
129
+ const clause = name === null ? null : generatedClauseOf(item);
130
+ if (name !== null && clause !== null) {
131
+ clauses.set(name, clause);
132
+ }
133
+ }
134
+ return clauses;
135
+ };
136
+
137
+ /* What the options of a table say, in the terms of both engines; the ones an engine has no word for stay empty. */
138
+ export interface TableOptions {
139
+ readonly engine: string;
140
+ readonly charset: string;
141
+ readonly collation: string;
142
+ readonly comment: string;
143
+ readonly withoutRowid: boolean;
144
+ readonly strict: boolean;
145
+ }
146
+
147
+ export const NO_OPTIONS: TableOptions = { engine: '', charset: '', collation: '', comment: '', withoutRowid: false, strict: false };
148
+
149
+ export const optionsOf = (structure: Pick<TableStructure, 'ddl'>): TableOptions => {
150
+ const tail = definitionOf(structure.ddl)?.tail ?? '';
151
+ const word = (pattern: RegExp): string => pattern.exec(tail)?.[1] ?? '';
152
+ const comment = /\bCOMMENT\s*=?\s*'((?:[^'\\]|''|\\.)*)'/i.exec(tail)?.[1] ?? '';
153
+ return {
154
+ engine: word(/\bENGINE\s*=\s*(\w+)/i),
155
+ charset: word(/\b(?:DEFAULT\s+)?(?:CHARSET|CHARACTER\s+SET)\s*=?\s*(\w+)/i),
156
+ collation: word(/\bCOLLATE\s*=?\s*(\w+)/i),
157
+ comment: comment.replaceAll("''", "'").replace(/\\(.)/g, '$1'),
158
+ withoutRowid: /\bWITHOUT\s+ROWID\b/i.test(tail),
159
+ strict: /\bSTRICT\b/i.test(tail)
160
+ };
161
+ };
@@ -0,0 +1,20 @@
1
+ import { quote, sqliteSchemaPrefix, tableName, type Dialect } from './dialect.ts';
2
+
3
+ export const dropTableSql = (dialect: Dialect, schema: string, table: string): string => `DROP TABLE ${tableName(dialect, schema, table)}`;
4
+
5
+ export const dropViewSql = (dialect: Dialect, schema: string, view: string): string => `DROP VIEW ${tableName(dialect, schema, view)}`;
6
+
7
+ /* SQLite has no `TRUNCATE`; deleting every row is what it does instead. */
8
+ export const truncateTableSql = (dialect: Dialect, schema: string, table: string): string =>
9
+ dialect.engine === 'sqlite' ? `DELETE FROM ${tableName(dialect, schema, table)}` : `TRUNCATE TABLE ${tableName(dialect, schema, table)}`;
10
+
11
+ export const renameTableSql = (dialect: Dialect, schema: string, from: string, to: string): string =>
12
+ dialect.engine === 'sqlite'
13
+ ? `ALTER TABLE ${tableName(dialect, schema, from)} RENAME TO ${quote(dialect, to)}`
14
+ : `RENAME TABLE ${tableName(dialect, schema, from)} TO ${tableName(dialect, schema, to)}`;
15
+
16
+ /* MySQL drops an index from the table it is on; SQLite names it alone, in its schema. */
17
+ export const dropIndexSql = (dialect: Dialect, schema: string, table: string, index: string): string =>
18
+ dialect.engine === 'sqlite'
19
+ ? `DROP INDEX ${sqliteSchemaPrefix(dialect, schema)}${quote(dialect, index)}`
20
+ : `DROP INDEX ${quote(dialect, index)} ON ${tableName(dialect, schema, table)}`;
@@ -0,0 +1,60 @@
1
+ import type { Dialect } from './dialect.ts';
2
+
3
+ const SQLITE_TYPES = ['INTEGER', 'TEXT', 'REAL', 'BLOB', 'NUMERIC'] as const;
4
+
5
+ const MYSQL_TYPES = [
6
+ 'tinyint',
7
+ 'smallint',
8
+ 'mediumint',
9
+ 'int',
10
+ 'bigint',
11
+ 'bigint unsigned',
12
+ 'decimal(10,2)',
13
+ 'float',
14
+ 'double',
15
+ 'bit(1)',
16
+ 'char(36)',
17
+ 'varchar(255)',
18
+ 'varchar(64)',
19
+ 'tinytext',
20
+ 'text',
21
+ 'mediumtext',
22
+ 'longtext',
23
+ 'binary(16)',
24
+ 'varbinary(255)',
25
+ 'tinyblob',
26
+ 'blob',
27
+ 'mediumblob',
28
+ 'longblob',
29
+ 'date',
30
+ 'time',
31
+ 'datetime',
32
+ 'datetime(6)',
33
+ 'timestamp',
34
+ 'timestamp(6)',
35
+ 'year',
36
+ 'json',
37
+ "enum('a','b')",
38
+ "set('a','b')"
39
+ ] as const;
40
+
41
+ /* The types a type field offers; any other text is still allowed, since both engines accept more than a list holds. */
42
+ export const typeSuggestionsOf = (dialect: Dialect): readonly string[] => (dialect.engine === 'sqlite' ? SQLITE_TYPES : MYSQL_TYPES);
43
+
44
+ export const MYSQL_ENGINES = ['InnoDB', 'MyISAM', 'MEMORY', 'ARCHIVE', 'CSV'] as const;
45
+
46
+ export const MYSQL_CHARSETS = ['utf8mb4', 'utf8mb3', 'latin1', 'ascii', 'binary'] as const;
47
+
48
+ /* Collations to offer for a character set, which is the family it is named after. */
49
+ export const collationSuggestionsOf = (charset: string): readonly string[] => {
50
+ if (charset === '') {
51
+ return [];
52
+ }
53
+ if (charset === 'binary') {
54
+ return ['binary'];
55
+ }
56
+ const suggestions = [`${charset}_general_ci`, `${charset}_unicode_ci`, `${charset}_bin`];
57
+ return charset === 'utf8mb4' ? ['utf8mb4_0900_ai_ci', 'utf8mb4_unicode_520_ci', ...suggestions] : suggestions;
58
+ };
59
+
60
+ export const FOREIGN_KEY_ACTIONS = ['CASCADE', 'SET NULL', 'RESTRICT', 'NO ACTION', 'SET DEFAULT'] as const;
@@ -0,0 +1,47 @@
1
+ import type { Dialect } from './dialect.ts';
2
+ import type { TableDraft } from './draft.ts';
3
+
4
+ /* What stops a draft from becoming SQL. `subject` names the item it is about, empty for the table itself. */
5
+ export interface DraftProblem {
6
+ readonly code: 'tableName' | 'noColumns' | 'columnName' | 'columnDuplicate' | 'columnType' | 'indexName' | 'indexColumns' | 'foreignKey';
7
+ readonly subject: string;
8
+ }
9
+
10
+ export const validateDraft = (dialect: Dialect, draft: TableDraft): DraftProblem[] => {
11
+ const problems: DraftProblem[] = [];
12
+ if (draft.name.trim() === '') {
13
+ problems.push({ code: 'tableName', subject: '' });
14
+ }
15
+ if (draft.columns.length === 0) {
16
+ problems.push({ code: 'noColumns', subject: '' });
17
+ }
18
+ const seen = new Set<string>();
19
+ for (const column of draft.columns) {
20
+ const folded = column.name.toLowerCase();
21
+ if (column.name.trim() === '') {
22
+ problems.push({ code: 'columnName', subject: '' });
23
+ } else if (seen.has(folded)) {
24
+ problems.push({ code: 'columnDuplicate', subject: column.name });
25
+ }
26
+ seen.add(folded);
27
+ if (dialect.engine === 'mysql' && column.type.trim() === '' && column.name.trim() !== '') {
28
+ problems.push({ code: 'columnType', subject: column.name });
29
+ }
30
+ }
31
+ for (const index of draft.indexes) {
32
+ if (index.columns.length === 0) {
33
+ problems.push({ code: 'indexColumns', subject: index.name });
34
+ }
35
+ if (dialect.engine === 'sqlite' && index.name.trim() === '') {
36
+ problems.push({ code: 'indexName', subject: '' });
37
+ }
38
+ }
39
+ for (const foreignKey of draft.foreignKeys) {
40
+ const complete =
41
+ foreignKey.columns.length > 0 && foreignKey.referencedTable !== '' && foreignKey.referencedColumns.length === foreignKey.columns.length;
42
+ if (!complete) {
43
+ problems.push({ code: 'foreignKey', subject: foreignKey.name });
44
+ }
45
+ }
46
+ return problems;
47
+ };
@@ -0,0 +1,204 @@
1
+ import { useId } from 'react';
2
+ import clsx from 'clsx';
3
+ import { ArrowDown, ArrowUp, KeyRound, Plus, Trash2 } from 'lucide-react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Button, Checkbox, EmptyState, Icon, IconButton, Input, Pill } from '@adecore/ui';
6
+ import { formatNumber } from '@adecore/ui/format';
7
+ import { typeSuggestionsOf } from '../ddl/index.ts';
8
+ import type { EditorProps } from './editor-props.ts';
9
+ import { addColumn, moveColumn, patchColumn, removeColumn, togglePrimaryKey } from './edit.ts';
10
+
11
+ const TH = 'sticky top-0 z-10 h-7 bg-surface px-2 text-left text-xs font-medium whitespace-nowrap text-text-faint';
12
+ const TD = 'border-t border-border-soft px-2 py-1 align-middle';
13
+
14
+ /* The columns of the table as rows to edit: name, type, null, default, key, comment and where each one sits. */
15
+ export function ColumnsEditor({ draft, dialect, disabled, onChange, className, ref }: EditorProps) {
16
+ const { t } = useTranslation('database');
17
+ const listId = useId();
18
+ const mysql = dialect.engine === 'mysql';
19
+ const defaultType = typeSuggestionsOf(dialect)[0]!;
20
+ return (
21
+ <div ref={ref} className={clsx('flex min-h-0 flex-col', className)}>
22
+ {draft.columns.length === 0 ? (
23
+ <EmptyState>{t('designer.columns.empty')}</EmptyState>
24
+ ) : (
25
+ <div>
26
+ <datalist id={listId}>
27
+ {typeSuggestionsOf(dialect).map((type) => (
28
+ <option key={type} value={type} />
29
+ ))}
30
+ </datalist>
31
+ <table className="w-full border-collapse">
32
+ <thead>
33
+ <tr>
34
+ <th scope="col" className={TH}>
35
+ <span className="sr-only">{t('designer.columns.order')}</span>
36
+ </th>
37
+ <th scope="col" className={TH}>
38
+ {t('designer.columns.name')}
39
+ </th>
40
+ <th scope="col" className={TH}>
41
+ {t('designer.columns.type')}
42
+ </th>
43
+ <th scope="col" className={clsx(TH, 'text-center')}>
44
+ {t('designer.columns.notNull')}
45
+ </th>
46
+ <th scope="col" className={TH}>
47
+ {t('designer.columns.default')}
48
+ </th>
49
+ {mysql && (
50
+ <th scope="col" className={clsx(TH, 'text-center')}>
51
+ {t('designer.columns.autoIncrement')}
52
+ </th>
53
+ )}
54
+ <th scope="col" className={TH}>
55
+ {t('designer.columns.primaryKey')}
56
+ </th>
57
+ {mysql && (
58
+ <th scope="col" className={TH}>
59
+ {t('designer.columns.comment')}
60
+ </th>
61
+ )}
62
+ <th scope="col" className={TH}>
63
+ <span className="sr-only">{t('designer.columns.remove')}</span>
64
+ </th>
65
+ </tr>
66
+ </thead>
67
+ <tbody>
68
+ {draft.columns.map((column, at) => {
69
+ const keyAt = draft.primaryKey.indexOf(column.name);
70
+ const locked = disabled || column.generated;
71
+ const patch = (change: Parameters<typeof patchColumn>[2]): void => onChange(patchColumn(draft, column.key, change));
72
+ return (
73
+ <tr key={column.key}>
74
+ <td className={clsx(TD, 'whitespace-nowrap')}>
75
+ <span className="flex">
76
+ <IconButton
77
+ icon={ArrowUp}
78
+ size="xs"
79
+ label={t('designer.columns.moveUp')}
80
+ disabled={disabled || at === 0}
81
+ onClick={() => onChange(moveColumn(draft, column.key, -1))}
82
+ />
83
+ <IconButton
84
+ icon={ArrowDown}
85
+ size="xs"
86
+ label={t('designer.columns.moveDown')}
87
+ disabled={disabled || at === draft.columns.length - 1}
88
+ onClick={() => onChange(moveColumn(draft, column.key, 1))}
89
+ />
90
+ </span>
91
+ </td>
92
+ <td className={clsx(TD, 'min-w-40')}>
93
+ <Input
94
+ size="sm"
95
+ className="w-full"
96
+ aria-label={t('designer.columns.name')}
97
+ spellCheck={false}
98
+ value={column.name}
99
+ disabled={locked}
100
+ onChange={(event) => patch({ name: event.target.value })}
101
+ />
102
+ </td>
103
+ <td className={clsx(TD, 'min-w-36')}>
104
+ <Input
105
+ size="sm"
106
+ mono
107
+ className="w-full"
108
+ aria-label={t('designer.columns.type')}
109
+ list={listId}
110
+ spellCheck={false}
111
+ value={column.type}
112
+ disabled={locked}
113
+ onChange={(event) => patch({ type: event.target.value })}
114
+ />
115
+ </td>
116
+ <td className={TD}>
117
+ <Checkbox
118
+ className="mx-auto"
119
+ label={t('designer.columns.notNullFor', { name: column.name })}
120
+ checked={!column.nullable}
121
+ disabled={locked}
122
+ onCheckedChange={(checked) => patch({ nullable: !checked })}
123
+ />
124
+ </td>
125
+ <td className={clsx(TD, 'min-w-36')}>
126
+ {column.generated ? (
127
+ <Pill mono>{column.generatedClause ?? t('designer.columns.generated')}</Pill>
128
+ ) : (
129
+ <Input
130
+ size="sm"
131
+ mono
132
+ className="w-full"
133
+ aria-label={t('designer.columns.default')}
134
+ placeholder={t('designer.columns.noDefault')}
135
+ spellCheck={false}
136
+ value={column.defaultValue ?? ''}
137
+ disabled={disabled}
138
+ onChange={(event) => patch({ defaultValue: event.target.value === '' ? null : event.target.value })}
139
+ />
140
+ )}
141
+ </td>
142
+ {mysql && (
143
+ <td className={TD}>
144
+ <Checkbox
145
+ className="mx-auto"
146
+ label={t('designer.columns.autoIncrementFor', { name: column.name })}
147
+ checked={column.autoIncrement}
148
+ disabled={locked}
149
+ onCheckedChange={(checked) => patch({ autoIncrement: checked })}
150
+ />
151
+ </td>
152
+ )}
153
+ <td className={clsx(TD, 'whitespace-nowrap')}>
154
+ <span className="flex items-center gap-1">
155
+ <IconButton
156
+ icon={KeyRound}
157
+ size="xs"
158
+ label={keyAt < 0 ? t('designer.columns.addToKey') : t('designer.columns.removeFromKey')}
159
+ aria-pressed={keyAt >= 0}
160
+ disabled={locked}
161
+ onClick={() => onChange(togglePrimaryKey(draft, column.key))}
162
+ />
163
+ {keyAt >= 0 && draft.primaryKey.length > 1 && (
164
+ <span className="text-xs text-text-muted">{formatNumber(keyAt + 1)}</span>
165
+ )}
166
+ </span>
167
+ </td>
168
+ {mysql && (
169
+ <td className={clsx(TD, 'min-w-40')}>
170
+ <Input
171
+ size="sm"
172
+ className="w-full"
173
+ aria-label={t('designer.columns.comment')}
174
+ value={column.comment}
175
+ disabled={locked}
176
+ onChange={(event) => patch({ comment: event.target.value })}
177
+ />
178
+ </td>
179
+ )}
180
+ <td className={TD}>
181
+ <IconButton
182
+ icon={Trash2}
183
+ size="xs"
184
+ label={t('designer.columns.remove')}
185
+ disabled={disabled}
186
+ onClick={() => onChange(removeColumn(draft, column.key))}
187
+ />
188
+ </td>
189
+ </tr>
190
+ );
191
+ })}
192
+ </tbody>
193
+ </table>
194
+ </div>
195
+ )}
196
+ <div className="shrink-0 border-t border-border-soft px-3 py-2">
197
+ <Button variant="secondary" size="sm" disabled={disabled} onClick={() => onChange(addColumn(draft, defaultType))}>
198
+ <Icon icon={Plus} size={12} />
199
+ {t('designer.columns.add')}
200
+ </Button>
201
+ </div>
202
+ </div>
203
+ );
204
+ }