velocious 1.0.567 → 1.0.569

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 (247) hide show
  1. package/README.md +49 -1
  2. package/build/configuration.js +36 -0
  3. package/build/database/drivers/base-table.js +4 -3
  4. package/build/database/drivers/base.js +195 -54
  5. package/build/database/drivers/index-metadata.js +28 -0
  6. package/build/database/drivers/mssql/column.js +2 -34
  7. package/build/database/drivers/mssql/columns-index.js +37 -0
  8. package/build/database/drivers/mssql/index.js +10 -7
  9. package/build/database/drivers/mssql/table.js +55 -11
  10. package/build/database/drivers/mysql/index.js +10 -5
  11. package/build/database/drivers/mysql/sql/alter-table.js +13 -0
  12. package/build/database/drivers/pgsql/column.js +2 -27
  13. package/build/database/drivers/pgsql/columns-index.js +37 -0
  14. package/build/database/drivers/pgsql/index.js +4 -4
  15. package/build/database/drivers/pgsql/table.js +61 -10
  16. package/build/database/drivers/sqlite/base.js +2 -2
  17. package/build/database/drivers/sqlite/connection-sql-js.js +62 -2
  18. package/build/database/drivers/sqlite/index.web.js +74 -0
  19. package/build/database/drivers/sqlite/sql/alter-table.js +18 -0
  20. package/build/database/migration/index.js +92 -3
  21. package/build/database/operation-connection.js +110 -0
  22. package/build/database/operation-lease.js +47 -0
  23. package/build/database/operation.js +140 -0
  24. package/build/database/pool/base.js +26 -1
  25. package/build/database/pool/single-multi-use.js +49 -0
  26. package/build/database/query/alter-table-base.js +20 -1
  27. package/build/database/query/model-class-query.js +38 -7
  28. package/build/database/query/preloader/belongs-to.js +3 -2
  29. package/build/database/query/preloader/has-many.js +4 -3
  30. package/build/database/query/preloader/has-one.js +2 -1
  31. package/build/database/query/preloader/query-for-model.js +17 -0
  32. package/build/database/query/query-data.js +12 -3
  33. package/build/database/query/with-count.js +5 -3
  34. package/build/database/record/acts-as-list.js +11 -8
  35. package/build/database/record/attachments/normalize-input.js +38 -10
  36. package/build/database/record/attachments/storage-drivers/filesystem.js +33 -2
  37. package/build/database/record/attachments/storage-drivers/native.js +17 -2
  38. package/build/database/record/attachments/storage-drivers/s3.js +27 -7
  39. package/build/database/record/attachments/store.js +173 -53
  40. package/build/database/record/auditing.js +12 -9
  41. package/build/database/record/counter-cache-magnitude.js +5 -3
  42. package/build/database/record/index.js +95 -14
  43. package/build/database/record/instance-relationships/belongs-to.js +4 -2
  44. package/build/database/record/instance-relationships/has-many.js +7 -5
  45. package/build/database/record/instance-relationships/has-one.js +4 -2
  46. package/build/database/record/validators/uniqueness.js +3 -2
  47. package/build/database/table-data/table-foreign-key.js +9 -1
  48. package/build/environment-handlers/base.js +1 -1
  49. package/build/environment-handlers/node/attachment-path-source.js +144 -0
  50. package/build/environment-handlers/node.js +28 -3
  51. package/build/frontend-models/websocket-publishers.js +2 -2
  52. package/build/src/configuration.d.ts +12 -0
  53. package/build/src/configuration.d.ts.map +1 -1
  54. package/build/src/configuration.js +35 -1
  55. package/build/src/database/drivers/base-table.d.ts.map +1 -1
  56. package/build/src/database/drivers/base-table.js +4 -3
  57. package/build/src/database/drivers/base.d.ts +73 -20
  58. package/build/src/database/drivers/base.d.ts.map +1 -1
  59. package/build/src/database/drivers/base.js +181 -54
  60. package/build/src/database/drivers/index-metadata.d.ts +41 -0
  61. package/build/src/database/drivers/index-metadata.d.ts.map +1 -0
  62. package/build/src/database/drivers/index-metadata.js +26 -0
  63. package/build/src/database/drivers/mssql/column.d.ts.map +1 -1
  64. package/build/src/database/drivers/mssql/column.js +3 -31
  65. package/build/src/database/drivers/mssql/columns-index.d.ts +41 -0
  66. package/build/src/database/drivers/mssql/columns-index.d.ts.map +1 -1
  67. package/build/src/database/drivers/mssql/columns-index.js +35 -1
  68. package/build/src/database/drivers/mssql/index.d.ts +4 -1
  69. package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
  70. package/build/src/database/drivers/mssql/index.js +11 -8
  71. package/build/src/database/drivers/mssql/table.d.ts +42 -0
  72. package/build/src/database/drivers/mssql/table.d.ts.map +1 -1
  73. package/build/src/database/drivers/mssql/table.js +49 -12
  74. package/build/src/database/drivers/mysql/index.d.ts +2 -1
  75. package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
  76. package/build/src/database/drivers/mysql/index.js +10 -6
  77. package/build/src/database/drivers/mysql/sql/alter-table.d.ts.map +1 -1
  78. package/build/src/database/drivers/mysql/sql/alter-table.js +12 -1
  79. package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
  80. package/build/src/database/drivers/pgsql/column.js +3 -24
  81. package/build/src/database/drivers/pgsql/columns-index.d.ts +41 -0
  82. package/build/src/database/drivers/pgsql/columns-index.d.ts.map +1 -1
  83. package/build/src/database/drivers/pgsql/columns-index.js +35 -1
  84. package/build/src/database/drivers/pgsql/index.d.ts +2 -1
  85. package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
  86. package/build/src/database/drivers/pgsql/index.js +5 -5
  87. package/build/src/database/drivers/pgsql/table.d.ts +42 -0
  88. package/build/src/database/drivers/pgsql/table.d.ts.map +1 -1
  89. package/build/src/database/drivers/pgsql/table.js +54 -11
  90. package/build/src/database/drivers/sqlite/base.d.ts +1 -1
  91. package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
  92. package/build/src/database/drivers/sqlite/base.js +3 -3
  93. package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +20 -0
  94. package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
  95. package/build/src/database/drivers/sqlite/connection-sql-js.js +56 -3
  96. package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
  97. package/build/src/database/drivers/sqlite/index.web.js +64 -1
  98. package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
  99. package/build/src/database/drivers/sqlite/sql/alter-table.js +15 -1
  100. package/build/src/database/migration/index.d.ts +35 -1
  101. package/build/src/database/migration/index.d.ts.map +1 -1
  102. package/build/src/database/migration/index.js +78 -4
  103. package/build/src/database/operation-connection.d.ts +61 -0
  104. package/build/src/database/operation-connection.d.ts.map +1 -0
  105. package/build/src/database/operation-connection.js +93 -0
  106. package/build/src/database/operation-lease.d.ts +27 -0
  107. package/build/src/database/operation-lease.d.ts.map +1 -0
  108. package/build/src/database/operation-lease.js +44 -0
  109. package/build/src/database/operation.d.ts +77 -0
  110. package/build/src/database/operation.d.ts.map +1 -0
  111. package/build/src/database/operation.js +123 -0
  112. package/build/src/database/pool/base.d.ts +14 -0
  113. package/build/src/database/pool/base.d.ts.map +1 -1
  114. package/build/src/database/pool/base.js +24 -2
  115. package/build/src/database/pool/single-multi-use.d.ts +1 -0
  116. package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
  117. package/build/src/database/pool/single-multi-use.js +49 -1
  118. package/build/src/database/query/alter-table-base.d.ts +6 -0
  119. package/build/src/database/query/alter-table-base.d.ts.map +1 -1
  120. package/build/src/database/query/alter-table-base.js +18 -2
  121. package/build/src/database/query/model-class-query.d.ts +14 -0
  122. package/build/src/database/query/model-class-query.d.ts.map +1 -1
  123. package/build/src/database/query/model-class-query.js +32 -8
  124. package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
  125. package/build/src/database/query/preloader/belongs-to.js +4 -3
  126. package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
  127. package/build/src/database/query/preloader/has-many.js +5 -4
  128. package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
  129. package/build/src/database/query/preloader/has-one.js +3 -2
  130. package/build/src/database/query/preloader/query-for-model.d.ts +10 -0
  131. package/build/src/database/query/preloader/query-for-model.d.ts.map +1 -0
  132. package/build/src/database/query/preloader/query-for-model.js +16 -0
  133. package/build/src/database/query/query-data.d.ts.map +1 -1
  134. package/build/src/database/query/query-data.js +13 -4
  135. package/build/src/database/query/with-count.d.ts.map +1 -1
  136. package/build/src/database/query/with-count.js +6 -4
  137. package/build/src/database/record/acts-as-list.js +12 -9
  138. package/build/src/database/record/attachments/attachment-record.d.ts +9 -0
  139. package/build/src/database/record/attachments/attachment-record.d.ts.map +1 -1
  140. package/build/src/database/record/attachments/normalize-input.d.ts +33 -4
  141. package/build/src/database/record/attachments/normalize-input.d.ts.map +1 -1
  142. package/build/src/database/record/attachments/normalize-input.js +39 -11
  143. package/build/src/database/record/attachments/storage-drivers/filesystem.d.ts +2 -5
  144. package/build/src/database/record/attachments/storage-drivers/filesystem.d.ts.map +1 -1
  145. package/build/src/database/record/attachments/storage-drivers/filesystem.js +28 -3
  146. package/build/src/database/record/attachments/storage-drivers/native.d.ts +2 -6
  147. package/build/src/database/record/attachments/storage-drivers/native.d.ts.map +1 -1
  148. package/build/src/database/record/attachments/storage-drivers/native.js +14 -3
  149. package/build/src/database/record/attachments/storage-drivers/s3.d.ts +2 -6
  150. package/build/src/database/record/attachments/storage-drivers/s3.d.ts.map +1 -1
  151. package/build/src/database/record/attachments/storage-drivers/s3.js +29 -8
  152. package/build/src/database/record/attachments/store.d.ts +31 -2
  153. package/build/src/database/record/attachments/store.d.ts.map +1 -1
  154. package/build/src/database/record/attachments/store.js +158 -54
  155. package/build/src/database/record/auditing.d.ts.map +1 -1
  156. package/build/src/database/record/auditing.js +13 -10
  157. package/build/src/database/record/counter-cache-magnitude.js +6 -4
  158. package/build/src/database/record/index.d.ts +42 -2
  159. package/build/src/database/record/index.d.ts.map +1 -1
  160. package/build/src/database/record/index.js +87 -15
  161. package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
  162. package/build/src/database/record/instance-relationships/belongs-to.js +4 -3
  163. package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
  164. package/build/src/database/record/instance-relationships/has-many.js +7 -5
  165. package/build/src/database/record/instance-relationships/has-one.d.ts.map +1 -1
  166. package/build/src/database/record/instance-relationships/has-one.js +4 -3
  167. package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
  168. package/build/src/database/record/validators/uniqueness.js +4 -3
  169. package/build/src/database/table-data/table-foreign-key.d.ts +9 -1
  170. package/build/src/database/table-data/table-foreign-key.d.ts.map +1 -1
  171. package/build/src/database/table-data/table-foreign-key.js +9 -2
  172. package/build/src/environment-handlers/base.d.ts +2 -5
  173. package/build/src/environment-handlers/base.d.ts.map +1 -1
  174. package/build/src/environment-handlers/base.js +2 -2
  175. package/build/src/environment-handlers/node/attachment-path-source.d.ts +64 -0
  176. package/build/src/environment-handlers/node/attachment-path-source.d.ts.map +1 -0
  177. package/build/src/environment-handlers/node/attachment-path-source.js +116 -0
  178. package/build/src/environment-handlers/node.d.ts +12 -0
  179. package/build/src/environment-handlers/node.d.ts.map +1 -1
  180. package/build/src/environment-handlers/node.js +24 -4
  181. package/build/src/frontend-models/websocket-publishers.js +3 -3
  182. package/build/src/sync/server-sequence-allocator.d.ts +16 -4
  183. package/build/src/sync/server-sequence-allocator.d.ts.map +1 -1
  184. package/build/src/sync/server-sequence-allocator.js +37 -11
  185. package/build/src/sync/sync-client.d.ts.map +1 -1
  186. package/build/src/sync/sync-client.js +7 -3
  187. package/build/src/sync/sync-publisher.d.ts +2 -1
  188. package/build/src/sync/sync-publisher.d.ts.map +1 -1
  189. package/build/src/sync/sync-publisher.js +11 -6
  190. package/build/sync/server-sequence-allocator.js +43 -10
  191. package/build/sync/sync-client.js +6 -2
  192. package/build/sync/sync-publisher.js +10 -5
  193. package/build/tsconfig.tsbuildinfo +1 -1
  194. package/package.json +1 -1
  195. package/src/configuration.js +36 -0
  196. package/src/database/drivers/base-table.js +4 -3
  197. package/src/database/drivers/base.js +195 -54
  198. package/src/database/drivers/index-metadata.js +28 -0
  199. package/src/database/drivers/mssql/column.js +2 -34
  200. package/src/database/drivers/mssql/columns-index.js +37 -0
  201. package/src/database/drivers/mssql/index.js +10 -7
  202. package/src/database/drivers/mssql/table.js +55 -11
  203. package/src/database/drivers/mysql/index.js +10 -5
  204. package/src/database/drivers/mysql/sql/alter-table.js +13 -0
  205. package/src/database/drivers/pgsql/column.js +2 -27
  206. package/src/database/drivers/pgsql/columns-index.js +37 -0
  207. package/src/database/drivers/pgsql/index.js +4 -4
  208. package/src/database/drivers/pgsql/table.js +61 -10
  209. package/src/database/drivers/sqlite/base.js +2 -2
  210. package/src/database/drivers/sqlite/connection-sql-js.js +62 -2
  211. package/src/database/drivers/sqlite/index.web.js +74 -0
  212. package/src/database/drivers/sqlite/sql/alter-table.js +18 -0
  213. package/src/database/migration/index.js +92 -3
  214. package/src/database/operation-connection.js +110 -0
  215. package/src/database/operation-lease.js +47 -0
  216. package/src/database/operation.js +140 -0
  217. package/src/database/pool/base.js +26 -1
  218. package/src/database/pool/single-multi-use.js +49 -0
  219. package/src/database/query/alter-table-base.js +20 -1
  220. package/src/database/query/model-class-query.js +38 -7
  221. package/src/database/query/preloader/belongs-to.js +3 -2
  222. package/src/database/query/preloader/has-many.js +4 -3
  223. package/src/database/query/preloader/has-one.js +2 -1
  224. package/src/database/query/preloader/query-for-model.js +17 -0
  225. package/src/database/query/query-data.js +12 -3
  226. package/src/database/query/with-count.js +5 -3
  227. package/src/database/record/acts-as-list.js +11 -8
  228. package/src/database/record/attachments/normalize-input.js +38 -10
  229. package/src/database/record/attachments/storage-drivers/filesystem.js +33 -2
  230. package/src/database/record/attachments/storage-drivers/native.js +17 -2
  231. package/src/database/record/attachments/storage-drivers/s3.js +27 -7
  232. package/src/database/record/attachments/store.js +173 -53
  233. package/src/database/record/auditing.js +12 -9
  234. package/src/database/record/counter-cache-magnitude.js +5 -3
  235. package/src/database/record/index.js +95 -14
  236. package/src/database/record/instance-relationships/belongs-to.js +4 -2
  237. package/src/database/record/instance-relationships/has-many.js +7 -5
  238. package/src/database/record/instance-relationships/has-one.js +4 -2
  239. package/src/database/record/validators/uniqueness.js +3 -2
  240. package/src/database/table-data/table-foreign-key.js +9 -1
  241. package/src/environment-handlers/base.js +1 -1
  242. package/src/environment-handlers/node/attachment-path-source.js +144 -0
  243. package/src/environment-handlers/node.js +28 -3
  244. package/src/frontend-models/websocket-publishers.js +2 -2
  245. package/src/sync/server-sequence-allocator.js +43 -10
  246. package/src/sync/sync-client.js +6 -2
  247. package/src/sync/sync-publisher.js +10 -5
