velocious 1.0.592 → 1.0.594
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.
- package/README.md +11 -2
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +68 -13
- package/build/database/drivers/base.js +83 -34
- package/build/database/drivers/mssql/index.js +32 -0
- package/build/database/drivers/mysql/index.js +17 -0
- package/build/database/drivers/pgsql/index.js +11 -0
- package/build/database/drivers/sqlite/base.js +12 -0
- package/build/database/drivers/sqlite/connection-sql-js.js +3 -2
- package/build/database/drivers/sqlite/index.js +7 -1
- package/build/database/drivers/sqlite/index.native.js +9 -3
- package/build/database/drivers/sqlite/index.web.js +6 -3
- package/build/database/migrator.js +90 -24
- package/build/database/operation.js +55 -4
- package/build/database/query/join-object.js +7 -4
- package/build/database/query/model-class-query.js +38 -7
- package/build/database/query/preloader/belongs-to.js +9 -6
- package/build/database/query/preloader/has-many.js +25 -14
- package/build/database/query/preloader/has-one.js +7 -4
- package/build/database/query/preloader/query-for-model.js +16 -1
- package/build/database/query/preloader.js +6 -4
- package/build/database/query/query-data.js +3 -3
- package/build/database/query/where-model-class-hash.js +8 -4
- package/build/database/query/with-count.js +4 -3
- package/build/database/record/index.js +148 -5
- package/build/database/record/instance-relationships/base.js +13 -0
- package/build/database/record/instance-relationships/belongs-to.js +1 -1
- package/build/database/record/instance-relationships/has-many.js +5 -2
- package/build/database/record/instance-relationships/has-one.js +1 -1
- package/build/database/record/relationships/base.js +17 -0
- package/build/database/use-database.js +81 -28
- package/build/environment-handlers/base.js +18 -0
- package/build/environment-handlers/node.js +32 -0
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +13 -4
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +56 -16
- package/build/src/database/drivers/base.d.ts +22 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +81 -37
- package/build/src/database/drivers/mssql/index.d.ts +7 -0
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +15 -1
- package/build/src/database/drivers/mysql/index.d.ts +7 -0
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +15 -1
- package/build/src/database/drivers/pgsql/index.d.ts +6 -0
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +10 -1
- package/build/src/database/drivers/sqlite/base.d.ts +7 -0
- package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/base.js +11 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +4 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.js +4 -3
- package/build/src/database/drivers/sqlite/index.d.ts +2 -1
- package/build/src/database/drivers/sqlite/index.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.js +7 -2
- package/build/src/database/drivers/sqlite/index.native.d.ts +2 -1
- package/build/src/database/drivers/sqlite/index.native.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.native.js +9 -4
- package/build/src/database/drivers/sqlite/index.web.d.ts +2 -1
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +7 -4
- package/build/src/database/migrator.d.ts +34 -0
- package/build/src/database/migrator.d.ts.map +1 -1
- package/build/src/database/migrator.js +83 -23
- package/build/src/database/operation.d.ts +15 -1
- package/build/src/database/operation.d.ts.map +1 -1
- package/build/src/database/operation.js +52 -5
- package/build/src/database/query/join-object.d.ts.map +1 -1
- package/build/src/database/query/join-object.js +7 -5
- package/build/src/database/query/model-class-query.d.ts +6 -0
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +33 -8
- package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
- package/build/src/database/query/preloader/belongs-to.js +9 -7
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +23 -16
- package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-one.js +7 -5
- package/build/src/database/query/preloader/query-for-model.d.ts +8 -0
- package/build/src/database/query/preloader/query-for-model.d.ts.map +1 -1
- package/build/src/database/query/preloader/query-for-model.js +15 -2
- package/build/src/database/query/preloader.d.ts.map +1 -1
- package/build/src/database/query/preloader.js +6 -5
- package/build/src/database/query/query-data.js +4 -4
- package/build/src/database/query/where-model-class-hash.d.ts.map +1 -1
- package/build/src/database/query/where-model-class-hash.js +7 -5
- package/build/src/database/query/with-count.js +5 -4
- package/build/src/database/record/index.d.ts +95 -0
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +126 -6
- package/build/src/database/record/instance-relationships/base.d.ts +6 -0
- package/build/src/database/record/instance-relationships/base.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/base.js +12 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +2 -2
- package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/has-many.js +6 -3
- package/build/src/database/record/instance-relationships/has-one.js +2 -2
- package/build/src/database/record/relationships/base.d.ts +11 -0
- package/build/src/database/record/relationships/base.d.ts.map +1 -1
- package/build/src/database/record/relationships/base.js +15 -1
- package/build/src/database/use-database.d.ts +33 -5
- package/build/src/database/use-database.d.ts.map +1 -1
- package/build/src/database/use-database.js +81 -30
- package/build/src/environment-handlers/base.d.ts +16 -0
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +17 -1
- package/build/src/environment-handlers/node.d.ts +20 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +29 -1
- package/build/src/sync/server-change-feed.d.ts +14 -2
- package/build/src/sync/server-change-feed.d.ts.map +1 -1
- package/build/src/sync/server-change-feed.js +50 -10
- package/build/src/sync/sync-scope-store.d.ts +13 -2
- package/build/src/sync/sync-scope-store.d.ts.map +1 -1
- package/build/src/sync/sync-scope-store.js +48 -7
- package/build/src/tenants/frontend-tenant-sqlite-lifecycle.d.ts +43 -1
- package/build/src/tenants/frontend-tenant-sqlite-lifecycle.d.ts.map +1 -1
- package/build/src/tenants/frontend-tenant-sqlite-lifecycle.js +141 -17
- package/build/src/tenants/tenant-handle.d.ts +36 -0
- package/build/src/tenants/tenant-handle.d.ts.map +1 -1
- package/build/src/tenants/tenant-handle.js +73 -3
- package/build/src/testing/browser-test-app.js +3 -1
- package/build/src/testing/browser-use-database-hook-scenarios.d.ts +16 -0
- package/build/src/testing/browser-use-database-hook-scenarios.d.ts.map +1 -0
- package/build/src/testing/browser-use-database-hook-scenarios.js +182 -0
- package/build/src/testing/shared-transaction-broker.d.ts +14 -0
- package/build/src/testing/shared-transaction-broker.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-broker.js +44 -7
- package/build/src/testing/shared-transaction-connection-coordinator.d.ts +5 -4
- package/build/src/testing/shared-transaction-connection-coordinator.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-connection-coordinator.js +21 -10
- package/build/src/utils/model-scope.d.ts +2 -2
- package/build/src/utils/model-scope.d.ts.map +1 -1
- package/build/src/utils/model-scope.js +5 -3
- package/build/src/utils/ransack.d.ts.map +1 -1
- package/build/src/utils/ransack.js +5 -3
- package/build/sync/server-change-feed.js +58 -8
- package/build/sync/sync-scope-store.js +55 -5
- package/build/tenants/frontend-tenant-sqlite-lifecycle.js +150 -15
- package/build/tenants/tenant-handle.js +90 -6
- package/build/testing/browser-test-app.js +2 -0
- package/build/testing/browser-use-database-hook-scenarios.js +204 -0
- package/build/testing/shared-transaction-broker.js +48 -7
- package/build/testing/shared-transaction-connection-coordinator.js +23 -9
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/model-scope.js +5 -2
- package/build/utils/ransack.js +5 -2
- package/package.json +1 -1
- package/scripts/browser-test-session.js +489 -0
- package/scripts/prewarm-chromedriver.js +3 -22
- package/scripts/test-browser.js +52 -16
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +68 -13
- package/src/database/drivers/base.js +83 -34
- package/src/database/drivers/mssql/index.js +32 -0
- package/src/database/drivers/mysql/index.js +17 -0
- package/src/database/drivers/pgsql/index.js +11 -0
- package/src/database/drivers/sqlite/base.js +12 -0
- package/src/database/drivers/sqlite/connection-sql-js.js +3 -2
- package/src/database/drivers/sqlite/index.js +7 -1
- package/src/database/drivers/sqlite/index.native.js +9 -3
- package/src/database/drivers/sqlite/index.web.js +6 -3
- package/src/database/migrator.js +90 -24
- package/src/database/operation.js +55 -4
- package/src/database/query/join-object.js +7 -4
- package/src/database/query/model-class-query.js +38 -7
- package/src/database/query/preloader/belongs-to.js +9 -6
- package/src/database/query/preloader/has-many.js +25 -14
- package/src/database/query/preloader/has-one.js +7 -4
- package/src/database/query/preloader/query-for-model.js +16 -1
- package/src/database/query/preloader.js +6 -4
- package/src/database/query/query-data.js +3 -3
- package/src/database/query/where-model-class-hash.js +8 -4
- package/src/database/query/with-count.js +4 -3
- package/src/database/record/index.js +148 -5
- package/src/database/record/instance-relationships/base.js +13 -0
- package/src/database/record/instance-relationships/belongs-to.js +1 -1
- package/src/database/record/instance-relationships/has-many.js +5 -2
- package/src/database/record/instance-relationships/has-one.js +1 -1
- package/src/database/record/relationships/base.js +17 -0
- package/src/database/use-database.js +81 -28
- package/src/environment-handlers/base.js +18 -0
- package/src/environment-handlers/node.js +32 -0
- package/src/sync/server-change-feed.js +58 -8
- package/src/sync/sync-scope-store.js +55 -5
- package/src/tenants/frontend-tenant-sqlite-lifecycle.js +150 -15
- package/src/tenants/tenant-handle.js +90 -6
- package/src/testing/browser-test-app.js +2 -0
- package/src/testing/browser-use-database-hook-scenarios.js +204 -0
- package/src/testing/shared-transaction-broker.js +48 -7
- package/src/testing/shared-transaction-connection-coordinator.js +23 -9
- package/src/utils/model-scope.js +5 -2
- package/src/utils/ransack.js +5 -2
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Self-sustaining sync feeds: upstream imports triggered by the changes pull itself, with framework-owned coalescing and throttling (see [docs/sync-upstream-imports.md](docs/sync-upstream-imports.md))
|
|
21
21
|
* AwesomeTasks-shaped offline sync proof using routed resources, domain commands, signed offline grants, and peer-forwarded mutations (see the [developer guide](docs/shared-resource-sync-guide.md) and [proof](docs/awesome-tasks-offline-sync-proof.md))
|
|
22
22
|
* SQLite web persistence that automatically prefers OPFS, then IndexedDB, and migrates legacy persisted bytes when possible (see [docs/sqlite-web-persistence.md](docs/sqlite-web-persistence.md))
|
|
23
|
-
* Bounded frontend tenant SQLite handles with durable flush/close, backend-complete deletion, clean-only LRU eviction, and scoped pins (see [docs/frontend-tenant-sqlite-lifecycle.md](docs/frontend-tenant-sqlite-lifecycle.md))
|
|
23
|
+
* Bounded frontend tenant SQLite handles with independently deduplicated per-database migrations/model readiness, React lifecycle integration, durable flush/close, backend-complete deletion, clean-only LRU eviction, and scoped pins (see [docs/frontend-tenant-sqlite-lifecycle.md](docs/frontend-tenant-sqlite-lifecycle.md))
|
|
24
24
|
* Expo / Metro compatibility guidance and a real Expo export check (see [docs/expo-metro-compatibility.md](docs/expo-metro-compatibility.md))
|
|
25
25
|
* Gap-less positional lists with automatic reordering via `actsAsList`, including models with numeric, string, or UUID primary keys (see [docs/acts-as-list.md](docs/acts-as-list.md))
|
|
26
26
|
* Rails-style nested-attribute writes on frontend-model `save()` (see [docs/nested-attributes.md](docs/nested-attributes.md))
|
|
@@ -328,6 +328,7 @@ npm run test:browser
|
|
|
328
328
|
```
|
|
329
329
|
|
|
330
330
|
Browser system tests must be named `*.browser-test.js` or `*.browser-spec.js` (override with `VELOCIOUS_BROWSER_TEST_PATTERN`).
|
|
331
|
+
The runner validates and persists the exact Chrome/ChromeDriver pair selected by `scripts/prewarm-chromedriver.js`, then owns ChromeDriver and Chrome as a managed process group. Startup failures report the runtime paths and versions, service URL, retained logs under `tmp/browser-test-chrome/`, and Chrome process state before and after cleanup.
|
|
331
332
|
|
|
332
333
|
Use beforeAll/afterAll for suite-level setup/teardown.
|
|
333
334
|
|
|
@@ -954,7 +955,8 @@ Translated models also get a `currentTranslation` `hasOne` relationship scoped t
|
|
|
954
955
|
Async class APIs initialize record metadata on first use when a model has not
|
|
955
956
|
already been initialized eagerly. See [docs/model-initialization.md](docs/model-initialization.md)
|
|
956
957
|
for the eager and lazy initialization behavior, including atomic shared bootstrap
|
|
957
|
-
and complete recovery after an eager initialization failure
|
|
958
|
+
and complete recovery after an eager initialization failure or database-connection
|
|
959
|
+
closure without overlapping stale and current bootstrap side effects.
|
|
958
960
|
|
|
959
961
|
## Lifecycle callbacks
|
|
960
962
|
|
|
@@ -2070,6 +2072,13 @@ If you are developing on Velocious, you can run the tests with:
|
|
|
2070
2072
|
|
|
2071
2073
|
Tests default to a 60-second timeout. Override per test with `{timeoutSeconds: 5}` or set a suite-wide default via `configureTests({defaultTimeoutSeconds: 30})`.
|
|
2072
2074
|
|
|
2075
|
+
Truncation-based test cleanup batches eligible tables into one request on PostgreSQL,
|
|
2076
|
+
SQL Server, and SQLite while preserving each driver's existing identity behavior,
|
|
2077
|
+
foreign-key restoration, stale-schema retry, and SQL.js persistence guarantees.
|
|
2078
|
+
MySQL/MariaDB batching requires the database's existing `multipleStatements: true`
|
|
2079
|
+
option; the default configuration keeps sequential `TRUNCATE TABLE` requests. See
|
|
2080
|
+
[database cleanup guidance](docs/testing-guidelines.md#truncation-cleanup).
|
|
2081
|
+
|
|
2073
2082
|
Request tests share transaction-active, non-tenant database connections with their in-process HTTP handlers. Eligibility is evaluated when each request is dispatched, so a hook can start a transaction and issue a request in the same callback. This makes uncommitted setup visible to handlers while preserving rollback isolation. Without an active transaction, handlers use independent pooled connections, so concurrency and locking tests can opt out of transaction cleanup and exercise production-style connections. Shared connection state is scoped to the test lifecycle and cleared around each test. See [docs/testing-guidelines.md](docs/testing-guidelines.md#request-test-database-connections).
|
|
2074
2083
|
|
|
2075
2084
|
Transactional tests also share active non-tenant connections with real forked, reusable pooled, and spawned background-job child runners through a per-attempt test-only loopback broker. Parent setup and child writes therefore occupy the same physical transaction and roll back together, including background-job persistence. Multiple configured databases route by identifier; tenant-only databases remain excluded. Tests using `{transaction: false, truncate: true}` retain ordinary independent physical connections for concurrency and locking coverage. See [docs/testing-guidelines.md](docs/testing-guidelines.md#request-test-database-connections).
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
* @property {() => ReturnType<typeof JSON.parse>} [getConnection] - Custom connection factory override.
|
|
73
73
|
* @property {string} [host] - Database host.
|
|
74
74
|
* @property {boolean} [migrations] - Whether migrations are enabled for this database.
|
|
75
|
-
* @property {boolean} [multipleStatements] - (MySQL) Opt in to multi-statement queries so
|
|
75
|
+
* @property {boolean} [multipleStatements] - (MySQL) Opt in to multi-statement queries so structure SQL loads and all-table cleanup can batch into one round-trip. Off by default; ordinary queries otherwise reject stacked statements.
|
|
76
76
|
* @property {number} [maxRowsPerInsert] - Maximum rows per `INSERT ... VALUES (...), (...), ...` statement generated by `Record.insertMultiple`. Defaults to 500.
|
|
77
77
|
* @property {number} [maxInsertSqlBytes] - Maximum serialized SQL size, in bytes, for a single `INSERT ... VALUES (...), (...), ...` statement. Defaults to 1 MiB (1048576).
|
|
78
78
|
* @property {number} [maxInClauseValues] - Maximum values in a single `IN (...)` cohort used by preloads, association counts, and queryData aggregates. Defaults to 999.
|
package/build/configuration.js
CHANGED
|
@@ -302,8 +302,9 @@ export default class VelociousConfiguration {
|
|
|
302
302
|
*/
|
|
303
303
|
this._initializeModelsPromise = undefined
|
|
304
304
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
305
|
+
* Current `initialize()` promise, memoized so concurrent callers await the
|
|
306
|
+
* same bootstrap. Retained across a connection close until stale bootstrap
|
|
307
|
+
* work settles, then cleared by identity before the new generation retries.
|
|
307
308
|
* @type {Promise<void> | undefined}
|
|
308
309
|
*/
|
|
309
310
|
this._initializePromise = undefined
|
|
@@ -1102,6 +1103,18 @@ export default class VelociousConfiguration {
|
|
|
1102
1103
|
}
|
|
1103
1104
|
}
|
|
1104
1105
|
|
|
1106
|
+
/**
|
|
1107
|
+
* Invalidates record metadata owned by one closed/deleted physical tenant
|
|
1108
|
+
* database while preserving every other tenant generation.
|
|
1109
|
+
* @param {string} databaseIdentity - Logical identifier plus pool reuse key.
|
|
1110
|
+
* @returns {void}
|
|
1111
|
+
*/
|
|
1112
|
+
clearRecordMetadataForDatabaseIdentity(databaseIdentity) {
|
|
1113
|
+
for (const modelClass of Object.values(this.modelClasses)) {
|
|
1114
|
+
modelClass.clearRecordMetadataValuesForDatabaseIdentity(databaseIdentity)
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1105
1118
|
/**
|
|
1106
1119
|
* Runs get database pool type.
|
|
1107
1120
|
* @param {string} identifier - Identifier.
|
|
@@ -2054,10 +2067,25 @@ export default class VelociousConfiguration {
|
|
|
2054
2067
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2055
2068
|
*/
|
|
2056
2069
|
async initializeModels(args = {type: "server"}) {
|
|
2070
|
+
const modelInitializationGeneration = this._modelInitializationGeneration
|
|
2071
|
+
|
|
2057
2072
|
if (this._modelsInitialized) return
|
|
2058
|
-
if (this._initializeModelsPromise)
|
|
2073
|
+
if (this._initializeModelsPromise) {
|
|
2074
|
+
const initializeModelsPromise = this._initializeModelsPromise
|
|
2075
|
+
|
|
2076
|
+
await initializeModelsPromise
|
|
2077
|
+
|
|
2078
|
+
if (this._modelInitializationGeneration === modelInitializationGeneration && !this._modelsInitialized) {
|
|
2079
|
+
if (this._initializeModelsPromise === initializeModelsPromise) {
|
|
2080
|
+
this._initializeModelsPromise = undefined
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
return await this.initializeModels(args)
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
return
|
|
2087
|
+
}
|
|
2059
2088
|
|
|
2060
|
-
const modelInitializationGeneration = this._modelInitializationGeneration
|
|
2061
2089
|
const initializeModelsPromise = (async () => {
|
|
2062
2090
|
const shouldSkipDummyModelInitialization = process.env.VELOCIOUS_SKIP_DUMMY_MODEL_INITIALIZATION === "1"
|
|
2063
2091
|
&& process.env.VELOCIOUS_BROWSER_TESTS === "true"
|
|
@@ -2110,6 +2138,12 @@ export default class VelociousConfiguration {
|
|
|
2110
2138
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2111
2139
|
*/
|
|
2112
2140
|
async initialize({type} = {type: "undefined"}) {
|
|
2141
|
+
if (this._closeDatabaseConnectionsPromise) {
|
|
2142
|
+
await this._closeDatabaseConnectionsPromise
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
const initializationGeneration = this._modelInitializationGeneration
|
|
2146
|
+
|
|
2113
2147
|
if (this._isInitialized) return
|
|
2114
2148
|
// Memoize the in-progress initialization so concurrent callers await the same
|
|
2115
2149
|
// bootstrap instead of racing. `_isInitialized` was previously set to `true`
|
|
@@ -2117,15 +2151,29 @@ export default class VelociousConfiguration {
|
|
|
2117
2151
|
// `pooledRunnerConcurrency > 1` starting several jobs on a cold child) could
|
|
2118
2152
|
// skip initialization and load models / perform a job while the first call
|
|
2119
2153
|
// was still awaiting model discovery and initializers. Mirrors connectBeacon.
|
|
2120
|
-
if (this._initializePromise)
|
|
2154
|
+
if (this._initializePromise) {
|
|
2155
|
+
const initializePromise = this._initializePromise
|
|
2121
2156
|
|
|
2122
|
-
|
|
2157
|
+
await initializePromise
|
|
2158
|
+
|
|
2159
|
+
if (this._modelInitializationGeneration === initializationGeneration && !this._isInitialized) {
|
|
2160
|
+
if (this._initializePromise === initializePromise) {
|
|
2161
|
+
this._initializePromise = undefined
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
return await this.initialize({type})
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
return
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
const initializePromise = (async () => {
|
|
2123
2171
|
await this.initializeModels({type})
|
|
2124
2172
|
|
|
2125
2173
|
// Model initialization can be invalidated by a concurrent connection close.
|
|
2126
2174
|
// If models are not ready, stop without marking the configuration initialized
|
|
2127
2175
|
// so the next caller retries a full bootstrap.
|
|
2128
|
-
if (!this._modelsInitialized) return
|
|
2176
|
+
if (this._modelInitializationGeneration !== initializationGeneration || !this._modelsInitialized) return
|
|
2129
2177
|
|
|
2130
2178
|
await this.getEnvironmentHandler().autoDiscoverResources(this)
|
|
2131
2179
|
this._mergeDiscoveredAbilityResources()
|
|
@@ -2147,22 +2195,28 @@ export default class VelociousConfiguration {
|
|
|
2147
2195
|
}
|
|
2148
2196
|
}
|
|
2149
2197
|
|
|
2150
|
-
this.
|
|
2198
|
+
if (this._modelInitializationGeneration === initializationGeneration) {
|
|
2199
|
+
this._isInitialized = true
|
|
2200
|
+
}
|
|
2151
2201
|
})()
|
|
2152
2202
|
|
|
2203
|
+
this._initializePromise = initializePromise
|
|
2204
|
+
|
|
2153
2205
|
try {
|
|
2154
|
-
await
|
|
2206
|
+
await initializePromise
|
|
2155
2207
|
} catch (error) {
|
|
2156
2208
|
// Let a later call retry a failed initialization instead of every future
|
|
2157
2209
|
// caller awaiting the same cached rejection.
|
|
2158
|
-
this._initializePromise
|
|
2210
|
+
if (this._initializePromise === initializePromise) {
|
|
2211
|
+
this._initializePromise = undefined
|
|
2212
|
+
}
|
|
2159
2213
|
throw error
|
|
2160
2214
|
}
|
|
2161
2215
|
|
|
2162
2216
|
// If the inner IIFE returned without marking the configuration initialized
|
|
2163
2217
|
// (e.g. because models were invalidated mid-bootstrap), clear the promise so
|
|
2164
2218
|
// a later call retries a full bootstrap.
|
|
2165
|
-
if (!this._isInitialized) {
|
|
2219
|
+
if (!this._isInitialized && this._initializePromise === initializePromise) {
|
|
2166
2220
|
this._initializePromise = undefined
|
|
2167
2221
|
}
|
|
2168
2222
|
}
|
|
@@ -3072,11 +3126,11 @@ export default class VelociousConfiguration {
|
|
|
3072
3126
|
* database configuration. No ambient tenant value is read during checkout or
|
|
3073
3127
|
* execution.
|
|
3074
3128
|
* @template T
|
|
3075
|
-
* @param {{databaseConfiguration: import("./configuration-types.js").DatabaseConfigurationType, databaseIdentifier: string, name?: string, tenant?: object}} options - Captured operation options.
|
|
3129
|
+
* @param {{databaseConfiguration: import("./configuration-types.js").DatabaseConfigurationType, databaseIdentifier: string, name?: string, schemaGeneration?: string, tenant?: object}} options - Captured operation options.
|
|
3076
3130
|
* @param {(operation: DatabaseOperation) => Promise<T>} callback - Operation callback.
|
|
3077
3131
|
* @returns {Promise<T>} - Callback result.
|
|
3078
3132
|
*/
|
|
3079
|
-
async withDatabaseOperation({databaseConfiguration, databaseIdentifier, name = "Configuration.withDatabaseOperation", tenant, ...restArgs}, callback) {
|
|
3133
|
+
async withDatabaseOperation({databaseConfiguration, databaseIdentifier, name = "Configuration.withDatabaseOperation", schemaGeneration, tenant, ...restArgs}, callback) {
|
|
3080
3134
|
restArgsError(restArgs)
|
|
3081
3135
|
|
|
3082
3136
|
if (!databaseIdentifier) throw new Error("Configuration.withDatabaseOperation requires a databaseIdentifier")
|
|
@@ -3095,6 +3149,7 @@ export default class VelociousConfiguration {
|
|
|
3095
3149
|
databaseIdentifier,
|
|
3096
3150
|
enforceCurrentTenantReuseKey: false,
|
|
3097
3151
|
owner,
|
|
3152
|
+
schemaGeneration,
|
|
3098
3153
|
tenant
|
|
3099
3154
|
})
|
|
3100
3155
|
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
* @property {boolean} [processListComment] - Whether to add process-list comments to the query.
|
|
66
66
|
* @property {boolean} [retry] - Whether retryable errors may retry the query; defaults to true.
|
|
67
67
|
* @property {boolean} [sessionTimeZone] - Whether to ensure the configured database session time zone before the query.
|
|
68
|
+
* @property {boolean} [sqliteScript] - Internal SQLite flag selecting native multi-statement script execution.
|
|
68
69
|
* @property {AbortSignal} [signal] - Aborts the in-flight query (destroying its connection) when it fires.
|
|
69
70
|
* @property {string} [sourceStack] - Stack captured at the caller boundary.
|
|
70
71
|
* @property {symbol} [operationOwner] - Opaque owner for an operation-leased connection.
|
|
@@ -141,7 +142,8 @@ import TableData from "../table-data/index.js"
|
|
|
141
142
|
import TableColumn from "../table-data/table-column.js"
|
|
142
143
|
import TableForeignKey from "../table-data/table-foreign-key.js"
|
|
143
144
|
import wait from "awaitery/build/wait.js"
|
|
144
|
-
import {optionalPositiveInteger} from "typanic"
|
|
145
|
+
import { optionalPositiveInteger } from "typanic"
|
|
146
|
+
import { coordinateSharedTransactionConnection } from "../../testing/shared-transaction-connection-coordinator.js"
|
|
145
147
|
|
|
146
148
|
/** Maximum characters inspected when building the debug SQL preview. */
|
|
147
149
|
const SQL_PREVIEW_SCAN_LIMIT = 4096
|
|
@@ -193,6 +195,10 @@ export default class VelociousDatabaseDriversBase {
|
|
|
193
195
|
* Narrows the runtime value to the documented type.
|
|
194
196
|
* @type {TransactionCallbackFrame[]} */
|
|
195
197
|
_transactionCallbackFrames
|
|
198
|
+
/** @type {Promise<void>} */
|
|
199
|
+
_transactionCompletionPromise
|
|
200
|
+
/** @type {(() => void) | undefined} */
|
|
201
|
+
_resolveTransactionCompletion
|
|
196
202
|
/**
|
|
197
203
|
* Narrows the runtime value to the documented type.
|
|
198
204
|
* @type {Map<string, Promise<ReturnType<typeof JSON.parse>>>} */
|
|
@@ -229,6 +235,8 @@ export default class VelociousDatabaseDriversBase {
|
|
|
229
235
|
this.logger = new Logger(this)
|
|
230
236
|
this._transactionCallbackFrames = []
|
|
231
237
|
this._transactionsCount = 0
|
|
238
|
+
this._transactionCompletionPromise = Promise.resolve()
|
|
239
|
+
this._resolveTransactionCompletion = undefined
|
|
232
240
|
this._transactionsActionsMutex = new Mutex()
|
|
233
241
|
this._schemaCache = new Map()
|
|
234
242
|
}
|
|
@@ -1424,6 +1432,12 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1424
1432
|
*/
|
|
1425
1433
|
insideTransaction() { return this._transactionsCount > 0 }
|
|
1426
1434
|
|
|
1435
|
+
/**
|
|
1436
|
+
* Returns the completion promise identifying the current outer transaction.
|
|
1437
|
+
* @returns {Promise<void>} Resolves after that transaction commits or rolls back.
|
|
1438
|
+
*/
|
|
1439
|
+
transactionCompletion() { return this._transactionCompletionPromise }
|
|
1440
|
+
|
|
1427
1441
|
/**
|
|
1428
1442
|
* Runs start transaction.
|
|
1429
1443
|
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
@@ -1444,6 +1458,12 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1444
1458
|
|
|
1445
1459
|
await this._startTransactionAction(options)
|
|
1446
1460
|
this._transactionsCount++
|
|
1461
|
+
|
|
1462
|
+
if (this._transactionsCount === 1) {
|
|
1463
|
+
this._transactionCompletionPromise = new Promise((resolve) => {
|
|
1464
|
+
this._resolveTransactionCompletion = resolve
|
|
1465
|
+
})
|
|
1466
|
+
}
|
|
1447
1467
|
})
|
|
1448
1468
|
|
|
1449
1469
|
if (!blockingOperationLease) return
|
|
@@ -1470,9 +1490,20 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1470
1490
|
await this._transactionsActionsMutex.sync(async () => {
|
|
1471
1491
|
await this._commitTransactionAction(options)
|
|
1472
1492
|
this._transactionsCount--
|
|
1493
|
+
this._resolveCompletedTransaction()
|
|
1473
1494
|
})
|
|
1474
1495
|
}
|
|
1475
1496
|
|
|
1497
|
+
/** Resolves the current outer transaction completion when it has finished. */
|
|
1498
|
+
_resolveCompletedTransaction() {
|
|
1499
|
+
if (this._transactionsCount !== 0) return
|
|
1500
|
+
|
|
1501
|
+
const resolve = this._resolveTransactionCompletion
|
|
1502
|
+
|
|
1503
|
+
this._resolveTransactionCompletion = undefined
|
|
1504
|
+
if (resolve) resolve()
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1476
1507
|
/**
|
|
1477
1508
|
* Runs commit transaction action.
|
|
1478
1509
|
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
@@ -1594,13 +1625,16 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1594
1625
|
async affectedRows(sql, options = {}) {
|
|
1595
1626
|
await this._waitForOperationLease(options.operationOwner)
|
|
1596
1627
|
this._assertWritableQuery(sql)
|
|
1597
|
-
await this.beforeQuery(sql, options)
|
|
1598
1628
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1629
|
+
return await coordinateSharedTransactionConnection(this, async () => {
|
|
1630
|
+
await this.beforeQuery(sql, options)
|
|
1631
|
+
|
|
1632
|
+
try {
|
|
1633
|
+
return await this._affectedRowsActual(sql)
|
|
1634
|
+
} finally {
|
|
1635
|
+
await this.afterQuery(sql, options)
|
|
1636
|
+
}
|
|
1637
|
+
}, options.operationOwner)
|
|
1604
1638
|
}
|
|
1605
1639
|
|
|
1606
1640
|
/**
|
|
@@ -1663,13 +1697,15 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1663
1697
|
* @returns {Promise<QueryResultType>} - Resolves with the query.
|
|
1664
1698
|
*/
|
|
1665
1699
|
async _queryActualWithHooks(sql, options) {
|
|
1666
|
-
await this
|
|
1700
|
+
return await coordinateSharedTransactionConnection(this, async () => {
|
|
1701
|
+
await this.beforeQuery(sql, options)
|
|
1667
1702
|
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1703
|
+
try {
|
|
1704
|
+
return await this._queryActual(sql, options)
|
|
1705
|
+
} finally {
|
|
1706
|
+
await this.afterQuery(sql, options)
|
|
1707
|
+
}
|
|
1708
|
+
}, options.operationOwner)
|
|
1673
1709
|
}
|
|
1674
1710
|
|
|
1675
1711
|
/**
|
|
@@ -2084,6 +2120,7 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2084
2120
|
await this._rollbackTransactionAction(options)
|
|
2085
2121
|
} finally {
|
|
2086
2122
|
this._transactionsCount--
|
|
2123
|
+
this._resolveCompletedTransaction()
|
|
2087
2124
|
|
|
2088
2125
|
// A rolled-back transaction may have reverted DDL (e.g. a CREATE TABLE
|
|
2089
2126
|
// run lazily inside the transaction), so any cached schema metadata is
|
|
@@ -2214,42 +2251,54 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2214
2251
|
await this.query(`ROLLBACK TO SAVEPOINT ${savePointName}`, options)
|
|
2215
2252
|
}
|
|
2216
2253
|
|
|
2254
|
+
/**
|
|
2255
|
+
* Truncates the given table snapshot. Drivers can override this to issue one batch.
|
|
2256
|
+
* @protected
|
|
2257
|
+
* @param {Array<import("./base-table.js").default>} tables - Eligible tables for this cleanup attempt.
|
|
2258
|
+
* @returns {Promise<void>} - Resolves when every table has been cleaned.
|
|
2259
|
+
*/
|
|
2260
|
+
async truncateTables(tables) {
|
|
2261
|
+
const truncateErrors = []
|
|
2262
|
+
|
|
2263
|
+
for (const table of tables) {
|
|
2264
|
+
try {
|
|
2265
|
+
await table.truncate({cascade: true})
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
truncateErrors.push(error)
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
if (truncateErrors.length > 0) throw truncateErrors[0]
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2217
2274
|
/**
|
|
2218
2275
|
* Runs truncate all tables.
|
|
2219
2276
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2220
2277
|
*/
|
|
2221
2278
|
async truncateAllTables() {
|
|
2222
2279
|
this._assertNotReadOnly()
|
|
2223
|
-
await this.
|
|
2224
|
-
let tries = 0
|
|
2280
|
+
let tables = (await this.getTables()).filter((table) => table.getName() != "schema_migrations")
|
|
2225
2281
|
|
|
2226
|
-
|
|
2227
|
-
tries++
|
|
2282
|
+
if (tables.length == 0) return
|
|
2228
2283
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2284
|
+
await this.withDisabledForeignKeys(async () => {
|
|
2285
|
+
for (let tries = 1; tries <= 6; tries++) {
|
|
2286
|
+
try {
|
|
2287
|
+
await this.truncateTables(tables)
|
|
2288
|
+
return
|
|
2289
|
+
} catch (error) {
|
|
2290
|
+
console.error(error)
|
|
2231
2291
|
|
|
2232
|
-
|
|
2233
|
-
if (table.getName() != "schema_migrations") {
|
|
2234
|
-
try {
|
|
2235
|
-
await table.truncate({cascade: true})
|
|
2236
|
-
} catch (error) {
|
|
2237
|
-
console.error(error)
|
|
2238
|
-
truncateErrors.push(error)
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2292
|
+
if (tries == 6) throw error
|
|
2242
2293
|
|
|
2243
|
-
if (truncateErrors.length == 0) {
|
|
2244
|
-
break
|
|
2245
|
-
} else if (tries <= 5) {
|
|
2246
2294
|
// A truncate failed — the schema cache may still list a table that was
|
|
2247
2295
|
// dropped out from under us (e.g. a db:rollback test that left the
|
|
2248
2296
|
// shared DB rolled back). Clear it so the next pass re-reads the live
|
|
2249
2297
|
// table list and no longer tries to truncate a table that is gone.
|
|
2250
2298
|
this.clearSchemaCache()
|
|
2251
|
-
|
|
2252
|
-
|
|
2299
|
+
tables = (await this.getTables()).filter((table) => table.getName() != "schema_migrations")
|
|
2300
|
+
|
|
2301
|
+
if (tables.length == 0) return
|
|
2253
2302
|
}
|
|
2254
2303
|
}
|
|
2255
2304
|
})
|
|
@@ -506,6 +506,38 @@ export default class VelociousDatabaseDriversMssql extends Base{
|
|
|
506
506
|
})
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Truncates all eligible tables in one SQL Server request, retaining the
|
|
511
|
+
* recognized foreign-key fallback used by the per-table implementation.
|
|
512
|
+
* @param {Array<import("../base-table.js").default>} tables - Eligible tables.
|
|
513
|
+
* @returns {Promise<void>} - Resolves when the batch completes.
|
|
514
|
+
*/
|
|
515
|
+
async truncateTables(tables) {
|
|
516
|
+
const statements = []
|
|
517
|
+
|
|
518
|
+
for (const table of tables) {
|
|
519
|
+
const quotedTable = this.quoteTable(table.getName())
|
|
520
|
+
|
|
521
|
+
statements.push(
|
|
522
|
+
"BEGIN TRY",
|
|
523
|
+
` TRUNCATE TABLE ${quotedTable};`,
|
|
524
|
+
"END TRY",
|
|
525
|
+
"BEGIN CATCH",
|
|
526
|
+
" IF ERROR_NUMBER() = 4712",
|
|
527
|
+
" BEGIN",
|
|
528
|
+
` DELETE FROM ${quotedTable};`,
|
|
529
|
+
" END",
|
|
530
|
+
" ELSE",
|
|
531
|
+
" BEGIN",
|
|
532
|
+
" THROW;",
|
|
533
|
+
" END",
|
|
534
|
+
"END CATCH;"
|
|
535
|
+
)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
await this.query(statements.join("\n"))
|
|
539
|
+
}
|
|
540
|
+
|
|
509
541
|
async lastInsertID(options = {}) {
|
|
510
542
|
const result = await this.query("SELECT SCOPE_IDENTITY() AS last_insert_id", options)
|
|
511
543
|
const lastInsertID = digg(result, 0, "last_insert_id")
|
|
@@ -486,6 +486,23 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
486
486
|
return true
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
/**
|
|
490
|
+
* Uses one multi-statement request only when the existing connection option
|
|
491
|
+
* explicitly allows it; otherwise retains the base sequential behavior.
|
|
492
|
+
* @param {Array<import("../base-table.js").default>} tables - Eligible tables.
|
|
493
|
+
* @returns {Promise<void>} - Resolves when every table has been truncated.
|
|
494
|
+
*/
|
|
495
|
+
async truncateTables(tables) {
|
|
496
|
+
if (!this.getArgs().multipleStatements) {
|
|
497
|
+
await super.truncateTables(tables)
|
|
498
|
+
return
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const statements = tables.map((table) => `TRUNCATE TABLE ${this.quoteTable(table.getName())}`)
|
|
502
|
+
|
|
503
|
+
await this.query(statements.join(";\n"))
|
|
504
|
+
}
|
|
505
|
+
|
|
489
506
|
/**
|
|
490
507
|
* Runs query to sql.
|
|
491
508
|
* @param {import("../../query/index.js").default} query - Query instance.
|
|
@@ -326,6 +326,17 @@ export default class VelociousDatabaseDriversPgsql extends Base{
|
|
|
326
326
|
})
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Truncates all eligible tables in one PostgreSQL request.
|
|
331
|
+
* @param {Array<import("../base-table.js").default>} tables - Eligible tables.
|
|
332
|
+
* @returns {Promise<void>} - Resolves when the batch completes.
|
|
333
|
+
*/
|
|
334
|
+
async truncateTables(tables) {
|
|
335
|
+
const quotedTables = tables.map((table) => this.quoteTable(table.getName()))
|
|
336
|
+
|
|
337
|
+
await this.query(`TRUNCATE TABLE ${quotedTables.join(", ")} CASCADE`)
|
|
338
|
+
}
|
|
339
|
+
|
|
329
340
|
async lastInsertID(options = {}) {
|
|
330
341
|
const result = await this.query("SELECT LASTVAL() AS last_insert_id", options)
|
|
331
342
|
|
|
@@ -161,6 +161,18 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
|
|
|
161
161
|
})
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Deletes every eligible table through the platform driver's native SQLite
|
|
166
|
+
* script path so the whole cleanup is submitted as one request.
|
|
167
|
+
* @param {Array<import("../base-table.js").default>} tables - Eligible tables.
|
|
168
|
+
* @returns {Promise<void>} - Resolves when the script completes.
|
|
169
|
+
*/
|
|
170
|
+
async truncateTables(tables) {
|
|
171
|
+
const statements = tables.map((table) => `DELETE FROM ${this.quoteTable(table.getName())}`)
|
|
172
|
+
|
|
173
|
+
await this.query(statements.join(";\n"), {sqliteScript: true})
|
|
174
|
+
}
|
|
175
|
+
|
|
164
176
|
/**
|
|
165
177
|
* Runs insert multiple.
|
|
166
178
|
* @param {string} tableName - Table name.
|
|
@@ -89,14 +89,15 @@ export default class VelociousDatabaseDriversSqliteConnectionSqlJs {
|
|
|
89
89
|
/**
|
|
90
90
|
* Runs query.
|
|
91
91
|
* @param {string} sql - SQL string.
|
|
92
|
+
* @param {{mutation?: boolean}} [options] - Internal query classification options.
|
|
92
93
|
* @returns {Promise<Record<string, ReturnType<typeof JSON.parse>>[]>} - Resolves with the query.
|
|
93
94
|
*/
|
|
94
|
-
async query(sql) {
|
|
95
|
+
async query(sql, {mutation = false} = {}) {
|
|
95
96
|
const result = await queryWeb(this.connection, sql)
|
|
96
97
|
const downcasedSQL = sql.toLowerCase().trim()
|
|
97
98
|
|
|
98
99
|
// Auto-save database in local storage in case we can find manipulating instructions in the SQL
|
|
99
|
-
if (downcasedSQL.startsWith("delete ") || downcasedSQL.startsWith("insert into ") || downcasedSQL.startsWith("update ")) {
|
|
100
|
+
if (mutation || downcasedSQL.startsWith("delete ") || downcasedSQL.startsWith("insert into ") || downcasedSQL.startsWith("update ")) {
|
|
100
101
|
this.saveDatabaseDebounce()
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -84,11 +84,17 @@ export default class VelociousDatabaseDriversSqliteNode extends Base {
|
|
|
84
84
|
/**
|
|
85
85
|
* Runs query actual.
|
|
86
86
|
* @param {string} sql - SQL string.
|
|
87
|
+
* @param {import("../base.js").QueryOptions} [options] - Query options.
|
|
87
88
|
* @returns {Promise<Record<string, ReturnType<typeof JSON.parse>>[]>} - Resolves with the query actual.
|
|
88
89
|
*/
|
|
89
|
-
async _queryActual(sql) {
|
|
90
|
+
async _queryActual(sql, options = {}) {
|
|
90
91
|
if (!this.connection) throw new Error("No connection")
|
|
91
92
|
|
|
93
|
+
if (options.sqliteScript) {
|
|
94
|
+
await this.connection.exec(sql)
|
|
95
|
+
return []
|
|
96
|
+
}
|
|
97
|
+
|
|
92
98
|
return await query(this.connection, sql)
|
|
93
99
|
}
|
|
94
100
|
|
|
@@ -82,13 +82,19 @@ export default class VelociousDatabaseDriversSqliteNative extends Base {
|
|
|
82
82
|
/**
|
|
83
83
|
* Runs query actual.
|
|
84
84
|
* @param {string} sql - SQL string.
|
|
85
|
+
* @param {import("../base.js").QueryOptions} [options] - Query options.
|
|
85
86
|
* @returns {Promise<Record<string, ReturnType<typeof JSON.parse>>[]>} - Query result rows.
|
|
86
87
|
*/
|
|
87
|
-
async _queryActual(sql) {
|
|
88
|
-
return await this._queryMutex.sync(() => {
|
|
88
|
+
async _queryActual(sql, options = {}) {
|
|
89
|
+
return await this._queryMutex.sync(async () => {
|
|
89
90
|
if (!this.connection) throw new Error("Not connected yet")
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
if (options.sqliteScript) {
|
|
93
|
+
await this.connection.execAsync(sql)
|
|
94
|
+
return []
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return await query(this.connection, sql)
|
|
92
98
|
})
|
|
93
99
|
}
|
|
94
100
|
|
|
@@ -178,13 +178,16 @@ export default class VelociousDatabaseDriversSqliteWeb extends Base {
|
|
|
178
178
|
/**
|
|
179
179
|
* Runs query actual.
|
|
180
180
|
* @param {string} sql - SQL string.
|
|
181
|
+
* @param {import("../base.js").QueryOptions} [options] - Query options.
|
|
181
182
|
* @returns {Promise<Record<string, ReturnType<typeof JSON.parse>>[]>} - Resolves with the query actual.
|
|
182
183
|
*/
|
|
183
|
-
async _queryActual(sql) {
|
|
184
|
-
const
|
|
184
|
+
async _queryActual(sql, options = {}) {
|
|
185
|
+
const connection = this.getConnection()
|
|
186
|
+
const result = connection instanceof ConnectionSqlJs
|
|
187
|
+
? await connection.query(sql, {mutation: options.sqliteScript === true})
|
|
188
|
+
: await connection.query(sql)
|
|
185
189
|
|
|
186
190
|
if (!Array.isArray(result)) {
|
|
187
|
-
const connection = this.getConnection()
|
|
188
191
|
const connectionName = connection?.constructor?.name || "UnknownConnection"
|
|
189
192
|
|
|
190
193
|
throw new Error(`Sqlite web connection ${connectionName} returned a non-array result: ${typeof result}`)
|