@remix-run/data-table 0.1.0 → 0.2.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 +306 -55
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/lib/adapter.d.ts +386 -16
- package/dist/lib/adapter.d.ts.map +1 -1
- package/dist/lib/column.d.ts +193 -0
- package/dist/lib/column.d.ts.map +1 -0
- package/dist/lib/column.js +302 -0
- package/dist/lib/database/execution-context.d.ts +10 -0
- package/dist/lib/database/execution-context.d.ts.map +1 -0
- package/dist/lib/database/execution-context.js +1 -0
- package/dist/lib/database/helpers.d.ts +26 -0
- package/dist/lib/database/helpers.d.ts.map +1 -0
- package/dist/lib/database/helpers.js +116 -0
- package/dist/lib/database/query-execution.d.ts +7 -0
- package/dist/lib/database/query-execution.d.ts.map +1 -0
- package/dist/lib/database/query-execution.js +401 -0
- package/dist/lib/database/relations.d.ts +4 -0
- package/dist/lib/database/relations.d.ts.map +1 -0
- package/dist/lib/database/relations.js +207 -0
- package/dist/lib/database/write-lifecycle.d.ts +13 -0
- package/dist/lib/database/write-lifecycle.d.ts.map +1 -0
- package/dist/lib/database/write-lifecycle.js +279 -0
- package/dist/lib/database.d.ts +141 -238
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +73 -1122
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +9 -0
- package/dist/lib/migrations/filename.d.ts +12 -0
- package/dist/lib/migrations/filename.d.ts.map +1 -0
- package/dist/lib/migrations/filename.js +20 -0
- package/dist/lib/migrations/helpers.d.ts +11 -0
- package/dist/lib/migrations/helpers.d.ts.map +1 -0
- package/dist/lib/migrations/helpers.js +77 -0
- package/dist/lib/migrations/journal-store.d.ts +15 -0
- package/dist/lib/migrations/journal-store.d.ts.map +1 -0
- package/dist/lib/migrations/journal-store.js +83 -0
- package/dist/lib/migrations/registry.d.ts +27 -0
- package/dist/lib/migrations/registry.d.ts.map +1 -0
- package/dist/lib/migrations/registry.js +51 -0
- package/dist/lib/migrations/runner.d.ts +20 -0
- package/dist/lib/migrations/runner.d.ts.map +1 -0
- package/dist/lib/migrations/runner.js +273 -0
- package/dist/lib/migrations/schema-api.d.ts +7 -0
- package/dist/lib/migrations/schema-api.d.ts.map +1 -0
- package/dist/lib/migrations/schema-api.js +326 -0
- package/dist/lib/migrations-node.d.ts +17 -0
- package/dist/lib/migrations-node.d.ts.map +1 -0
- package/dist/lib/migrations-node.js +65 -0
- package/dist/lib/migrations.d.ts +292 -0
- package/dist/lib/migrations.d.ts.map +1 -0
- package/dist/lib/migrations.js +38 -0
- package/dist/lib/operators.d.ts +3 -0
- package/dist/lib/operators.d.ts.map +1 -1
- package/dist/lib/query.d.ts +159 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +401 -0
- package/dist/lib/references.d.ts +0 -1
- package/dist/lib/references.d.ts.map +1 -1
- package/dist/lib/sql-helpers.d.ts +50 -0
- package/dist/lib/sql-helpers.d.ts.map +1 -0
- package/dist/lib/sql-helpers.js +111 -0
- package/dist/lib/sql.d.ts +23 -6
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/sql.js +19 -5
- package/dist/lib/table.d.ts +355 -40
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/lib/table.js +113 -90
- package/dist/migrations/node.d.ts +2 -0
- package/dist/migrations/node.d.ts.map +1 -0
- package/dist/migrations/node.js +1 -0
- package/dist/migrations.d.ts +8 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +5 -0
- package/dist/operators.d.ts +3 -0
- package/dist/operators.d.ts.map +1 -0
- package/dist/operators.js +1 -0
- package/dist/sql-helpers.d.ts +3 -0
- package/dist/sql-helpers.d.ts.map +1 -0
- package/dist/sql-helpers.js +1 -0
- package/package.json +23 -8
- package/src/index.ts +93 -10
- package/src/lib/adapter.ts +469 -25
- package/src/lib/column.ts +384 -0
- package/src/lib/database/execution-context.ts +15 -0
- package/src/lib/database/helpers.ts +216 -0
- package/src/lib/database/query-execution.ts +638 -0
- package/src/lib/database/relations.ts +332 -0
- package/src/lib/database/write-lifecycle.ts +487 -0
- package/src/lib/database.ts +246 -1848
- package/src/lib/errors.ts +10 -0
- package/src/lib/migrations/filename.ts +25 -0
- package/src/lib/migrations/helpers.ts +108 -0
- package/src/lib/migrations/journal-store.ts +122 -0
- package/src/lib/migrations/registry.ts +62 -0
- package/src/lib/migrations/runner.ts +374 -0
- package/src/lib/migrations/schema-api.ts +417 -0
- package/src/lib/migrations-node.ts +71 -0
- package/src/lib/migrations.ts +328 -0
- package/src/lib/operators.ts +3 -0
- package/src/lib/query.ts +958 -0
- package/src/lib/references.ts +0 -1
- package/src/lib/sql-helpers.ts +146 -0
- package/src/lib/sql.ts +23 -6
- package/src/lib/table.ts +484 -156
- package/src/migrations/node.ts +1 -0
- package/src/migrations.ts +26 -0
- package/src/operators.ts +18 -0
- package/src/sql-helpers.ts +9 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import type { ColumnDefinition, ForeignKeyAction, IdentityOptions } from './adapter.ts'
|
|
2
|
+
import { toTableRef } from './migrations/helpers.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Chainable builder used to describe physical column definitions.
|
|
6
|
+
*/
|
|
7
|
+
export class ColumnBuilder<output = unknown> {
|
|
8
|
+
#definition: ColumnDefinition
|
|
9
|
+
|
|
10
|
+
constructor(definition: ColumnDefinition) {
|
|
11
|
+
this.#definition = definition
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Marks the column as nullable.
|
|
16
|
+
* @returns The column builder with `null` added to its output type.
|
|
17
|
+
*/
|
|
18
|
+
nullable(): ColumnBuilder<output | null> {
|
|
19
|
+
this.#definition.nullable = true
|
|
20
|
+
return this as unknown as ColumnBuilder<output | null>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Marks the column as non-nullable.
|
|
25
|
+
* @returns The column builder with `null` removed from its output type.
|
|
26
|
+
*/
|
|
27
|
+
notNull(): ColumnBuilder<Exclude<output, null>> {
|
|
28
|
+
this.#definition.nullable = false
|
|
29
|
+
return this as unknown as ColumnBuilder<Exclude<output, null>>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Sets a literal default value for the column.
|
|
34
|
+
* @param value Default value to apply when the column is omitted.
|
|
35
|
+
* @returns The column builder.
|
|
36
|
+
*/
|
|
37
|
+
default(value: unknown): ColumnBuilder<output> {
|
|
38
|
+
this.#definition.default = {
|
|
39
|
+
kind: 'literal',
|
|
40
|
+
value,
|
|
41
|
+
}
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sets the column default to the current timestamp at write time.
|
|
47
|
+
* @returns The column builder.
|
|
48
|
+
*/
|
|
49
|
+
defaultNow(): ColumnBuilder<output> {
|
|
50
|
+
this.#definition.default = {
|
|
51
|
+
kind: 'now',
|
|
52
|
+
}
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sets a raw SQL expression as the column default.
|
|
58
|
+
* @param expression SQL expression used as the default value.
|
|
59
|
+
* @returns The column builder.
|
|
60
|
+
*/
|
|
61
|
+
defaultSql(expression: string): ColumnBuilder<output> {
|
|
62
|
+
this.#definition.default = {
|
|
63
|
+
kind: 'sql',
|
|
64
|
+
expression,
|
|
65
|
+
}
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Marks the column as part of the primary key.
|
|
71
|
+
* @returns The column builder.
|
|
72
|
+
*/
|
|
73
|
+
primaryKey(): ColumnBuilder<output> {
|
|
74
|
+
this.#definition.primaryKey = true
|
|
75
|
+
return this
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Marks the column as unique.
|
|
80
|
+
* @param name Optional constraint name.
|
|
81
|
+
* @returns The column builder.
|
|
82
|
+
*/
|
|
83
|
+
unique(name?: string): ColumnBuilder<output> {
|
|
84
|
+
this.#definition.unique = name ? { name } : true
|
|
85
|
+
return this
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Adds a foreign-key reference for the column.
|
|
90
|
+
* @param table Referenced table name.
|
|
91
|
+
* @param name Constraint name.
|
|
92
|
+
* @returns The column builder.
|
|
93
|
+
*/
|
|
94
|
+
references(table: string, name: string): ColumnBuilder<output>
|
|
95
|
+
/**
|
|
96
|
+
* Adds a foreign-key reference for the column.
|
|
97
|
+
* @param table Referenced table name.
|
|
98
|
+
* @param columns Referenced column list.
|
|
99
|
+
* @param name Constraint name.
|
|
100
|
+
* @returns The column builder.
|
|
101
|
+
*/
|
|
102
|
+
references(table: string, columns: string | string[], name: string): ColumnBuilder<output>
|
|
103
|
+
references(
|
|
104
|
+
table: string,
|
|
105
|
+
columnsOrName: string | string[],
|
|
106
|
+
maybeName?: string,
|
|
107
|
+
): ColumnBuilder<output> {
|
|
108
|
+
let columns = maybeName === undefined ? 'id' : columnsOrName
|
|
109
|
+
let name = maybeName === undefined ? String(columnsOrName) : maybeName
|
|
110
|
+
|
|
111
|
+
this.#definition.references = {
|
|
112
|
+
table: toTableRef(table),
|
|
113
|
+
columns: Array.isArray(columns) ? [...columns] : [columns],
|
|
114
|
+
onDelete: this.#definition.references?.onDelete,
|
|
115
|
+
onUpdate: this.#definition.references?.onUpdate,
|
|
116
|
+
name,
|
|
117
|
+
}
|
|
118
|
+
return this
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Sets the foreign-key action used when the referenced row is deleted.
|
|
123
|
+
* @param action Delete action to apply.
|
|
124
|
+
* @returns The column builder.
|
|
125
|
+
*/
|
|
126
|
+
onDelete(action: ForeignKeyAction): ColumnBuilder<output> {
|
|
127
|
+
if (!this.#definition.references) {
|
|
128
|
+
throw new Error('onDelete() requires references() to be set first')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.#definition.references.onDelete = action
|
|
132
|
+
return this
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Sets the foreign-key action used when the referenced row is updated.
|
|
137
|
+
* @param action Update action to apply.
|
|
138
|
+
* @returns The column builder.
|
|
139
|
+
*/
|
|
140
|
+
onUpdate(action: ForeignKeyAction): ColumnBuilder<output> {
|
|
141
|
+
if (!this.#definition.references) {
|
|
142
|
+
throw new Error('onUpdate() requires references() to be set first')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.#definition.references.onUpdate = action
|
|
146
|
+
return this
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Adds a check constraint for the column.
|
|
151
|
+
* @param expression SQL check expression.
|
|
152
|
+
* @param name Constraint name.
|
|
153
|
+
* @returns The column builder.
|
|
154
|
+
*/
|
|
155
|
+
check(expression: string, name: string): ColumnBuilder<output> {
|
|
156
|
+
let checks = this.#definition.checks ?? []
|
|
157
|
+
checks.push({ expression, name })
|
|
158
|
+
this.#definition.checks = checks
|
|
159
|
+
return this
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Adds a database comment for the column.
|
|
164
|
+
* @param text Comment text.
|
|
165
|
+
* @returns The column builder.
|
|
166
|
+
*/
|
|
167
|
+
comment(text: string): ColumnBuilder<output> {
|
|
168
|
+
this.#definition.comment = text
|
|
169
|
+
return this
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Marks the column as computed from a SQL expression.
|
|
174
|
+
* @param expression SQL expression for the computed value.
|
|
175
|
+
* @param options Computed-column options.
|
|
176
|
+
* @param options.stored Whether the computed column should be stored instead of virtual.
|
|
177
|
+
* @returns The column builder.
|
|
178
|
+
*/
|
|
179
|
+
computed(expression: string, options?: { stored?: boolean }): ColumnBuilder<output> {
|
|
180
|
+
this.#definition.computed = {
|
|
181
|
+
expression,
|
|
182
|
+
stored: options?.stored ?? true,
|
|
183
|
+
}
|
|
184
|
+
return this
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Marks the column as unsigned when the dialect supports it.
|
|
189
|
+
* @returns The column builder.
|
|
190
|
+
*/
|
|
191
|
+
unsigned(): ColumnBuilder<output> {
|
|
192
|
+
this.#definition.unsigned = true
|
|
193
|
+
return this
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Marks the column as auto-incrementing when the dialect supports it.
|
|
198
|
+
* @returns The column builder.
|
|
199
|
+
*/
|
|
200
|
+
autoIncrement(): ColumnBuilder<output> {
|
|
201
|
+
this.#definition.autoIncrement = true
|
|
202
|
+
return this
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Configures an identity column strategy when the dialect supports it.
|
|
207
|
+
* @param options Identity sequence options.
|
|
208
|
+
* @returns The column builder.
|
|
209
|
+
*/
|
|
210
|
+
identity(options?: IdentityOptions): ColumnBuilder<output> {
|
|
211
|
+
this.#definition.identity = options ?? {}
|
|
212
|
+
return this
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Sets the collation for the column.
|
|
217
|
+
* @param name Collation name.
|
|
218
|
+
* @returns The column builder.
|
|
219
|
+
*/
|
|
220
|
+
collate(name: string): ColumnBuilder<output> {
|
|
221
|
+
this.#definition.collate = name
|
|
222
|
+
return this
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Sets the character set for the column.
|
|
227
|
+
* @param name Character set name.
|
|
228
|
+
* @returns The column builder.
|
|
229
|
+
*/
|
|
230
|
+
charset(name: string): ColumnBuilder<output> {
|
|
231
|
+
this.#definition.charset = name
|
|
232
|
+
return this
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Sets the column length.
|
|
237
|
+
* @param value Maximum length value.
|
|
238
|
+
* @returns The column builder.
|
|
239
|
+
*/
|
|
240
|
+
length(value: number): ColumnBuilder<output> {
|
|
241
|
+
this.#definition.length = value
|
|
242
|
+
return this
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Sets numeric precision and optional scale for the column.
|
|
247
|
+
* @param value Precision value.
|
|
248
|
+
* @param scale Optional scale value.
|
|
249
|
+
* @returns The column builder.
|
|
250
|
+
*/
|
|
251
|
+
precision(value: number, scale?: number): ColumnBuilder<output> {
|
|
252
|
+
this.#definition.precision = value
|
|
253
|
+
|
|
254
|
+
if (scale !== undefined) {
|
|
255
|
+
this.#definition.scale = scale
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return this
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Sets numeric scale for the column.
|
|
263
|
+
* @param value Scale value.
|
|
264
|
+
* @returns The column builder.
|
|
265
|
+
*/
|
|
266
|
+
scale(value: number): ColumnBuilder<output> {
|
|
267
|
+
this.#definition.scale = value
|
|
268
|
+
return this
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Enables or disables timezone support for time-based columns.
|
|
273
|
+
* @param enabled Whether timezone support should be enabled.
|
|
274
|
+
* @returns The column builder.
|
|
275
|
+
*/
|
|
276
|
+
timezone(enabled = true): ColumnBuilder<output> {
|
|
277
|
+
this.#definition.withTimezone = enabled
|
|
278
|
+
return this
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Builds the immutable column definition.
|
|
283
|
+
* @returns A normalized column definition.
|
|
284
|
+
*/
|
|
285
|
+
build(): ColumnDefinition {
|
|
286
|
+
return {
|
|
287
|
+
...this.#definition,
|
|
288
|
+
checks: this.#definition.checks ? [...this.#definition.checks] : undefined,
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Resolves the runtime output type from a column builder. */
|
|
294
|
+
export type ColumnOutput<column extends ColumnBuilder<any>> =
|
|
295
|
+
column extends ColumnBuilder<infer output> ? output : never
|
|
296
|
+
|
|
297
|
+
/** Input type accepted when writing values for a column builder. */
|
|
298
|
+
export type ColumnInput<column extends ColumnBuilder<any>> = ColumnOutput<column>
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Public constructor namespace for column builders.
|
|
302
|
+
*/
|
|
303
|
+
export type ColumnNamespace = {
|
|
304
|
+
varchar(length: number): ColumnBuilder<string>
|
|
305
|
+
text(): ColumnBuilder<string>
|
|
306
|
+
integer(): ColumnBuilder<number>
|
|
307
|
+
bigint(): ColumnBuilder
|
|
308
|
+
decimal(precision: number, scale: number): ColumnBuilder<number>
|
|
309
|
+
boolean(): ColumnBuilder<boolean>
|
|
310
|
+
uuid(): ColumnBuilder<string>
|
|
311
|
+
date(): ColumnBuilder
|
|
312
|
+
time(options?: { precision?: number; withTimezone?: boolean }): ColumnBuilder
|
|
313
|
+
timestamp(options?: { precision?: number; withTimezone?: boolean }): ColumnBuilder
|
|
314
|
+
json(): ColumnBuilder
|
|
315
|
+
binary(length?: number): ColumnBuilder
|
|
316
|
+
enum<values extends readonly string[]>(values: values): ColumnBuilder<values[number]>
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function createColumnBuilder<output = unknown>(
|
|
320
|
+
type: ColumnDefinition['type'],
|
|
321
|
+
): ColumnBuilder<output> {
|
|
322
|
+
return new ColumnBuilder({ type })
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Chainable column builder namespace.
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* import { column as c } from 'remix/data-table'
|
|
330
|
+
*
|
|
331
|
+
* let email = c.varchar(255).notNull().unique('users_email_uq')
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
export const column: ColumnNamespace = {
|
|
335
|
+
varchar(length: number) {
|
|
336
|
+
return new ColumnBuilder({ type: 'varchar', length })
|
|
337
|
+
},
|
|
338
|
+
text() {
|
|
339
|
+
return createColumnBuilder<string>('text')
|
|
340
|
+
},
|
|
341
|
+
integer() {
|
|
342
|
+
return createColumnBuilder<number>('integer')
|
|
343
|
+
},
|
|
344
|
+
bigint() {
|
|
345
|
+
return createColumnBuilder('bigint')
|
|
346
|
+
},
|
|
347
|
+
decimal(precision: number, scale: number) {
|
|
348
|
+
return new ColumnBuilder({ type: 'decimal', precision, scale })
|
|
349
|
+
},
|
|
350
|
+
boolean() {
|
|
351
|
+
return createColumnBuilder<boolean>('boolean')
|
|
352
|
+
},
|
|
353
|
+
uuid() {
|
|
354
|
+
return createColumnBuilder<string>('uuid')
|
|
355
|
+
},
|
|
356
|
+
date() {
|
|
357
|
+
return createColumnBuilder('date')
|
|
358
|
+
},
|
|
359
|
+
time(options?: { precision?: number; withTimezone?: boolean }) {
|
|
360
|
+
return new ColumnBuilder({
|
|
361
|
+
type: 'time',
|
|
362
|
+
precision: options?.precision,
|
|
363
|
+
withTimezone: options?.withTimezone,
|
|
364
|
+
})
|
|
365
|
+
},
|
|
366
|
+
timestamp(options?: { precision?: number; withTimezone?: boolean }) {
|
|
367
|
+
return new ColumnBuilder({
|
|
368
|
+
type: 'timestamp',
|
|
369
|
+
precision: options?.precision,
|
|
370
|
+
withTimezone: options?.withTimezone,
|
|
371
|
+
})
|
|
372
|
+
},
|
|
373
|
+
json() {
|
|
374
|
+
return createColumnBuilder('json')
|
|
375
|
+
},
|
|
376
|
+
binary(length?: number) {
|
|
377
|
+
return new ColumnBuilder({ type: 'binary', length })
|
|
378
|
+
},
|
|
379
|
+
enum<values extends readonly string[]>(values: values) {
|
|
380
|
+
return new ColumnBuilder({ type: 'enum', enumValues: [...values] }) as ColumnBuilder<
|
|
381
|
+
values[number]
|
|
382
|
+
>
|
|
383
|
+
},
|
|
384
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DataManipulationOperation,
|
|
3
|
+
DataManipulationResult,
|
|
4
|
+
DatabaseAdapter,
|
|
5
|
+
} from '../adapter.ts'
|
|
6
|
+
import type { Database } from '../database.ts'
|
|
7
|
+
|
|
8
|
+
export const executeOperation = Symbol('executeOperation')
|
|
9
|
+
|
|
10
|
+
export type QueryExecutionContext = {
|
|
11
|
+
adapter: DatabaseAdapter
|
|
12
|
+
now(): unknown
|
|
13
|
+
transaction<result>(callback: (database: Database) => Promise<result>): Promise<result>
|
|
14
|
+
[executeOperation](operation: DataManipulationOperation): Promise<DataManipulationResult>
|
|
15
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { DataTableQueryError } from '../errors.ts'
|
|
2
|
+
import type {
|
|
3
|
+
OrderByInput,
|
|
4
|
+
OrderByTuple,
|
|
5
|
+
QueryColumnName,
|
|
6
|
+
QueryColumns,
|
|
7
|
+
QueryColumnTypeMap,
|
|
8
|
+
QueryTableInput,
|
|
9
|
+
SingleTableWhere,
|
|
10
|
+
TableColumnName,
|
|
11
|
+
WriteResult,
|
|
12
|
+
WriteRowsResult,
|
|
13
|
+
} from '../database.ts'
|
|
14
|
+
import type { Predicate } from '../operators.ts'
|
|
15
|
+
import { and, eq, inList, or } from '../operators.ts'
|
|
16
|
+
import { query as createQuery } from '../query.ts'
|
|
17
|
+
import type { AnyTable, PrimaryKeyInput, TableName, TablePrimaryKey, TableRow } from '../table.ts'
|
|
18
|
+
import { getPrimaryKeyObject, getTableName, getTablePrimaryKey } from '../table.ts'
|
|
19
|
+
|
|
20
|
+
import type { QueryExecutionContext } from './execution-context.ts'
|
|
21
|
+
import { loadRowsWithRelationsForQuery } from './query-execution.ts'
|
|
22
|
+
|
|
23
|
+
export function asQueryTableInput<table extends AnyTable>(
|
|
24
|
+
table: table,
|
|
25
|
+
): QueryTableInput<TableName<table>, TableRow<table>, TablePrimaryKey<table>> {
|
|
26
|
+
return table as unknown as QueryTableInput<
|
|
27
|
+
TableName<table>,
|
|
28
|
+
TableRow<table>,
|
|
29
|
+
TablePrimaryKey<table>
|
|
30
|
+
>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getPrimaryKeyWhere<table extends AnyTable>(
|
|
34
|
+
table: table,
|
|
35
|
+
value: PrimaryKeyInput<table>,
|
|
36
|
+
): SingleTableWhere<table> {
|
|
37
|
+
return getPrimaryKeyObject(table, value as any) as SingleTableWhere<table>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getPrimaryKeyWhereFromRow<table extends AnyTable>(
|
|
41
|
+
table: table,
|
|
42
|
+
row: Record<string, unknown>,
|
|
43
|
+
): SingleTableWhere<table> {
|
|
44
|
+
let where: Record<string, unknown> = {}
|
|
45
|
+
|
|
46
|
+
for (let key of getTablePrimaryKey(table) as string[]) {
|
|
47
|
+
where[key] = row[key]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return where as SingleTableWhere<table>
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resolveCreateRowWhere<table extends AnyTable>(
|
|
54
|
+
table: table,
|
|
55
|
+
values: Partial<TableRow<table>>,
|
|
56
|
+
insertId: unknown,
|
|
57
|
+
): SingleTableWhere<table> {
|
|
58
|
+
let primaryKey = getTablePrimaryKey(table) as string[]
|
|
59
|
+
|
|
60
|
+
if (primaryKey.length === 1) {
|
|
61
|
+
let key = primaryKey[0]
|
|
62
|
+
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(values, key)) {
|
|
64
|
+
return {
|
|
65
|
+
[key]: (values as Record<string, unknown>)[key],
|
|
66
|
+
} as SingleTableWhere<table>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (insertId !== undefined) {
|
|
70
|
+
return {
|
|
71
|
+
[key]: insertId,
|
|
72
|
+
} as SingleTableWhere<table>
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let where: Record<string, unknown> = {}
|
|
77
|
+
|
|
78
|
+
for (let key of primaryKey) {
|
|
79
|
+
if (!Object.prototype.hasOwnProperty.call(values, key)) {
|
|
80
|
+
throw new DataTableQueryError(
|
|
81
|
+
'create({ returnRow: true }) requires primary key values for table "' +
|
|
82
|
+
getTableName(table) +
|
|
83
|
+
'" when adapter does not support RETURNING',
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
where[key] = (values as Record<string, unknown>)[key]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return where as SingleTableWhere<table>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function normalizeOrderByInput<table extends AnyTable>(
|
|
94
|
+
input: OrderByInput<table> | undefined,
|
|
95
|
+
): OrderByTuple<table>[] {
|
|
96
|
+
if (!input) {
|
|
97
|
+
return []
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (input.length === 0) {
|
|
101
|
+
return []
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (Array.isArray(input[0])) {
|
|
105
|
+
return input as OrderByTuple<table>[]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return [input as OrderByTuple<table>]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function toWriteResult(result: WriteResult | WriteRowsResult<unknown>): WriteResult {
|
|
112
|
+
return {
|
|
113
|
+
affectedRows: result.affectedRows,
|
|
114
|
+
insertId: result.insertId,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function hasScopedWriteModifiers(state: {
|
|
119
|
+
orderBy: unknown[]
|
|
120
|
+
limit?: number
|
|
121
|
+
offset?: number
|
|
122
|
+
}): boolean {
|
|
123
|
+
return state.orderBy.length > 0 || state.limit !== undefined || state.offset !== undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function loadPrimaryKeyRowsForScope<table extends AnyTable>(
|
|
127
|
+
database: QueryExecutionContext,
|
|
128
|
+
table: table,
|
|
129
|
+
state: {
|
|
130
|
+
where: Predicate<string>[]
|
|
131
|
+
orderBy: Array<{ column: string; direction: 'asc' | 'desc' }>
|
|
132
|
+
limit?: number
|
|
133
|
+
offset?: number
|
|
134
|
+
},
|
|
135
|
+
): Promise<Record<string, unknown>[]> {
|
|
136
|
+
let query = createQuery(
|
|
137
|
+
table as unknown as QueryTableInput<TableName<table>, TableRow<table>, TablePrimaryKey<table>>,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
for (let predicate of state.where) {
|
|
141
|
+
query = query.where(predicate as Predicate<QueryColumnName<table>>)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (let clause of state.orderBy) {
|
|
145
|
+
query = query.orderBy(
|
|
146
|
+
clause.column as QueryColumns<QueryColumnTypeMap<table>>,
|
|
147
|
+
clause.direction,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (state.limit !== undefined) {
|
|
152
|
+
query = query.limit(state.limit)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (state.offset !== undefined) {
|
|
156
|
+
query = query.offset(state.offset)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let rows = await loadRowsWithRelationsForQuery(
|
|
160
|
+
database,
|
|
161
|
+
query.select(...(getTablePrimaryKey(table) as (keyof TableRow<table> & string)[])),
|
|
162
|
+
)
|
|
163
|
+
let primaryKeys = getTablePrimaryKey(table) as string[]
|
|
164
|
+
|
|
165
|
+
return rows.map((row) => {
|
|
166
|
+
let keyObject: Record<string, unknown> = {}
|
|
167
|
+
|
|
168
|
+
for (let key of rowKeys(row as Record<string, unknown>, primaryKeys)) {
|
|
169
|
+
keyObject[key] = (row as Record<string, unknown>)[key]
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return keyObject
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function buildPrimaryKeyPredicate<table extends AnyTable>(
|
|
177
|
+
table: table,
|
|
178
|
+
keyObjects: Record<string, unknown>[],
|
|
179
|
+
): Predicate<TableColumnName<table>> | undefined {
|
|
180
|
+
let primaryKey = getTablePrimaryKey(table)
|
|
181
|
+
|
|
182
|
+
if (keyObjects.length === 0) {
|
|
183
|
+
return undefined
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (primaryKey.length === 1) {
|
|
187
|
+
let key = primaryKey[0] as TableColumnName<table>
|
|
188
|
+
return inList(
|
|
189
|
+
key,
|
|
190
|
+
keyObjects.map((objectValue) => objectValue[key]),
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let predicates = keyObjects.map((objectValue) => {
|
|
195
|
+
let comparisons = primaryKey.map((key) => {
|
|
196
|
+
let typedKey = key as TableColumnName<table>
|
|
197
|
+
return eq(typedKey, objectValue[typedKey])
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
return and(...comparisons)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
return or(...predicates)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function rowKeys(row: Record<string, unknown>, keys: string[]): string[] {
|
|
207
|
+
let output: string[] = []
|
|
208
|
+
|
|
209
|
+
for (let key of keys) {
|
|
210
|
+
if (Object.prototype.hasOwnProperty.call(row, key)) {
|
|
211
|
+
output.push(key)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return output
|
|
216
|
+
}
|