@@ -0,0 +1,28 @@
1
+ // @ts-check
2
+
3
+ import { forcedBoolean, forcedString } from "typanic"
4
+
5
+ /**
6
+ * IndexMetadataType type.
7
+ * @typedef {object} IndexMetadataType
8
+ * @property {string} column_name - Index column name.
9
+ * @property {string} index_name - Index name.
10
+ * @property {boolean} is_primary_key - Whether the index is primary.
11
+ * @property {boolean} is_unique - Whether the index is unique.
12
+ * @property {string} table_name - Table name.
13
+ */
14
+
15
+ /**
16
+ * Normalizes one untrusted database index metadata row.
17
+ * @param {import("./base.js").QueryRowType} row - Database index metadata row.
18
+ * @returns {IndexMetadataType} - Validated index metadata.
19
+ */
20
+ export function normalizeIndexMetadataRow(row) {
21
+ return {
22
+ column_name: forcedString(row.column_name, "index column_name"),
23
+ index_name: forcedString(row.index_name, "index index_name"),
24
+ is_primary_key: forcedBoolean(row.is_primary_key, "index is_primary_key"),
25
+ is_unique: forcedBoolean(row.is_unique, "index is_unique"),
26
+ table_name: forcedString(row.table_name, "index table_name")
27
+ }
28
+ }
@@ -1,7 +1,6 @@
1
1
  // @ts-check
