@remix-run/data-table 0.3.0 → 0.4.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.
- package/README.md +205 -79
- package/dist/cli.d.ts +54 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +53 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/lib/column.d.ts +1 -1
- package/dist/lib/column.d.ts.map +1 -1
- package/dist/lib/database/execution-context.d.ts +5 -5
- package/dist/lib/database/execution-context.d.ts.map +1 -1
- package/dist/lib/database/execution-context.js +1 -0
- package/dist/lib/database/helpers.js +6 -6
- package/dist/lib/database/query-execution.d.ts.map +1 -1
- package/dist/lib/database/query-execution.js +17 -17
- package/dist/lib/database/relations.js +5 -5
- package/dist/lib/database/write-lifecycle.d.ts +2 -2
- package/dist/lib/database/write-lifecycle.d.ts.map +1 -1
- package/dist/lib/database/write-lifecycle.js +5 -5
- package/dist/lib/database.d.ts +74 -59
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +176 -83
- package/dist/lib/{adapter.d.ts → driver.d.ts} +46 -48
- package/dist/lib/driver.d.ts.map +1 -0
- package/dist/lib/errors.d.ts +2 -2
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +4 -4
- package/dist/lib/migrations/journal-store.d.ts +6 -6
- package/dist/lib/migrations/journal-store.d.ts.map +1 -1
- package/dist/lib/migrations/journal-store.js +20 -11
- package/dist/lib/migrations/runner.d.ts +12 -19
- package/dist/lib/migrations/runner.d.ts.map +1 -1
- package/dist/lib/migrations/runner.js +151 -129
- package/dist/lib/migrations-node.d.ts +19 -1
- package/dist/lib/migrations-node.d.ts.map +1 -1
- package/dist/lib/migrations-node.js +22 -1
- package/dist/lib/migrations.d.ts +55 -20
- package/dist/lib/migrations.d.ts.map +1 -1
- package/dist/lib/operators.d.ts +1 -1
- package/dist/lib/operators.js +1 -1
- package/dist/lib/query.d.ts +1 -1
- package/dist/lib/query.d.ts.map +1 -1
- package/dist/lib/query.js +4 -4
- package/dist/lib/sql-helpers.d.ts +1 -1
- package/dist/lib/sql-helpers.d.ts.map +1 -1
- package/dist/lib/sql.d.ts +1 -1
- package/dist/lib/sql.js +1 -1
- package/dist/lib/table.d.ts +1 -1
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/lib/table.js +5 -5
- package/dist/migrations/node.d.ts +1 -1
- package/dist/migrations/node.d.ts.map +1 -1
- package/dist/migrations/node.js +1 -1
- package/dist/migrations.d.ts +1 -2
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -3
- package/dist/operators.js +1 -1
- package/dist/sql-helpers.js +1 -1
- package/package.json +13 -9
- package/src/cli.ts +120 -0
- package/src/index.ts +19 -20
- package/src/lib/column.ts +1 -1
- package/src/lib/database/execution-context.ts +10 -6
- package/src/lib/database/helpers.ts +1 -1
- package/src/lib/database/query-execution.ts +15 -11
- package/src/lib/database/write-lifecycle.ts +4 -4
- package/src/lib/database.ts +223 -96
- package/src/lib/{adapter.ts → driver.ts} +48 -48
- package/src/lib/errors.ts +4 -4
- package/src/lib/migrations/journal-store.ts +21 -11
- package/src/lib/migrations/runner.ts +200 -147
- package/src/lib/migrations-node.ts +23 -1
- package/src/lib/migrations.ts +58 -19
- package/src/lib/operators.ts +1 -1
- package/src/lib/query.ts +1 -1
- package/src/lib/sql-helpers.ts +1 -1
- package/src/lib/sql.ts +1 -1
- package/src/lib/table.ts +1 -1
- package/src/migrations/node.ts +1 -1
- package/src/migrations.ts +3 -4
- package/dist/lib/adapter.d.ts.map +0 -1
- /package/dist/lib/{adapter.js → driver.js} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,37 +1,23 @@
|
|
|
1
1
|
export type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DatabaseCapabilities,
|
|
3
|
+
DatabaseDriver,
|
|
4
|
+
DataManipulationOperation,
|
|
4
5
|
DataManipulationRequest,
|
|
5
6
|
ColumnCheck,
|
|
6
7
|
ColumnComputed,
|
|
7
8
|
ColumnDefault,
|
|
8
9
|
ColumnDefinition,
|
|
9
10
|
ColumnTypeName,
|
|
10
|
-
CountOperation,
|
|
11
11
|
DataManipulationResult,
|
|
12
|
-
DataManipulationOperation,
|
|
13
|
-
DeleteOperation,
|
|
14
|
-
DatabaseAdapter,
|
|
15
|
-
ExistsOperation,
|
|
16
12
|
ForeignKeyAction,
|
|
17
13
|
IdentityOptions,
|
|
18
|
-
InsertManyOperation,
|
|
19
|
-
InsertOperation,
|
|
20
|
-
JoinClause,
|
|
21
|
-
JoinType,
|
|
22
|
-
RawOperation,
|
|
23
|
-
ReturningSelection,
|
|
24
|
-
SelectColumn,
|
|
25
|
-
SelectOperation,
|
|
26
14
|
TableRef,
|
|
27
15
|
TransactionOptions,
|
|
28
16
|
TransactionToken,
|
|
29
|
-
|
|
30
|
-
UpsertOperation,
|
|
31
|
-
} from './lib/adapter.ts'
|
|
17
|
+
} from './lib/driver.ts'
|
|
32
18
|
|
|
33
19
|
export {
|
|
34
|
-
|
|
20
|
+
DataTableDatabaseError,
|
|
35
21
|
DataTableConstraintError,
|
|
36
22
|
DataTableError,
|
|
37
23
|
DataTableQueryError,
|
|
@@ -145,6 +131,7 @@ export type {
|
|
|
145
131
|
CreateResultOptions,
|
|
146
132
|
CreateRowOptions,
|
|
147
133
|
DeleteManyOptions,
|
|
134
|
+
DatabaseOptions,
|
|
148
135
|
FindManyOptions,
|
|
149
136
|
FindOneOptions,
|
|
150
137
|
OrderByInput,
|
|
@@ -160,6 +147,18 @@ export type {
|
|
|
160
147
|
WriteRowResult,
|
|
161
148
|
WriteRowsResult,
|
|
162
149
|
} from './lib/database.ts'
|
|
163
|
-
export {
|
|
150
|
+
export { Database } from './lib/database.ts'
|
|
151
|
+
export type {
|
|
152
|
+
DatabaseMigrateOptions,
|
|
153
|
+
DatabaseMigrationStatusOptions,
|
|
154
|
+
DatabaseResetOptions,
|
|
155
|
+
MigrateResult,
|
|
156
|
+
MigrationDescriptor,
|
|
157
|
+
MigrationRegistry,
|
|
158
|
+
Migrations,
|
|
159
|
+
MigrationStatus,
|
|
160
|
+
MigrationStatusEntry,
|
|
161
|
+
Seed,
|
|
162
|
+
} from './lib/migrations.ts'
|
|
164
163
|
export type { AnyQuery } from './lib/query.ts'
|
|
165
164
|
export { Query, query } from './lib/query.ts'
|
package/src/lib/column.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ColumnDefinition, ForeignKeyAction, IdentityOptions, TableRef } from './
|
|
1
|
+
import type { ColumnDefinition, ForeignKeyAction, IdentityOptions, TableRef } from './driver.ts'
|
|
2
2
|
|
|
3
3
|
function toTableRef(name: string): TableRef {
|
|
4
4
|
let segments = name.split('.')
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
DatabaseCapabilities,
|
|
2
3
|
DataManipulationOperation,
|
|
3
4
|
DataManipulationResult,
|
|
4
|
-
|
|
5
|
-
} from '../
|
|
6
|
-
import type { Database } from '../database.ts'
|
|
5
|
+
TransactionOptions,
|
|
6
|
+
} from '../driver.ts'
|
|
7
7
|
|
|
8
8
|
export const executeOperation = Symbol('executeOperation')
|
|
9
|
+
export const runInTransaction = Symbol('runInTransaction')
|
|
9
10
|
|
|
10
|
-
export type QueryExecutionContext = {
|
|
11
|
-
|
|
11
|
+
export type QueryExecutionContext<dialect extends string = string> = {
|
|
12
|
+
capabilities: DatabaseCapabilities
|
|
12
13
|
now(): unknown
|
|
13
|
-
transaction<result>(callback: (database: Database) => Promise<result>): Promise<result>
|
|
14
14
|
[executeOperation](operation: DataManipulationOperation): Promise<DataManipulationResult>
|
|
15
|
+
[runInTransaction]<result>(
|
|
16
|
+
callback: (database: QueryExecutionContext<dialect>) => Promise<result>,
|
|
17
|
+
options?: TransactionOptions,
|
|
18
|
+
): Promise<result>
|
|
15
19
|
}
|
|
@@ -80,7 +80,7 @@ export function resolveCreateRowWhere<table extends AnyTable>(
|
|
|
80
80
|
throw new DataTableQueryError(
|
|
81
81
|
'create({ returnRow: true }) requires primary key values for table "' +
|
|
82
82
|
getTableName(table) +
|
|
83
|
-
'" when
|
|
83
|
+
'" when the database does not support RETURNING',
|
|
84
84
|
)
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
SelectOperation,
|
|
10
10
|
UpdateOperation,
|
|
11
11
|
UpsertOperation,
|
|
12
|
-
} from '../
|
|
12
|
+
} from '../driver.ts'
|
|
13
13
|
import { DataTableQueryError } from '../errors.ts'
|
|
14
14
|
import type { ReturningInput, WriteResult, WriteRowResult, WriteRowsResult } from '../database.ts'
|
|
15
15
|
import { normalizeWhereInput } from '../operators.ts'
|
|
@@ -18,7 +18,11 @@ import { cloneQueryState, querySnapshot } from '../query.ts'
|
|
|
18
18
|
import type { AnyTable } from '../table.ts'
|
|
19
19
|
import { getPrimaryKeyObject, getTableName } from '../table.ts'
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
executeOperation,
|
|
23
|
+
runInTransaction,
|
|
24
|
+
type QueryExecutionContext,
|
|
25
|
+
} from './execution-context.ts'
|
|
22
26
|
import {
|
|
23
27
|
buildPrimaryKeyPredicate,
|
|
24
28
|
hasScopedWriteModifiers,
|
|
@@ -244,7 +248,7 @@ async function executeInsert(
|
|
|
244
248
|
)
|
|
245
249
|
let returning = options?.returning
|
|
246
250
|
|
|
247
|
-
assertReturningCapability(database.
|
|
251
|
+
assertReturningCapability(database.capabilities, 'insert', returning)
|
|
248
252
|
|
|
249
253
|
if (returning) {
|
|
250
254
|
let operation: InsertOperation<AnyTable> = {
|
|
@@ -314,7 +318,7 @@ async function executeInsertMany(
|
|
|
314
318
|
}
|
|
315
319
|
|
|
316
320
|
let returning = options?.returning
|
|
317
|
-
assertReturningCapability(database.
|
|
321
|
+
assertReturningCapability(database.capabilities, 'insertMany', returning)
|
|
318
322
|
|
|
319
323
|
if (returning) {
|
|
320
324
|
let operation: InsertManyOperation<AnyTable> = {
|
|
@@ -371,7 +375,7 @@ async function executeUpdate(
|
|
|
371
375
|
options?: { returning?: ReturningInput<Record<string, unknown>>; touch?: boolean },
|
|
372
376
|
): Promise<WriteResult | WriteRowsResult<Record<string, unknown>>> {
|
|
373
377
|
let returning = options?.returning
|
|
374
|
-
assertReturningCapability(database.
|
|
378
|
+
assertReturningCapability(database.capabilities, 'update', returning)
|
|
375
379
|
let preparedChanges = prepareUpdateValues(
|
|
376
380
|
table,
|
|
377
381
|
changes as never,
|
|
@@ -386,7 +390,7 @@ async function executeUpdate(
|
|
|
386
390
|
let result: DataManipulationResult
|
|
387
391
|
|
|
388
392
|
if (hasScopedWriteModifiers(state)) {
|
|
389
|
-
result = await database
|
|
393
|
+
result = await database[runInTransaction](async (tx) => {
|
|
390
394
|
let primaryKeys = await loadPrimaryKeyRowsForScope(tx, table, state)
|
|
391
395
|
let primaryKeyPredicate = buildPrimaryKeyPredicate(table, primaryKeys)
|
|
392
396
|
|
|
@@ -448,7 +452,7 @@ async function executeDelete(
|
|
|
448
452
|
options?: { returning?: ReturningInput<Record<string, unknown>> },
|
|
449
453
|
): Promise<WriteResult | WriteRowsResult<Record<string, unknown>>> {
|
|
450
454
|
let returning = options?.returning
|
|
451
|
-
assertReturningCapability(database.
|
|
455
|
+
assertReturningCapability(database.capabilities, 'delete', returning)
|
|
452
456
|
let tableName = getTableName(table)
|
|
453
457
|
let deleteContext = {
|
|
454
458
|
tableName,
|
|
@@ -462,7 +466,7 @@ async function executeDelete(
|
|
|
462
466
|
let result: DataManipulationResult
|
|
463
467
|
|
|
464
468
|
if (hasScopedWriteModifiers(state)) {
|
|
465
|
-
result = await database
|
|
469
|
+
result = await database[runInTransaction](async (tx) => {
|
|
466
470
|
let primaryKeys = await loadPrimaryKeyRowsForScope(tx, table, state)
|
|
467
471
|
let primaryKeyPredicate = buildPrimaryKeyPredicate(table, primaryKeys)
|
|
468
472
|
|
|
@@ -527,8 +531,8 @@ async function executeUpsert(
|
|
|
527
531
|
update?: Record<string, unknown>
|
|
528
532
|
},
|
|
529
533
|
): Promise<WriteResult | WriteRowResult<Record<string, unknown>>> {
|
|
530
|
-
if (!database.
|
|
531
|
-
throw new DataTableQueryError('
|
|
534
|
+
if (!database.capabilities.upsert) {
|
|
535
|
+
throw new DataTableQueryError('Database does not support upsert')
|
|
532
536
|
}
|
|
533
537
|
|
|
534
538
|
let preparedValues = prepareInsertValues(
|
|
@@ -547,7 +551,7 @@ async function executeUpsert(
|
|
|
547
551
|
)
|
|
548
552
|
: undefined
|
|
549
553
|
let returning = options?.returning
|
|
550
|
-
assertReturningCapability(database.
|
|
554
|
+
assertReturningCapability(database.capabilities, 'upsert', returning)
|
|
551
555
|
|
|
552
556
|
if (returning) {
|
|
553
557
|
let operation: UpsertOperation<AnyTable> = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DatabaseCapabilities, ReturningSelection } from '../driver.ts'
|
|
2
2
|
import { DataTableQueryError, DataTableValidationError } from '../errors.ts'
|
|
3
3
|
import type { ReturningInput } from '../database.ts'
|
|
4
4
|
import type {
|
|
@@ -249,12 +249,12 @@ export function runAfterDeleteHook<table extends AnyTable>(
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
export function assertReturningCapability<row extends Record<string, unknown>>(
|
|
252
|
-
|
|
252
|
+
capabilities: DatabaseCapabilities,
|
|
253
253
|
operation: 'insert' | 'insertMany' | 'update' | 'delete' | 'upsert',
|
|
254
254
|
returning: ReturningInput<row> | undefined,
|
|
255
255
|
): void {
|
|
256
|
-
if (returning && !
|
|
257
|
-
throw new DataTableQueryError(operation + '() returning is not supported by this
|
|
256
|
+
if (returning && !capabilities.returning) {
|
|
257
|
+
throw new DataTableQueryError(operation + '() returning is not supported by this database')
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
package/src/lib/database.ts
CHANGED
|
@@ -2,13 +2,19 @@ import type {
|
|
|
2
2
|
ColumnDefinition,
|
|
3
3
|
DataManipulationOperation,
|
|
4
4
|
DataManipulationResult,
|
|
5
|
-
|
|
5
|
+
DatabaseCapabilities,
|
|
6
|
+
DatabaseDriver,
|
|
7
|
+
TableRef,
|
|
6
8
|
TransactionOptions,
|
|
7
9
|
TransactionToken,
|
|
8
|
-
} from './
|
|
10
|
+
} from './driver.ts'
|
|
9
11
|
import type { ColumnBuilder } from './column.ts'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { DataTableDatabaseError, DataTableQueryError } from './errors.ts'
|
|
13
|
+
import {
|
|
14
|
+
executeOperation,
|
|
15
|
+
runInTransaction,
|
|
16
|
+
type QueryExecutionContext,
|
|
17
|
+
} from './database/execution-context.ts'
|
|
12
18
|
import {
|
|
13
19
|
asQueryTableInput,
|
|
14
20
|
getPrimaryKeyWhere,
|
|
@@ -26,6 +32,15 @@ import type {
|
|
|
26
32
|
} from './query.ts'
|
|
27
33
|
import { bindQueryRuntime, query as createQuery } from './query.ts'
|
|
28
34
|
import type { ColumnInput, NormalizeColumnInput, TableMetadataLike } from './references.ts'
|
|
35
|
+
import type {
|
|
36
|
+
DatabaseMigrateOptions,
|
|
37
|
+
DatabaseMigrationStatusOptions,
|
|
38
|
+
DatabaseResetOptions,
|
|
39
|
+
MigrateResult,
|
|
40
|
+
Migrations,
|
|
41
|
+
MigrationStatusEntry,
|
|
42
|
+
} from './migrations.ts'
|
|
43
|
+
import { createMigrationRunner } from './migrations/runner.ts'
|
|
29
44
|
import type { SqlStatement } from './sql.ts'
|
|
30
45
|
import { isSqlStatement, rawSql } from './sql.ts'
|
|
31
46
|
import type {
|
|
@@ -327,7 +342,9 @@ type SavepointCounter = {
|
|
|
327
342
|
value: number
|
|
328
343
|
}
|
|
329
344
|
|
|
330
|
-
|
|
345
|
+
/** Options shared by database instances. */
|
|
346
|
+
export interface DatabaseOptions {
|
|
347
|
+
/** Clock function used for auto-managed timestamps. */
|
|
331
348
|
now?: () => unknown
|
|
332
349
|
}
|
|
333
350
|
|
|
@@ -336,39 +353,168 @@ type DatabaseInternalState = {
|
|
|
336
353
|
savepointCounter: SavepointCounter
|
|
337
354
|
}
|
|
338
355
|
|
|
339
|
-
const createInternalDatabase = Symbol('createInternalDatabase')
|
|
340
|
-
|
|
341
356
|
/**
|
|
342
|
-
* High-level database runtime used to
|
|
357
|
+
* High-level database runtime used to query and manage a database.
|
|
343
358
|
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
359
|
+
* Database dialects extend this class and provide a {@link DatabaseDriver} to the constructor.
|
|
360
|
+
* The driver owns SQL execution, transactions, and connection lifecycle while this class provides
|
|
361
|
+
* the shared query, persistence, and migration APIs.
|
|
346
362
|
*/
|
|
347
|
-
export class Database
|
|
348
|
-
#
|
|
363
|
+
export class Database<dialect extends string = string> {
|
|
364
|
+
#driver: DatabaseDriver<dialect>
|
|
365
|
+
#executionContext: QueryExecutionContext<dialect>
|
|
349
366
|
#token?: TransactionToken
|
|
350
367
|
#now: () => unknown
|
|
351
368
|
#savepointCounter: SavepointCounter
|
|
352
369
|
|
|
353
|
-
|
|
354
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Creates a database backed by a driver.
|
|
372
|
+
* @param driver Low-level database engine integration.
|
|
373
|
+
* @param options Database runtime options.
|
|
374
|
+
*/
|
|
375
|
+
constructor(driver: DatabaseDriver<dialect>, options?: DatabaseOptions) {
|
|
376
|
+
this.#driver = driver
|
|
355
377
|
this.#now = options?.now ?? defaultNow
|
|
356
378
|
this.#savepointCounter = { value: 0 }
|
|
379
|
+
|
|
380
|
+
let database = this
|
|
381
|
+
this.#executionContext = {
|
|
382
|
+
get capabilities() {
|
|
383
|
+
return database.capabilities
|
|
384
|
+
},
|
|
385
|
+
now() {
|
|
386
|
+
return database.now()
|
|
387
|
+
},
|
|
388
|
+
[executeOperation](operation) {
|
|
389
|
+
return database.#executeOperation(operation)
|
|
390
|
+
},
|
|
391
|
+
[runInTransaction](callback, transactionOptions) {
|
|
392
|
+
return database.#runInTransaction(
|
|
393
|
+
(transactionDatabase) => callback(transactionDatabase.#executionContext),
|
|
394
|
+
transactionOptions,
|
|
395
|
+
)
|
|
396
|
+
},
|
|
397
|
+
}
|
|
357
398
|
}
|
|
358
399
|
|
|
359
|
-
static
|
|
360
|
-
|
|
400
|
+
static #createInternalDatabase<dialect extends string>(
|
|
401
|
+
driver: DatabaseDriver<dialect>,
|
|
361
402
|
options: DatabaseOptions | undefined,
|
|
362
403
|
internal: DatabaseInternalState,
|
|
363
|
-
): Database {
|
|
364
|
-
let database = new Database(
|
|
404
|
+
): Database<dialect> {
|
|
405
|
+
let database = new Database(driver, options)
|
|
365
406
|
database.#token = internal.token
|
|
366
407
|
database.#savepointCounter = internal.savepointCounter
|
|
367
408
|
return database
|
|
368
409
|
}
|
|
369
410
|
|
|
370
|
-
|
|
371
|
-
|
|
411
|
+
/** Stable identifier for the SQL dialect. */
|
|
412
|
+
get dialect(): dialect {
|
|
413
|
+
return this.#driver.dialect
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Immutable feature flags used by shared query and migration behavior. */
|
|
417
|
+
get capabilities(): DatabaseCapabilities {
|
|
418
|
+
return this.#driver.capabilities
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Executes a migration or raw multi-statement SQL script.
|
|
423
|
+
* @param sql SQL script to execute.
|
|
424
|
+
* @returns A promise that resolves when execution completes.
|
|
425
|
+
*/
|
|
426
|
+
executeScript(sql: string): Promise<void> {
|
|
427
|
+
return this.#driver.executeScript(sql, this.#token)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Reports whether a table exists.
|
|
432
|
+
* @param table Table to inspect.
|
|
433
|
+
* @returns A promise that resolves to `true` when the table exists.
|
|
434
|
+
*/
|
|
435
|
+
hasTable(table: TableRef): Promise<boolean> {
|
|
436
|
+
return this.#driver.hasTable(table, this.#token)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Reports whether a column exists on a table.
|
|
441
|
+
* @param table Table to inspect.
|
|
442
|
+
* @param column Column name to inspect.
|
|
443
|
+
* @returns A promise that resolves to `true` when the column exists.
|
|
444
|
+
*/
|
|
445
|
+
hasColumn(table: TableRef, column: string): Promise<boolean> {
|
|
446
|
+
return this.#driver.hasColumn(table, column, this.#token)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Closes resources owned by this database.
|
|
451
|
+
* @returns A promise that resolves when owned resources have been released.
|
|
452
|
+
*/
|
|
453
|
+
async close(): Promise<void> {
|
|
454
|
+
this.#assertLifecycleOperationAllowed('close')
|
|
455
|
+
await this.#driver.close()
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Destructively recreates the configured database.
|
|
460
|
+
* @returns A promise that resolves when the database is ready for use.
|
|
461
|
+
*/
|
|
462
|
+
async wipe(): Promise<void> {
|
|
463
|
+
this.#assertLifecycleOperationAllowed('wipe')
|
|
464
|
+
await this.#driver.wipe()
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Applies or reverts migrations in order.
|
|
469
|
+
*
|
|
470
|
+
* @param migrations Migration descriptors or registry to apply.
|
|
471
|
+
* @param options Migration direction, bound, dry-run, and journal configuration.
|
|
472
|
+
* @returns The migrations applied or reverted by this run and their SQL scripts.
|
|
473
|
+
*/
|
|
474
|
+
async migrate(migrations: Migrations, options?: DatabaseMigrateOptions): Promise<MigrateResult> {
|
|
475
|
+
this.#assertLifecycleOperationAllowed('migrate')
|
|
476
|
+
let { direction = 'up', journalTable, ...migrateOptions } = options ?? {}
|
|
477
|
+
let runner = createMigrationRunner(this.#driver, migrations, { journalTable })
|
|
478
|
+
return direction === 'up' ? runner.up(migrateOptions) : runner.down(migrateOptions)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Reports the current state of the provided migrations.
|
|
483
|
+
*
|
|
484
|
+
* @param migrations Migration descriptors or registry to inspect.
|
|
485
|
+
* @param options Migration journal configuration.
|
|
486
|
+
* @returns Status entries for the provided migrations.
|
|
487
|
+
*/
|
|
488
|
+
async migrationStatus(
|
|
489
|
+
migrations: Migrations,
|
|
490
|
+
options: DatabaseMigrationStatusOptions = {},
|
|
491
|
+
): Promise<MigrationStatusEntry[]> {
|
|
492
|
+
this.#assertLifecycleOperationAllowed('migrationStatus')
|
|
493
|
+
let runner = createMigrationRunner(this.#driver, migrations, options)
|
|
494
|
+
return runner.status()
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Wipes the database, applies migrations, and optionally seeds data.
|
|
499
|
+
*
|
|
500
|
+
* @param options Migrations and optional seed function used to rebuild the database.
|
|
501
|
+
* @returns A promise that resolves when the database has been rebuilt.
|
|
502
|
+
*/
|
|
503
|
+
async reset(options: DatabaseResetOptions): Promise<void> {
|
|
504
|
+
this.#assertLifecycleOperationAllowed('reset')
|
|
505
|
+
await this.wipe()
|
|
506
|
+
await this.migrate(options.migrations, { journalTable: options.journalTable })
|
|
507
|
+
await options.seed?.(this)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
#assertLifecycleOperationAllowed(
|
|
511
|
+
method: 'close' | 'migrate' | 'migrationStatus' | 'reset' | 'wipe',
|
|
512
|
+
): void {
|
|
513
|
+
if (this.#token) {
|
|
514
|
+
throw new DataTableQueryError(
|
|
515
|
+
'Cannot call ' + method + '() from a transaction-scoped database',
|
|
516
|
+
)
|
|
517
|
+
}
|
|
372
518
|
}
|
|
373
519
|
|
|
374
520
|
now(): unknown {
|
|
@@ -423,7 +569,7 @@ export class Database implements QueryExecutionContext {
|
|
|
423
569
|
return toWriteResult(result)
|
|
424
570
|
}
|
|
425
571
|
|
|
426
|
-
if (this
|
|
572
|
+
if (this.capabilities.returning) {
|
|
427
573
|
let result = (await query.insert(values, {
|
|
428
574
|
returning: '*',
|
|
429
575
|
touch,
|
|
@@ -485,9 +631,9 @@ export class Database implements QueryExecutionContext {
|
|
|
485
631
|
let query: QueryForTable<table> = this.query(asQueryTableInput(table))
|
|
486
632
|
|
|
487
633
|
if (options?.returnRows === true) {
|
|
488
|
-
if (!this
|
|
634
|
+
if (!this.capabilities.returning) {
|
|
489
635
|
throw new DataTableQueryError(
|
|
490
|
-
'createMany({ returnRows: true }) is not supported by this
|
|
636
|
+
'createMany({ returnRows: true }) is not supported by this database',
|
|
491
637
|
)
|
|
492
638
|
}
|
|
493
639
|
|
|
@@ -616,7 +762,7 @@ export class Database implements QueryExecutionContext {
|
|
|
616
762
|
): Promise<TableRowWith<table, LoadedRelationMap<relations>>> {
|
|
617
763
|
let where = getPrimaryKeyWhere(table, value)
|
|
618
764
|
|
|
619
|
-
if (this
|
|
765
|
+
if (this.capabilities.returning) {
|
|
620
766
|
let updateResult = (await this.query(asQueryTableInput(table)).where(where).update(changes, {
|
|
621
767
|
touch: options?.touch,
|
|
622
768
|
returning: '*',
|
|
@@ -729,23 +875,30 @@ export class Database implements QueryExecutionContext {
|
|
|
729
875
|
? statementOrInput
|
|
730
876
|
: rawSql(statementOrInput, values)
|
|
731
877
|
|
|
732
|
-
return this
|
|
878
|
+
return this.#executeOperation({
|
|
733
879
|
kind: 'raw',
|
|
734
880
|
sql: sqlStatement,
|
|
735
881
|
})
|
|
736
882
|
}
|
|
737
883
|
|
|
738
|
-
return executeQuery(this, statementOrInput)
|
|
884
|
+
return executeQuery(this.#executionContext, statementOrInput)
|
|
739
885
|
}
|
|
740
886
|
|
|
741
887
|
async transaction<result>(
|
|
742
|
-
callback: (database: Database) => Promise<result>,
|
|
888
|
+
callback: (database: Database<dialect>) => Promise<result>,
|
|
889
|
+
options?: TransactionOptions,
|
|
890
|
+
): Promise<result> {
|
|
891
|
+
return this.#runInTransaction(callback, options)
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
async #runInTransaction<result>(
|
|
895
|
+
callback: (database: Database<dialect>) => Promise<result>,
|
|
743
896
|
options?: TransactionOptions,
|
|
744
897
|
): Promise<result> {
|
|
745
898
|
if (!this.#token) {
|
|
746
|
-
let token = await this.#
|
|
747
|
-
let tx = Database
|
|
748
|
-
this.#
|
|
899
|
+
let token = await this.#driver.beginTransaction(options)
|
|
900
|
+
let tx = Database.#createInternalDatabase(
|
|
901
|
+
this.#driver,
|
|
749
902
|
{ now: this.#now },
|
|
750
903
|
{
|
|
751
904
|
token,
|
|
@@ -753,47 +906,71 @@ export class Database implements QueryExecutionContext {
|
|
|
753
906
|
},
|
|
754
907
|
)
|
|
755
908
|
|
|
909
|
+
let result: result
|
|
756
910
|
try {
|
|
757
|
-
|
|
758
|
-
await this.#adapter.commitTransaction(token)
|
|
759
|
-
return result
|
|
911
|
+
result = await callback(tx)
|
|
760
912
|
} catch (error) {
|
|
761
|
-
|
|
913
|
+
try {
|
|
914
|
+
await this.#driver.rollbackTransaction(token)
|
|
915
|
+
} catch (rollbackError) {
|
|
916
|
+
throw new AggregateError(
|
|
917
|
+
[error, rollbackError],
|
|
918
|
+
'Database transaction and rollback both failed',
|
|
919
|
+
{ cause: error },
|
|
920
|
+
)
|
|
921
|
+
}
|
|
762
922
|
throw error
|
|
763
923
|
}
|
|
924
|
+
|
|
925
|
+
await this.#driver.commitTransaction(token)
|
|
926
|
+
return result
|
|
764
927
|
}
|
|
765
928
|
|
|
766
|
-
if (!this
|
|
767
|
-
throw new DataTableQueryError('Nested transactions require
|
|
929
|
+
if (!this.capabilities.savepoints) {
|
|
930
|
+
throw new DataTableQueryError('Nested transactions require database savepoint support')
|
|
768
931
|
}
|
|
769
932
|
|
|
770
933
|
let savepointName = 'sp_' + String(this.#savepointCounter.value)
|
|
771
934
|
this.#savepointCounter.value += 1
|
|
772
935
|
|
|
773
|
-
await this.#
|
|
936
|
+
await this.#driver.createSavepoint(this.#token, savepointName)
|
|
774
937
|
|
|
938
|
+
let result: result
|
|
775
939
|
try {
|
|
776
|
-
|
|
777
|
-
await this.#adapter.releaseSavepoint(this.#token, savepointName)
|
|
778
|
-
return result
|
|
940
|
+
result = await callback(this)
|
|
779
941
|
} catch (error) {
|
|
780
|
-
|
|
781
|
-
|
|
942
|
+
let failures: unknown[] = [error]
|
|
943
|
+
try {
|
|
944
|
+
await this.#driver.rollbackToSavepoint(this.#token, savepointName)
|
|
945
|
+
} catch (rollbackError) {
|
|
946
|
+
failures.push(rollbackError)
|
|
947
|
+
}
|
|
948
|
+
try {
|
|
949
|
+
await this.#driver.releaseSavepoint(this.#token, savepointName)
|
|
950
|
+
} catch (releaseError) {
|
|
951
|
+
failures.push(releaseError)
|
|
952
|
+
}
|
|
953
|
+
if (failures.length > 1) {
|
|
954
|
+
throw new AggregateError(failures, 'Nested transaction cleanup failed', { cause: error })
|
|
955
|
+
}
|
|
782
956
|
throw error
|
|
783
957
|
}
|
|
958
|
+
|
|
959
|
+
await this.#driver.releaseSavepoint(this.#token, savepointName)
|
|
960
|
+
return result
|
|
784
961
|
}
|
|
785
962
|
|
|
786
|
-
async
|
|
963
|
+
async #executeOperation(operation: DataManipulationOperation): Promise<DataManipulationResult> {
|
|
787
964
|
try {
|
|
788
|
-
return await this.#
|
|
965
|
+
return await this.#driver.execute({
|
|
789
966
|
operation,
|
|
790
967
|
transaction: this.#token,
|
|
791
968
|
})
|
|
792
969
|
} catch (error) {
|
|
793
|
-
throw new
|
|
970
|
+
throw new DataTableDatabaseError('Database execution failed', {
|
|
794
971
|
cause: error,
|
|
795
972
|
metadata: {
|
|
796
|
-
dialect: this
|
|
973
|
+
dialect: this.dialect,
|
|
797
974
|
operationKind: operation.kind,
|
|
798
975
|
},
|
|
799
976
|
})
|
|
@@ -801,56 +978,6 @@ export class Database implements QueryExecutionContext {
|
|
|
801
978
|
}
|
|
802
979
|
}
|
|
803
980
|
|
|
804
|
-
/**
|
|
805
|
-
* Creates a database runtime from an adapter.
|
|
806
|
-
* Thin wrapper around `new Database(adapter, options)`.
|
|
807
|
-
* @param adapter Adapter implementation responsible for SQL execution.
|
|
808
|
-
* @param options Optional runtime options.
|
|
809
|
-
* @param options.now Clock function used for auto-managed timestamps.
|
|
810
|
-
* @returns A {@link Database} API instance.
|
|
811
|
-
* @example
|
|
812
|
-
* ```ts
|
|
813
|
-
* import { column as c, createDatabase, table } from 'remix/data-table'
|
|
814
|
-
*
|
|
815
|
-
* let users = table({
|
|
816
|
-
* name: 'users',
|
|
817
|
-
* columns: {
|
|
818
|
-
* id: c.integer(),
|
|
819
|
-
* email: c.varchar(255),
|
|
820
|
-
* },
|
|
821
|
-
* })
|
|
822
|
-
*
|
|
823
|
-
* let db = createDatabase(adapter)
|
|
824
|
-
* let rows = await db.query(users).where({ id: 1 }).all()
|
|
825
|
-
* ```
|
|
826
|
-
*/
|
|
827
|
-
export function createDatabase(
|
|
828
|
-
adapter: DatabaseAdapter,
|
|
829
|
-
options?: { now?: () => unknown },
|
|
830
|
-
): Database {
|
|
831
|
-
return new Database(adapter, options)
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
/**
|
|
835
|
-
* Creates a database runtime bound to an existing adapter transaction token.
|
|
836
|
-
* This is an internal helper used by the migration runner.
|
|
837
|
-
* @param adapter Adapter implementation responsible for SQL execution.
|
|
838
|
-
* @param token Active adapter transaction token.
|
|
839
|
-
* @param options Optional runtime options.
|
|
840
|
-
* @param options.now Clock function used for auto-managed timestamps.
|
|
841
|
-
* @returns A {@link Database} API instance bound to the provided transaction.
|
|
842
|
-
*/
|
|
843
|
-
export function createDatabaseWithTransaction(
|
|
844
|
-
adapter: DatabaseAdapter,
|
|
845
|
-
token: TransactionToken,
|
|
846
|
-
options?: { now?: () => unknown },
|
|
847
|
-
): Database {
|
|
848
|
-
return Database[createInternalDatabase](adapter, options, {
|
|
849
|
-
token,
|
|
850
|
-
savepointCounter: { value: 0 },
|
|
851
|
-
})
|
|
852
|
-
}
|
|
853
|
-
|
|
854
981
|
function defaultNow(): Date {
|
|
855
982
|
return new Date()
|
|
856
983
|
}
|