2
2
 
3
3
  import BaseColumn from "../base-column.js"
4
- import ColumnsIndex from "./columns-index.js"
5
4
  import {digg} from "diggerize"
6
5
 
7
6
  export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
@@ -19,39 +18,9 @@ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
19
18
  getAutoIncrement() { return digg(this, "data", "isIdentity") === 1 }
20
19
 
21
20
  async getIndexes() {
22
- const options = this.getOptions()
23
- const sql = `
24
- SELECT
25
- sys.tables.name AS TableName,
26
- sys.columns.name AS ColumnName,
27
- sys.indexes.name AS index_name,
28
- sys.indexes.type_desc AS IndexType,
29
- sys.index_columns.is_included_column AS IsIncludedColumn,
30
- sys.indexes.is_unique,
31
- sys.indexes.is_primary_key,
32
- sys.indexes.is_unique_constraint
33
- FROM sys.indexes
34
- INNER JOIN sys.index_columns ON sys.indexes.object_id = sys.index_columns.object_id AND sys.indexes.index_id = sys.index_columns.index_id
35
- INNER JOIN sys.columns ON sys.index_columns.object_id = sys.columns.object_id AND sys.index_columns.column_id = sys.columns.column_id
36
- INNER JOIN sys.tables ON sys.indexes.object_id = sys.tables.object_id
37
- WHERE
38
- sys.tables.name = ${options.quote(this.getTable().getName())} AND
39
- sys.columns.name = ${options.quote(this.getName())}
40
- ORDER BY
41
- sys.indexes.name,
42
- sys.index_columns.key_ordinal
43
- `
21
+ const indexes = await this.getTable().getIndexes()
44
22
 
45
- const rows = await this.getDriver().query(sql)
46
- const indexes = []
47
-
48
- for (const row of rows) {
49
- const index = new ColumnsIndex(this.getTable(), row)
50
-
51
- indexes.push(index)
52
- }
53
-
54
- return indexes
23
+ return indexes.filter((index) => index.getColumnNames().includes(this.getName()))
55
24
  }
56
25
 
57
26
  getDefault() { return digg(this, "data", "COLUMN_DEFAULT") }
@@ -71,4 +40,3 @@ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
71
40
  getPrimaryKey() { return digg(this, "data", "isIdentity") === 1 }
72
41
  getType() { return digg(this, "data", "DATA_TYPE") }
73
42
  }
74
-
@@ -1,6 +1,43 @@
1
1
  // @ts-check
2
2
 
3
3
  import BaseColumnsIndex from "../base-columns-index.js"
4
+ import TableIndex from "../../table-data/table-index.js"
5
+
6
+ /**
7
+ * MssqlColumnsIndexDataType type.
8
+ * @typedef {object} MssqlColumnsIndexDataType
9
+ * @property {string[]} columnNames - Ordered index column names.
10
+ * @property {string} index_name - Index name.
11
+ * @property {boolean} is_primary_key - Whether the index is primary.
12
+ * @property {boolean} is_unique - Whether the index is unique.
13
+ * @property {string} table_name - Table name.
14
+ */
4
15
 
5
16
  export default class VelociousDatabaseDriversMssqlColumnsIndex extends BaseColumnsIndex {
17
+ /**
18
+ * Runs constructor.
19
+ * @param {import("../base-table.js").default} table - Table.
20
+ * @param {MssqlColumnsIndexDataType} data - Grouped index metadata.
21
+ */
22
+ constructor(table, data) {
23
+ super(table, data)
24
+ this.indexData = data
25
+ }
26
+
27
+ /**
28
+ * Runs get column names.
29
+ * @returns {string[]} - Ordered index column names.
30
+ */
31
+ getColumnNames() { return this.indexData.columnNames }
32
+
33
+ /**
34
+ * Runs get table data index.
35
+ * @returns {TableIndex} - Table-data index.
36
+ */
37
+ getTableDataIndex() {
38
+ return new TableIndex(this.getColumnNames(), {
39
+ name: this.getName(),
40
+ unique: this.isUnique()
41
+ })
42
+ }
6
43
  }
@@ -463,8 +463,8 @@ export default class VelociousDatabaseDriversMssql extends Base{
463
463
  })
464
464
  }
465
465
 
466
- async lastInsertID() {
467
- const result = await this.query("SELECT SCOPE_IDENTITY() AS last_insert_id")
466
+ async lastInsertID(options = {}) {
467
+ const result = await this.query("SELECT SCOPE_IDENTITY() AS last_insert_id", options)
468
468
  const lastInsertID = digg(result, 0, "last_insert_id")
469
469
 
470
470
  if (lastInsertID === null) throw new Error("Couldn't get the last inserted ID")
@@ -534,29 +534,32 @@ export default class VelociousDatabaseDriversMssql extends Base{
534
534
  /**
535
535
  * Runs start save point action.
536
536
  * @param {string} savePointName - Save point name.
537
+ * @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [options] - Transaction ownership.
537
538
  * @returns {Promise<void>} - Resolves when complete.
538
539
  */
539
- async _startSavePointAction(savePointName) {
540
- await this.query(`SAVE TRANSACTION [${savePointName}]`)
540
+ async _startSavePointAction(savePointName, options = {}) {
541
+ await this.query(`SAVE TRANSACTION [${savePointName}]`, options)
541
542
  }
542
543
 
543
544
  /**
544
545
  * Runs release save point action.
545
546
  * @param {string} savePointName - Save point name.
547
+ * @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [_options] - Transaction ownership.
546
548
  * @returns {Promise<void>} - Resolves when complete.
547
549
  */
548
- async _releaseSavePointAction(savePointName) { // eslint-disable-line no-unused-vars
550
+ async _releaseSavePointAction(savePointName, _options = {}) {
549
551
  // Do nothing in MS-SQL.
550
552
  }
551
553
 
552
554
  /**
553
555
  * Runs rollback save point action.
554
556
  * @param {string} savePointName - Save point name.
557
+ * @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [options] - Transaction ownership.
555
558
  * @returns {Promise<void>} - Resolves when complete.
556
559
  */
557
- async _rollbackSavePointAction(savePointName) {
560
+ async _rollbackSavePointAction(savePointName, options = {}) {
558
561
  try {
559
- await this.query(`ROLLBACK TRANSACTION [${savePointName}]`)
562
+ await this.query(`ROLLBACK TRANSACTION [${savePointName}]`, options)
560
563
  } catch (error) {
561
564
  const message = error instanceof Error ? error.message : `${error}`
562
565
 
@@ -5,6 +5,51 @@ import Column from "./column.js"
5
5
  import ColumnsIndex from "./columns-index.js"
6
6
  import {digg} from "diggerize"
7
7
  import ForeignKey from "./foreign-key.js"
8
+ import { normalizeIndexMetadataRow } from "../index-metadata.js"
9
+
10
+ /**
11
+ * MssqlGroupedIndexDataType type.
12
+ * @typedef {object} MssqlGroupedIndexDataType
13
+ * @property {string[]} columnNames - Ordered index column names.
14
+ * @property {string} index_name - Index name.
15
+ * @property {boolean} is_primary_key - Whether the index is primary.
16
+ * @property {boolean} is_unique - Whether the index is unique.
17
+ * @property {string} table_name - Table name.
18
+ */
19
+
20
+ /**
21
+ * Groups ordered SQL Server index rows into one metadata value per index.
22
+ * @param {import("../index-metadata.js").IndexMetadataType[]} indexRows - Ordered index metadata rows.
23
+ * @returns {MssqlGroupedIndexDataType[]} - Grouped index metadata.
24
+ */
25
+ export function groupMssqlIndexRows(indexRows) {
26
+ /** @type {Map<string, MssqlGroupedIndexDataType>} */
27
+ const indexDataByName = new Map()
28
+ /** @type {MssqlGroupedIndexDataType[]} */
29
+ const groupedIndexData = []
30
+
31
+ for (const indexRow of indexRows) {
32
+ const existingIndexData = indexDataByName.get(indexRow.index_name)
33
+
34
+ if (existingIndexData) {
35
+ existingIndexData.columnNames.push(indexRow.column_name)
36
+ continue
37
+ }
38
+
39
+ const indexData = {
40
+ columnNames: [indexRow.column_name],
41
+ index_name: indexRow.index_name,
42
+ is_primary_key: indexRow.is_primary_key,
43
+ is_unique: indexRow.is_unique,
44
+ table_name: indexRow.table_name
45
+ }
46
+
47
+ indexDataByName.set(indexRow.index_name, indexData)
48
+ groupedIndexData.push(indexData)
49
+ }
50
+
51
+ return groupedIndexData
52
+ }
8
53
 
9
54
  export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
10
55
  /**
@@ -43,7 +88,7 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
43
88
  return await this.getDriver()._cachedTableSchemaMetadata(this.getName(), "foreignKeys", async () => {
44
89
  const sql = `
45
90
  SELECT
46
- fk.name AS ForeignKeyName,
91
+ fk.name AS CONSTRAINT_NAME,
47
92
  tp.name AS ParentTable,
48
93
  ref.name AS ReferencedTable,
49
94
  cp.name AS ParentColumn,
@@ -63,7 +108,7 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
63
108
  ON fkc.referenced_object_id = cref.object_id
64
109
  AND fkc.referenced_column_id = cref.column_id
65
110
  WHERE tp.name = ${this.driver.quote(this.getName())}
66
- ORDER BY ForeignKeyName, ParentTable, ReferencedTable;
111
+ ORDER BY CONSTRAINT_NAME, ParentTable, ReferencedTable;
67
112
  `
68
113
 
69
114
  const foreignKeyRows = await this.driver.query(sql)
@@ -84,20 +129,18 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
84
129
  const options = this.getOptions()
85
130
  const sql = `
86
131
  SELECT
87
- sys.tables.name AS TableName,
88
- sys.columns.name AS ColumnName,
132
+ sys.tables.name AS table_name,
133
+ sys.columns.name AS column_name,
89
134
  sys.indexes.name AS index_name,
90
- sys.indexes.type_desc AS IndexType,
91
- sys.index_columns.is_included_column AS IsIncludedColumn,
92
135
  sys.indexes.is_unique,
93
- sys.indexes.is_primary_key,
94
- sys.indexes.is_unique_constraint
136
+ sys.indexes.is_primary_key
95
137
  FROM sys.indexes
96
138
  INNER JOIN sys.index_columns ON sys.indexes.object_id = sys.index_columns.object_id AND sys.indexes.index_id = sys.index_columns.index_id
97
139
  INNER JOIN sys.columns ON sys.index_columns.object_id = sys.columns.object_id AND sys.index_columns.column_id = sys.columns.column_id
98
140
  INNER JOIN sys.tables ON sys.indexes.object_id = sys.tables.object_id
99
141
  WHERE
100
- sys.tables.name = ${options.quote(this.getName())}
142
+ sys.tables.name = ${options.quote(this.getName())} AND
143
+ sys.index_columns.is_included_column = 0
101
144
  ORDER BY
102
145
  sys.indexes.name,
103
146
  sys.index_columns.key_ordinal
@@ -105,9 +148,10 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
105
148
 
106
149
  const rows = await this.getDriver().query(sql)
107
150
  const indexes = []
151
+ const indexRows = rows.map((row) => normalizeIndexMetadataRow(row))
108
152
 
109
- for (const row of rows) {
110
- const index = new ColumnsIndex(this, row)
153
+ for (const indexData of groupMssqlIndexRows(indexRows)) {
154
+ const index = new ColumnsIndex(this, indexData)
111
155
 
112
156
  indexes.push(index)
113
157
  }
@@ -403,9 +403,12 @@ export default class VelociousDatabaseDriversMysql extends Base{
403
403
  * large result set is read incrementally instead of being buffered. Overrides the base
404
404
  * buffered fallback with true server-side streaming.
405
405
  * @param {string} sql - SQL string to stream.
406
+ * @param {import("../base.js").QueryOptions} [options] - Query ownership options.
406
407
  * @yields {Record<string, unknown>} - The result rows, one at a time.
407
408
  */
408
- async *queryStream(sql) {
409
+ async *queryStream(sql, options = {}) {
410
+ await this._waitForOperationLease(options.operationOwner)
411
+
409
412
  if (!this.pool) await this.connect()
410
413
  if (!this.pool) throw new Error("MySQL pool failed to initialize")
411
414
 
@@ -554,10 +557,11 @@ export default class VelociousDatabaseDriversMysql extends Base{
554
557
 
555
558
  /**
556
559
  * Runs last insert id.
560
+ * @param {import("../base.js").QueryOptions} [options] - Query ownership options.
557
561
  * @returns {Promise<number>} - Resolves with the last insert id.
558
562
  */
559
- async lastInsertID() {
560
- const result = await this.query("SELECT LAST_INSERT_ID() AS last_insert_id")
563
+ async lastInsertID(options = {}) {
564
+ const result = await this.query("SELECT LAST_INSERT_ID() AS last_insert_id", options)
561
565
 
562
566
  return digg(result, 0, "last_insert_id")
563
567
  }
@@ -574,10 +578,11 @@ export default class VelociousDatabaseDriversMysql extends Base{
574
578
 
575
579
  /**
576
580
  * Runs start transaction action.
581
+ * @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [options] - Transaction ownership.
577
582
  * @returns {Promise<void>} - Resolves when complete.
578
583
  */
579
- async _startTransactionAction() {
580
- await this.query("START TRANSACTION")
584
+ async _startTransactionAction(options = {}) {
585
+ await this.query("START TRANSACTION", options)
581
586
  }
582
587
 
583
588
  /**
@@ -4,6 +4,19 @@ import AlterTableBase from "../../../query/alter-table-base.js"
4
4
  import TableColumn from "../../../table-data/table-column.js"
5
5
 
6
6
  export default class VelociousDatabaseConnectionDriversMysqlSqlAlterTable extends AlterTableBase {
7
+ /**
8
+ * Runs get drop foreign key sql.
9
+ * @param {import("../../../table-data/table-foreign-key.js").default} foreignKey - Foreign key to drop.
10
+ * @returns {string} - SQL fragment that removes the foreign key.
11
+ */
12
+ getDropForeignKeySQL(foreignKey) {
13
+ const name = foreignKey.getName()
14
+
15
+ if (!name) throw new Error(`Cannot remove unnamed foreign key on ${foreignKey.getTableName()}.${foreignKey.getColumnName()}`)
16
+
17
+ return `DROP FOREIGN KEY ${this.getOptions().quoteIndexName(name)}`
18
+ }
19
+
7
20
  /**
8
21
  * Builds MySQL ALTER TABLE statements, adding indexes atomically with columns.
9
22
  * @returns {Promise<string[]>} - Resolves with SQL statements.
@@ -1,7 +1,6 @@
1
1
  // @ts-check
2
2
 
3
3
  import BaseColumn from "../base-column.js"
4
- import ColumnsIndex from "./columns-index.js"
5
4
  import {digg} from "diggerize"
6
5
 
7
6
  export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumn {
@@ -25,33 +24,9 @@ export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumn {
25
24
  }
26
25
 
27
26
  async getIndexes() {
28
- const options = this.getOptions()
27
+ const indexes = await this.getTable().getIndexes()
29
28
 
30
- const indexesRows = await this.table.getDriver().query(`
31
- SELECT
32
- pg_attribute.attname AS column_name,
33
- pg_index.indexrelid::regclass as index_name,
34
- pg_class.relnamespace::regnamespace as schema_name,
35
- pg_class.relname as table_name,
36
- pg_index.indisprimary as is_primary_key,
37
- pg_index.indisunique as is_unique
38
- FROM pg_index
39
- JOIN pg_class ON pg_class.oid = pg_index.indrelid
40
- JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = ANY(pg_index.indkey)
41
- WHERE
42
- pg_attribute.attname = ${options.quote(this.getName())} AND
43
- pg_class.relname = ${options.quote(this.getTable().getName())}
44
- `)
45
-
46
- const indexes = []
47
-
48
- for (const indexRow of indexesRows) {
49
- const columnsIndex = new ColumnsIndex(this.getTable(), indexRow)
50
-
51
- indexes.push(columnsIndex)
52
- }
53
-
54
- return indexes
29
+ return indexes.filter((index) => index.getColumnNames().includes(this.getName()))
55
30
  }
56
31
 
57
32
  getDefault() {
@@ -1,6 +1,43 @@
1
1
  // @ts-check
2
2
 
3
3
  import BaseColumnsIndex from "../base-columns-index.js"
4
+ import TableIndex from "../../table-data/table-index.js"
5
+
6
+ /**
7
+ * PgsqlColumnsIndexDataType type.
8
+ * @typedef {object} PgsqlColumnsIndexDataType
9
+ * @property {string[]} columnNames - Ordered index column names.
10
+ * @property {string} index_name - Index name.
11
+ * @property {boolean} is_primary_key - Whether the index is primary.
12
+ * @property {boolean} is_unique - Whether the index is unique.
13
+ * @property {string} table_name - Table name.
14
+ */
4
15
 
5
16
  export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumnsIndex {
17
+ /**
18
+ * Runs constructor.
19
+ * @param {import("../base-table.js").default} table - Table.
20
+ * @param {PgsqlColumnsIndexDataType} data - Grouped index metadata.
21
+ */
22
+ constructor(table, data) {
23
+ super(table, data)
24
+ this.indexData = data
25
+ }
26
+
27
+ /**
28
+ * Runs get column names.
29
+ * @returns {string[]} - Ordered index column names.
30
+ */
31
+ getColumnNames() { return this.indexData.columnNames }
32
+
33
+ /**
34
+ * Runs get table data index.
35
+ * @returns {TableIndex} - Table-data index.
36
+ */
37
+ getTableDataIndex() {
38
+ return new TableIndex(this.getColumnNames(), {
39
+ name: this.getName(),
40
+ unique: this.isUnique()
41
+ })
42
+ }
6
43
  }
@@ -326,8 +326,8 @@ export default class VelociousDatabaseDriversPgsql extends Base{
326
326
  })
327
327
  }
328
328
 
329
- async lastInsertID() {
330
- const result = await this.query("SELECT LASTVAL() AS last_insert_id")
329
+ async lastInsertID(options = {}) {
330
+ const result = await this.query("SELECT LASTVAL() AS last_insert_id", options)
331
331
 
332
332
  return digg(result, 0, "last_insert_id")
333
333
  }
@@ -338,8 +338,8 @@ export default class VelociousDatabaseDriversPgsql extends Base{
338
338
  return this._options
339
339
  }
340
340
 
341
- async _startTransactionAction() {
342
- await this.query("START TRANSACTION")
341
+ async _startTransactionAction(options = {}) {
342
+ await this.query("START TRANSACTION", options)
343
343
  }
344
344
 
345
345
  /**
@@ -4,6 +4,51 @@ import BaseTable from "../base-table.js"
4
4
  import Column from "./column.js"
5
5
  import ColumnsIndex from "./columns-index.js"
6
6
  import ForeignKey from "./foreign-key.js"
7
+ import { normalizeIndexMetadataRow } from "../index-metadata.js"
8
+
9
+ /**
10
+ * PgsqlGroupedIndexDataType type.
11
+ * @typedef {object} PgsqlGroupedIndexDataType
12
+ * @property {string[]} columnNames - Ordered index column names.
13
+ * @property {string} index_name - Index name.
14
+ * @property {boolean} is_primary_key - Whether the index is primary.
15
+ * @property {boolean} is_unique - Whether the index is unique.
16
+ * @property {string} table_name - Table name.
17
+ */
18
+
19
+ /**
20
+ * Groups ordered PostgreSQL index rows into one metadata value per index.
21
+ * @param {import("../index-metadata.js").IndexMetadataType[]} indexRows - Ordered index metadata rows.
22
+ * @returns {PgsqlGroupedIndexDataType[]} - Grouped index metadata.
23
+ */
24
+ export function groupPgsqlIndexRows(indexRows) {
25
+ /** @type {Map<string, PgsqlGroupedIndexDataType>} */
26
+ const indexDataByName = new Map()
27
+ /** @type {PgsqlGroupedIndexDataType[]} */
28
+ const groupedIndexData = []
29
+
30
+ for (const indexRow of indexRows) {
31
+ const existingIndexData = indexDataByName.get(indexRow.index_name)
32
+
33
+ if (existingIndexData) {
34
+ existingIndexData.columnNames.push(indexRow.column_name)
35
+ continue
36
+ }
37
+
38
+ const indexData = {
39
+ columnNames: [indexRow.column_name],
40
+ index_name: indexRow.index_name,
41
+ is_primary_key: indexRow.is_primary_key,
42
+ is_unique: indexRow.is_unique,
43
+ table_name: indexRow.table_name
44
+ }
45
+
46
+ indexDataByName.set(indexRow.index_name, indexData)
47
+ groupedIndexData.push(indexData)
48
+ }
49
+
50
+ return groupedIndexData
51
+ }
7
52
 
8
53
  export default class VelociousDatabaseDriversPgsqlTable extends BaseTable {
9
54
  /**
@@ -100,23 +145,29 @@ export default class VelociousDatabaseDriversPgsqlTable extends BaseTable {
100
145
 
101
146
  const indexesRows = await this.getDriver().query(`
102
147
  SELECT
103
- pg_attribute.attname AS column_name,
104
- pg_index.indexrelid::regclass as index_name,
105
- pg_class.relnamespace::regnamespace as schema_name,
106
- pg_class.relname as table_name,
107
- pg_index.indisprimary as is_primary_key,
108
- pg_index.indisunique as is_unique
148
+ index_attribute.attname AS column_name,
149
+ pg_index.indexrelid::regclass AS index_name,
150
+ pg_class.relname AS table_name,
151
+ pg_index.indisprimary AS is_primary_key,
152
+ pg_index.indisunique AS is_unique
109
153
  FROM pg_index
110
154
  JOIN pg_class ON pg_class.oid = pg_index.indrelid
111
- JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = ANY(pg_index.indkey)
155
+ JOIN LATERAL unnest(pg_index.indkey) WITH ORDINALITY AS index_columns(attribute_number, ordinal_position) ON true
156
+ JOIN pg_attribute AS index_attribute ON index_attribute.attrelid = pg_class.oid AND index_attribute.attnum = index_columns.attribute_number
112
157
  WHERE
113
- pg_class.relname = ${options.quote(this.getName())}
158
+ pg_class.relname = ${options.quote(this.getName())} AND
159
+ index_columns.ordinal_position <= pg_index.indnkeyatts
160
+ ORDER BY
161
+ pg_index.indexrelid,
162
+ index_columns.ordinal_position
114
163
  `)
115
164
 
116
165
  const indexes = []
117
166
 
118
- for (const indexRow of indexesRows) {
119
- const columnsIndex = new ColumnsIndex(this, indexRow)
167
+ const indexRows = indexesRows.map((indexRow) => normalizeIndexMetadataRow(indexRow))
168
+
169
+ for (const indexData of groupPgsqlIndexRows(indexRows)) {
170
+ const columnsIndex = new ColumnsIndex(this, indexData)
120
171
 
121
172
  indexes.push(columnsIndex)
122
173
  }
@@ -262,8 +262,8 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
262
262
  })
263
263
  }
264
264
 
265
- async lastInsertID() {
266
- const result = await this.query("SELECT LAST_INSERT_ROWID() AS last_insert_id")
265
+ async lastInsertID(options = {}) {
266
+ const result = await this.query("SELECT LAST_INSERT_ROWID() AS last_insert_id", options)
267
267
 
268
268
  return digg(result, 0, "last_insert_id")
269
269
  }
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  import debounce from "debounce"
4
+ import Mutex from "epic-locks/build/mutex.js"
4
5
  import queryWeb from "./query.web.js"
5
6
 
6
7
  export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
@@ -12,6 +13,9 @@ export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
12
13
  */
13
14
  constructor(driver, connection, persistence) {
14
15
  this.connection = connection
16
+ this.databaseSaveDeferred = false
17
+ this.databaseSaveMutex = new Mutex()
18
+ this.databaseTransactionStarting = false
15
19
  this.driver = driver
16
20
  this.persistence = persistence
17
21
  }
@@ -30,6 +34,54 @@ export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
30
34
  await this.saveDatabase()
31
35
  }
32
36
 
37
+ /**
38
+ * Flushes only when a mutation save is pending or was deferred by a transaction.
39
+ * @returns {Promise<void>} - Resolves when pending database bytes are stored.
40
+ */
41
+ async flushPendingDatabaseSave() {
42
+ if (!this.saveDatabaseDebounce.isPending && !this.databaseSaveDeferred) return
43
+
44
+ await this.flushDatabaseSave()
45
+ }
46
+
47
+ /**
48
+ * Drains active and queued persistence before atomically starting an outer transaction.
49
+ * @param {() => Promise<void>} callback - Starts the SQL transaction.
50
+ * @returns {Promise<void>} - Resolves after BEGIN succeeds.
51
+ */
52
+ async withTransactionStart(callback) {
53
+ if (this.saveDatabaseDebounce.isPending) {
54
+ this.saveDatabaseDebounce.clear()
55
+ this.databaseSaveDeferred = true
56
+ }
57
+
58
+ await this.databaseSaveMutex.sync(async () => {
59
+ if (this.databaseSaveDeferred) {
60
+ this.databaseSaveDeferred = false
61
+ const databaseContent = this.connection.export()
62
+
63
+ await this.persistence.save(databaseContent)
64
+ }
65
+
66
+ this.databaseTransactionStarting = true
67
+
68
+ try {
69
+ await callback()
70
+ } catch (error) {
71
+ this.databaseTransactionStarting = false
72
+ throw error
73
+ }
74
+ })
75
+ }
76
+
77
+ /**
78
+ * Marks successful outer transaction admission complete after driver state is updated.
79
+ * @returns {void}
80
+ */
81
+ completeTransactionStart() {
82
+ this.databaseTransactionStarting = false
83
+ }
84
+
33
85
  /**
34
86
  * Runs query.
35
87
  * @param {string} sql - SQL string.
@@ -59,9 +111,17 @@ export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
59
111
  }
60
112
 
61
113
  saveDatabase = async () => {
62
- const databaseContent = this.connection.export()
114
+ await this.databaseSaveMutex.sync(async () => {
115
+ if (this.driver.insideTransaction() || this.databaseTransactionStarting) {
116
+ this.databaseSaveDeferred = true
117
+ return
118
+ }
119
+
120
+ this.databaseSaveDeferred = false
121
+ const databaseContent = this.connection.export()
63
122
 
64
- await this.persistence.save(databaseContent)
123
+ await this.persistence.save(databaseContent)
124
+ })
65
125
  }
66
126
 
67
127
  saveDatabaseDebounce = debounce(this.saveDatabase, 500)