@remix-run/data-table 0.1.0 → 0.2.1
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 +307 -56
- 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 +24 -10
- 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,401 @@
|
|
|
1
|
+
import { DataTableQueryError, DataTableValidationError } from "./errors.js";
|
|
2
|
+
import { normalizeWhereInput } from "./operators.js";
|
|
3
|
+
import { normalizeColumnInput } from "./references.js";
|
|
4
|
+
import { getTableColumns, getTableName } from "./table.js";
|
|
5
|
+
export const bindQueryRuntime = Symbol('bindQueryRuntime');
|
|
6
|
+
export const querySnapshot = Symbol('querySnapshot');
|
|
7
|
+
export class Query {
|
|
8
|
+
#table;
|
|
9
|
+
#state;
|
|
10
|
+
#plan;
|
|
11
|
+
#runtime;
|
|
12
|
+
constructor(table) {
|
|
13
|
+
this.#table = table;
|
|
14
|
+
this.#state = createInitialQueryState();
|
|
15
|
+
this.#plan = { kind: 'all' };
|
|
16
|
+
}
|
|
17
|
+
static #createInternal(table, state, plan, runtime) {
|
|
18
|
+
let output = new Query(table);
|
|
19
|
+
output.#state = cloneQueryState(state);
|
|
20
|
+
output.#plan = cloneQueryPlan(plan);
|
|
21
|
+
output.#runtime = runtime;
|
|
22
|
+
return output;
|
|
23
|
+
}
|
|
24
|
+
select(...input) {
|
|
25
|
+
if (input.length === 1 &&
|
|
26
|
+
typeof input[0] === 'object' &&
|
|
27
|
+
input[0] !== null &&
|
|
28
|
+
!Array.isArray(input[0])) {
|
|
29
|
+
let selection = input[0];
|
|
30
|
+
let aliases = Object.keys(selection);
|
|
31
|
+
let select = aliases.map((alias) => ({
|
|
32
|
+
column: normalizeColumnInput(selection[alias]),
|
|
33
|
+
alias,
|
|
34
|
+
}));
|
|
35
|
+
return this.#clone({ select });
|
|
36
|
+
}
|
|
37
|
+
let columns = input;
|
|
38
|
+
return this.#clone({
|
|
39
|
+
select: columns.map((column) => ({ column, alias: column })),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
distinct(value = true) {
|
|
43
|
+
return this.#clone({ distinct: value });
|
|
44
|
+
}
|
|
45
|
+
where(input) {
|
|
46
|
+
let predicate = normalizeWhereInput(input);
|
|
47
|
+
let normalizedPredicate = normalizePredicateValues(predicate, createPredicateColumnResolver([this.#table, ...this.#state.joins.map((join) => join.table)]));
|
|
48
|
+
return this.#clone({
|
|
49
|
+
where: [...this.#state.where, normalizedPredicate],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
having(input) {
|
|
53
|
+
let predicate = normalizeWhereInput(input);
|
|
54
|
+
let normalizedPredicate = normalizePredicateValues(predicate, createPredicateColumnResolver([this.#table, ...this.#state.joins.map((join) => join.table)]));
|
|
55
|
+
return this.#clone({
|
|
56
|
+
having: [...this.#state.having, normalizedPredicate],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
join(target, on, type = 'inner') {
|
|
60
|
+
let normalizedOn = normalizePredicateValues(on, createPredicateColumnResolver([
|
|
61
|
+
this.#table,
|
|
62
|
+
...this.#state.joins.map((join) => join.table),
|
|
63
|
+
target,
|
|
64
|
+
]));
|
|
65
|
+
return this.#clone({
|
|
66
|
+
joins: [...this.#state.joins, { type, table: target, on: normalizedOn }],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
leftJoin(target, on) {
|
|
70
|
+
return this.join(target, on, 'left');
|
|
71
|
+
}
|
|
72
|
+
rightJoin(target, on) {
|
|
73
|
+
return this.join(target, on, 'right');
|
|
74
|
+
}
|
|
75
|
+
orderBy(column, direction = 'asc') {
|
|
76
|
+
return this.#clone({
|
|
77
|
+
orderBy: [...this.#state.orderBy, { column: normalizeColumnInput(column), direction }],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
groupBy(...columns) {
|
|
81
|
+
return this.#clone({
|
|
82
|
+
groupBy: [...this.#state.groupBy, ...columns.map((column) => normalizeColumnInput(column))],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
limit(value) {
|
|
86
|
+
return this.#clone({ limit: value });
|
|
87
|
+
}
|
|
88
|
+
offset(value) {
|
|
89
|
+
return this.#clone({ offset: value });
|
|
90
|
+
}
|
|
91
|
+
with(relations) {
|
|
92
|
+
return this.#clone({
|
|
93
|
+
with: {
|
|
94
|
+
...this.#state.with,
|
|
95
|
+
...relations,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
all() {
|
|
100
|
+
return this.#boundRuntime().exec(this);
|
|
101
|
+
}
|
|
102
|
+
first() {
|
|
103
|
+
return this.#resolveTerminal({ kind: 'first' });
|
|
104
|
+
}
|
|
105
|
+
find(value) {
|
|
106
|
+
return this.#resolveTerminal({ kind: 'find', value });
|
|
107
|
+
}
|
|
108
|
+
count() {
|
|
109
|
+
return this.#resolveTerminal({ kind: 'count' });
|
|
110
|
+
}
|
|
111
|
+
exists() {
|
|
112
|
+
return this.#resolveTerminal({ kind: 'exists' });
|
|
113
|
+
}
|
|
114
|
+
insert(values, options) {
|
|
115
|
+
assertWriteState(this.#state, 'insert', {
|
|
116
|
+
where: false,
|
|
117
|
+
orderBy: false,
|
|
118
|
+
limit: false,
|
|
119
|
+
offset: false,
|
|
120
|
+
});
|
|
121
|
+
return this.#resolveTerminal({ kind: 'insert', values, options });
|
|
122
|
+
}
|
|
123
|
+
insertMany(values, options) {
|
|
124
|
+
assertWriteState(this.#state, 'insertMany', {
|
|
125
|
+
where: false,
|
|
126
|
+
orderBy: false,
|
|
127
|
+
limit: false,
|
|
128
|
+
offset: false,
|
|
129
|
+
});
|
|
130
|
+
return this.#resolveTerminal({ kind: 'insertMany', values, options });
|
|
131
|
+
}
|
|
132
|
+
update(changes, options) {
|
|
133
|
+
assertWriteState(this.#state, 'update', {
|
|
134
|
+
where: true,
|
|
135
|
+
orderBy: true,
|
|
136
|
+
limit: true,
|
|
137
|
+
offset: true,
|
|
138
|
+
});
|
|
139
|
+
return this.#resolveTerminal({ kind: 'update', changes, options });
|
|
140
|
+
}
|
|
141
|
+
delete(options) {
|
|
142
|
+
assertWriteState(this.#state, 'delete', {
|
|
143
|
+
where: true,
|
|
144
|
+
orderBy: true,
|
|
145
|
+
limit: true,
|
|
146
|
+
offset: true,
|
|
147
|
+
});
|
|
148
|
+
return this.#resolveTerminal({ kind: 'delete', options });
|
|
149
|
+
}
|
|
150
|
+
upsert(values, options) {
|
|
151
|
+
assertWriteState(this.#state, 'upsert', {
|
|
152
|
+
where: false,
|
|
153
|
+
orderBy: false,
|
|
154
|
+
limit: false,
|
|
155
|
+
offset: false,
|
|
156
|
+
});
|
|
157
|
+
return this.#resolveTerminal({ kind: 'upsert', values, options });
|
|
158
|
+
}
|
|
159
|
+
[querySnapshot]() {
|
|
160
|
+
return this.#snapshot();
|
|
161
|
+
}
|
|
162
|
+
#resolveTerminal(plan) {
|
|
163
|
+
let next = this.#withPlan(plan);
|
|
164
|
+
return (this.#runtime ? this.#runtime.exec(next) : next);
|
|
165
|
+
}
|
|
166
|
+
[bindQueryRuntime](runtime) {
|
|
167
|
+
return Query.#createInternal(this.#table, this.#state, this.#plan, runtime);
|
|
168
|
+
}
|
|
169
|
+
#clone(patch) {
|
|
170
|
+
return Query.#createInternal(this.#table, {
|
|
171
|
+
select: patch.select ?? cloneSelection(this.#state.select),
|
|
172
|
+
distinct: patch.distinct ?? this.#state.distinct,
|
|
173
|
+
joins: patch.joins ? [...patch.joins] : [...this.#state.joins],
|
|
174
|
+
where: patch.where ? [...patch.where] : [...this.#state.where],
|
|
175
|
+
groupBy: patch.groupBy ? [...patch.groupBy] : [...this.#state.groupBy],
|
|
176
|
+
having: patch.having ? [...patch.having] : [...this.#state.having],
|
|
177
|
+
orderBy: patch.orderBy ? [...patch.orderBy] : [...this.#state.orderBy],
|
|
178
|
+
limit: patch.limit === undefined ? this.#state.limit : patch.limit,
|
|
179
|
+
offset: patch.offset === undefined ? this.#state.offset : patch.offset,
|
|
180
|
+
with: patch.with ? { ...patch.with } : { ...this.#state.with },
|
|
181
|
+
}, this.#plan, this.#runtime);
|
|
182
|
+
}
|
|
183
|
+
#withPlan(plan) {
|
|
184
|
+
return Query.#createInternal(this.#table, this.#state, plan, this.#runtime);
|
|
185
|
+
}
|
|
186
|
+
#snapshot() {
|
|
187
|
+
return {
|
|
188
|
+
table: this.#table,
|
|
189
|
+
state: cloneQueryState(this.#state),
|
|
190
|
+
plan: cloneQueryPlan(this.#plan),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
#boundRuntime() {
|
|
194
|
+
if (!this.#runtime) {
|
|
195
|
+
throw new DataTableQueryError('Use db.exec(query) to execute an unbound Query');
|
|
196
|
+
}
|
|
197
|
+
return this.#runtime;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export function query(table) {
|
|
201
|
+
return new Query(table);
|
|
202
|
+
}
|
|
203
|
+
export function cloneQueryState(state) {
|
|
204
|
+
return {
|
|
205
|
+
select: cloneSelection(state.select),
|
|
206
|
+
distinct: state.distinct,
|
|
207
|
+
joins: [...state.joins],
|
|
208
|
+
where: [...state.where],
|
|
209
|
+
groupBy: [...state.groupBy],
|
|
210
|
+
having: [...state.having],
|
|
211
|
+
orderBy: [...state.orderBy],
|
|
212
|
+
limit: state.limit,
|
|
213
|
+
offset: state.offset,
|
|
214
|
+
with: { ...state.with },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function createInitialQueryState() {
|
|
218
|
+
return {
|
|
219
|
+
select: '*',
|
|
220
|
+
distinct: false,
|
|
221
|
+
joins: [],
|
|
222
|
+
where: [],
|
|
223
|
+
groupBy: [],
|
|
224
|
+
having: [],
|
|
225
|
+
orderBy: [],
|
|
226
|
+
with: {},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function cloneQueryPlan(plan) {
|
|
230
|
+
switch (plan.kind) {
|
|
231
|
+
case 'all':
|
|
232
|
+
return { kind: 'all' };
|
|
233
|
+
case 'first':
|
|
234
|
+
return { kind: 'first' };
|
|
235
|
+
case 'find':
|
|
236
|
+
return {
|
|
237
|
+
kind: 'find',
|
|
238
|
+
value: clonePrimaryKeyValue(plan.value),
|
|
239
|
+
};
|
|
240
|
+
case 'count':
|
|
241
|
+
return { kind: 'count' };
|
|
242
|
+
case 'exists':
|
|
243
|
+
return { kind: 'exists' };
|
|
244
|
+
case 'insert':
|
|
245
|
+
return {
|
|
246
|
+
kind: 'insert',
|
|
247
|
+
values: { ...plan.values },
|
|
248
|
+
options: plan.options ? { ...plan.options } : undefined,
|
|
249
|
+
};
|
|
250
|
+
case 'insertMany':
|
|
251
|
+
return {
|
|
252
|
+
kind: 'insertMany',
|
|
253
|
+
values: plan.values.map((value) => ({ ...value })),
|
|
254
|
+
options: plan.options ? { ...plan.options } : undefined,
|
|
255
|
+
};
|
|
256
|
+
case 'update':
|
|
257
|
+
return {
|
|
258
|
+
kind: 'update',
|
|
259
|
+
changes: { ...plan.changes },
|
|
260
|
+
options: plan.options ? { ...plan.options } : undefined,
|
|
261
|
+
};
|
|
262
|
+
case 'delete':
|
|
263
|
+
return {
|
|
264
|
+
kind: 'delete',
|
|
265
|
+
options: plan.options ? { ...plan.options } : undefined,
|
|
266
|
+
};
|
|
267
|
+
case 'upsert':
|
|
268
|
+
return {
|
|
269
|
+
kind: 'upsert',
|
|
270
|
+
values: { ...plan.values },
|
|
271
|
+
options: plan.options
|
|
272
|
+
? {
|
|
273
|
+
...plan.options,
|
|
274
|
+
conflictTarget: plan.options.conflictTarget
|
|
275
|
+
? [...plan.options.conflictTarget]
|
|
276
|
+
: undefined,
|
|
277
|
+
update: plan.options.update ? { ...plan.options.update } : undefined,
|
|
278
|
+
}
|
|
279
|
+
: undefined,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function clonePrimaryKeyValue(value) {
|
|
284
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
return { ...value };
|
|
288
|
+
}
|
|
289
|
+
function cloneSelection(selection) {
|
|
290
|
+
if (selection === '*') {
|
|
291
|
+
return '*';
|
|
292
|
+
}
|
|
293
|
+
return selection.map((column) => ({ ...column }));
|
|
294
|
+
}
|
|
295
|
+
function assertWriteState(state, operation, policy) {
|
|
296
|
+
let unsupported = [];
|
|
297
|
+
if (state.select !== '*')
|
|
298
|
+
unsupported.push('select()');
|
|
299
|
+
if (state.distinct)
|
|
300
|
+
unsupported.push('distinct()');
|
|
301
|
+
if (state.joins.length > 0)
|
|
302
|
+
unsupported.push('join()');
|
|
303
|
+
if (state.groupBy.length > 0)
|
|
304
|
+
unsupported.push('groupBy()');
|
|
305
|
+
if (state.having.length > 0)
|
|
306
|
+
unsupported.push('having()');
|
|
307
|
+
if (Object.keys(state.with).length > 0)
|
|
308
|
+
unsupported.push('with()');
|
|
309
|
+
if (!policy.where && state.where.length > 0)
|
|
310
|
+
unsupported.push('where()');
|
|
311
|
+
if (!policy.orderBy && state.orderBy.length > 0)
|
|
312
|
+
unsupported.push('orderBy()');
|
|
313
|
+
if (!policy.limit && state.limit !== undefined)
|
|
314
|
+
unsupported.push('limit()');
|
|
315
|
+
if (!policy.offset && state.offset !== undefined)
|
|
316
|
+
unsupported.push('offset()');
|
|
317
|
+
if (unsupported.length > 0) {
|
|
318
|
+
throw new DataTableQueryError(operation + '() does not support these query modifiers: ' + unsupported.join(', '));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function createPredicateColumnResolver(tables) {
|
|
322
|
+
let qualifiedColumns = new Map();
|
|
323
|
+
let unqualifiedColumns = new Map();
|
|
324
|
+
let ambiguousColumns = new Set();
|
|
325
|
+
for (let table of tables) {
|
|
326
|
+
let tableColumns = getTableColumns(table);
|
|
327
|
+
let tableName = getTableName(table);
|
|
328
|
+
for (let columnName in tableColumns) {
|
|
329
|
+
if (!Object.prototype.hasOwnProperty.call(tableColumns, columnName)) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
let resolvedColumn = {
|
|
333
|
+
tableName,
|
|
334
|
+
columnName,
|
|
335
|
+
};
|
|
336
|
+
qualifiedColumns.set(tableName + '.' + columnName, resolvedColumn);
|
|
337
|
+
if (ambiguousColumns.has(columnName)) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (unqualifiedColumns.has(columnName)) {
|
|
341
|
+
unqualifiedColumns.delete(columnName);
|
|
342
|
+
ambiguousColumns.add(columnName);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
unqualifiedColumns.set(columnName, resolvedColumn);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return function resolveColumn(column) {
|
|
349
|
+
let qualified = qualifiedColumns.get(column);
|
|
350
|
+
if (qualified)
|
|
351
|
+
return qualified;
|
|
352
|
+
if (column.includes('.')) {
|
|
353
|
+
throw new DataTableQueryError('Unknown predicate column "' + column + '"');
|
|
354
|
+
}
|
|
355
|
+
if (ambiguousColumns.has(column)) {
|
|
356
|
+
throw new DataTableQueryError('Ambiguous predicate column "' + column + '". Use a qualified column name');
|
|
357
|
+
}
|
|
358
|
+
let unqualified = unqualifiedColumns.get(column);
|
|
359
|
+
if (!unqualified) {
|
|
360
|
+
throw new DataTableQueryError('Unknown predicate column "' + column + '"');
|
|
361
|
+
}
|
|
362
|
+
return unqualified;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function normalizePredicateValues(predicate, resolveColumn) {
|
|
366
|
+
if (predicate.type === 'comparison') {
|
|
367
|
+
let column = resolveColumn(predicate.column);
|
|
368
|
+
if (predicate.valueType === 'column') {
|
|
369
|
+
resolveColumn(predicate.value);
|
|
370
|
+
return predicate;
|
|
371
|
+
}
|
|
372
|
+
if (predicate.operator === 'in' || predicate.operator === 'notIn') {
|
|
373
|
+
if (!Array.isArray(predicate.value)) {
|
|
374
|
+
throw new DataTableValidationError('Invalid filter value for column "' +
|
|
375
|
+
column.columnName +
|
|
376
|
+
'" in table "' +
|
|
377
|
+
column.tableName +
|
|
378
|
+
'"', [{ message: 'Expected an array value for "' + predicate.operator + '" predicate' }], {
|
|
379
|
+
metadata: {
|
|
380
|
+
table: column.tableName,
|
|
381
|
+
column: column.columnName,
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
return predicate;
|
|
386
|
+
}
|
|
387
|
+
return predicate;
|
|
388
|
+
}
|
|
389
|
+
if (predicate.type === 'between') {
|
|
390
|
+
resolveColumn(predicate.column);
|
|
391
|
+
return predicate;
|
|
392
|
+
}
|
|
393
|
+
if (predicate.type === 'null') {
|
|
394
|
+
resolveColumn(predicate.column);
|
|
395
|
+
return predicate;
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
...predicate,
|
|
399
|
+
predicates: predicate.predicates.map((child) => normalizePredicateValues(child, resolveColumn)),
|
|
400
|
+
};
|
|
401
|
+
}
|
package/dist/lib/references.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ export type ColumnReferenceLike<qualifiedName extends string = string> = {
|
|
|
21
21
|
tableName: string;
|
|
22
22
|
columnName: string;
|
|
23
23
|
qualifiedName: qualifiedName;
|
|
24
|
-
schema: unknown;
|
|
25
24
|
};
|
|
26
25
|
};
|
|
27
26
|
export type ColumnInput<qualifiedName extends string = string> = qualifiedName | ColumnReferenceLike<qualifiedName>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../../src/lib/references.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,gBAAgB,eAAqC,CAAA;AAElE;;GAEG;AACH,eAAO,MAAM,iBAAiB,eAAsC,CAAA;AAEpE,KAAK,oBAAoB,CACvB,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,EACxD,UAAU,GAAG,OAAO,IAClB;IACF,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,iBAAiB,CAC3B,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,EACxD,UAAU,GAAG,OAAO,IAClB;IACF,CAAC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;CAChF,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,aAAa,SAAS,MAAM,GAAG,MAAM,IAAI;IACvE,IAAI,EAAE,QAAQ,CAAA;IACd,CAAC,iBAAiB,CAAC,EAAE;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;QAClB,aAAa,EAAE,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"references.d.ts","sourceRoot":"","sources":["../../src/lib/references.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,gBAAgB,eAAqC,CAAA;AAElE;;GAEG;AACH,eAAO,MAAM,iBAAiB,eAAsC,CAAA;AAEpE,KAAK,oBAAoB,CACvB,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,EACxD,UAAU,GAAG,OAAO,IAClB;IACF,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,iBAAiB,CAC3B,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,UAAU,SAAS,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,EACxD,UAAU,GAAG,OAAO,IAClB;IACF,CAAC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;CAChF,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,aAAa,SAAS,MAAM,GAAG,MAAM,IAAI;IACvE,IAAI,EAAE,QAAQ,CAAA;IACd,CAAC,iBAAiB,CAAC,EAAE;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;QAClB,aAAa,EAAE,aAAa,CAAA;KAC7B,CAAA;CACF,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,aAAa,SAAS,MAAM,GAAG,MAAM,IACzD,aAAa,GACb,mBAAmB,CAAC,aAAa,CAAC,CAAA;AAEtC,MAAM,MAAM,oBAAoB,CAAC,KAAK,IACpC,KAAK,SAAS,mBAAmB,CAAC,MAAM,aAAa,CAAC,GAAG,aAAa,GAAG,KAAK,CAAA;AAEhF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAU9E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,MAAM,GAAG,mBAAmB,EAC7E,KAAK,EAAE,KAAK,GACX,oBAAoB,CAAC,KAAK,CAAC,CAM7B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DataManipulationOperation, DataMigrationOperation, TableRef } from './adapter.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Function used to quote SQL identifiers for a dialect.
|
|
4
|
+
*/
|
|
5
|
+
export type QuoteIdentifier = (value: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Type guard that narrows an operation to the data-manipulation union.
|
|
8
|
+
* @param operation Operation to inspect.
|
|
9
|
+
* @returns `true` when the operation is a data-manipulation operation.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isDataManipulationOperation(operation: DataManipulationOperation | DataMigrationOperation): operation is DataManipulationOperation;
|
|
12
|
+
/**
|
|
13
|
+
* Normalizes an arbitrary join type string into `inner`, `left`, or `right`.
|
|
14
|
+
* @param type Input join type.
|
|
15
|
+
* @returns Normalized join type.
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizeJoinType(type: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Returns stable column order from the union of keys in the provided rows.
|
|
20
|
+
* @param rows Row objects to scan for keys.
|
|
21
|
+
* @returns Deduplicated key list in encounter order.
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectColumns(rows: Record<string, unknown>[]): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Quotes each segment of a dotted identifier path.
|
|
26
|
+
*
|
|
27
|
+
* Wildcard segments (`*`) are preserved.
|
|
28
|
+
* @param path Dotted path to quote.
|
|
29
|
+
* @param quoteIdentifier Dialect-specific identifier quote function.
|
|
30
|
+
* @returns Quoted path string.
|
|
31
|
+
*/
|
|
32
|
+
export declare function quotePath(path: string, quoteIdentifier: QuoteIdentifier): string;
|
|
33
|
+
/**
|
|
34
|
+
* Quotes a `{ schema?, name }` table reference using a dialect quote function.
|
|
35
|
+
* @param table Table reference to quote.
|
|
36
|
+
* @param quoteIdentifier Dialect-specific identifier quote function.
|
|
37
|
+
* @returns Quoted table reference.
|
|
38
|
+
*/
|
|
39
|
+
export declare function quoteTableRef(table: TableRef, quoteIdentifier: QuoteIdentifier): string;
|
|
40
|
+
/**
|
|
41
|
+
* Converts a JavaScript value into a SQL literal string.
|
|
42
|
+
* @param value Value to serialize.
|
|
43
|
+
* @param options Serialization options.
|
|
44
|
+
* @param options.booleansAsIntegers When `true`, booleans render as `1`/`0`.
|
|
45
|
+
* @returns SQL literal text.
|
|
46
|
+
*/
|
|
47
|
+
export declare function quoteLiteral(value: unknown, options?: {
|
|
48
|
+
booleansAsIntegers?: boolean;
|
|
49
|
+
}): string;
|
|
50
|
+
//# sourceMappingURL=sql-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/sql-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE/F;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;AAEvD;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,yBAAyB,GAAG,sBAAsB,GAC5D,SAAS,IAAI,yBAAyB,CAYxC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,EAAE,CAoBxE;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,GAAG,MAAM,CAehF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,eAAe,GAAG,MAAM,CAMvF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;IACR,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B,GACA,MAAM,CAsBR"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard that narrows an operation to the data-manipulation union.
|
|
3
|
+
* @param operation Operation to inspect.
|
|
4
|
+
* @returns `true` when the operation is a data-manipulation operation.
|
|
5
|
+
*/
|
|
6
|
+
export function isDataManipulationOperation(operation) {
|
|
7
|
+
return (operation.kind === 'select' ||
|
|
8
|
+
operation.kind === 'count' ||
|
|
9
|
+
operation.kind === 'exists' ||
|
|
10
|
+
operation.kind === 'insert' ||
|
|
11
|
+
operation.kind === 'insertMany' ||
|
|
12
|
+
operation.kind === 'update' ||
|
|
13
|
+
operation.kind === 'delete' ||
|
|
14
|
+
operation.kind === 'upsert' ||
|
|
15
|
+
operation.kind === 'raw');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Normalizes an arbitrary join type string into `inner`, `left`, or `right`.
|
|
19
|
+
* @param type Input join type.
|
|
20
|
+
* @returns Normalized join type.
|
|
21
|
+
*/
|
|
22
|
+
export function normalizeJoinType(type) {
|
|
23
|
+
if (type === 'left') {
|
|
24
|
+
return 'left';
|
|
25
|
+
}
|
|
26
|
+
if (type === 'right') {
|
|
27
|
+
return 'right';
|
|
28
|
+
}
|
|
29
|
+
return 'inner';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns stable column order from the union of keys in the provided rows.
|
|
33
|
+
* @param rows Row objects to scan for keys.
|
|
34
|
+
* @returns Deduplicated key list in encounter order.
|
|
35
|
+
*/
|
|
36
|
+
export function collectColumns(rows) {
|
|
37
|
+
let columns = [];
|
|
38
|
+
let seen = new Set();
|
|
39
|
+
for (let row of rows) {
|
|
40
|
+
for (let key in row) {
|
|
41
|
+
if (!Object.prototype.hasOwnProperty.call(row, key)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (seen.has(key)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
seen.add(key);
|
|
48
|
+
columns.push(key);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return columns;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Quotes each segment of a dotted identifier path.
|
|
55
|
+
*
|
|
56
|
+
* Wildcard segments (`*`) are preserved.
|
|
57
|
+
* @param path Dotted path to quote.
|
|
58
|
+
* @param quoteIdentifier Dialect-specific identifier quote function.
|
|
59
|
+
* @returns Quoted path string.
|
|
60
|
+
*/
|
|
61
|
+
export function quotePath(path, quoteIdentifier) {
|
|
62
|
+
if (path === '*') {
|
|
63
|
+
return '*';
|
|
64
|
+
}
|
|
65
|
+
return path
|
|
66
|
+
.split('.')
|
|
67
|
+
.map((segment) => {
|
|
68
|
+
if (segment === '*') {
|
|
69
|
+
return '*';
|
|
70
|
+
}
|
|
71
|
+
return quoteIdentifier(segment);
|
|
72
|
+
})
|
|
73
|
+
.join('.');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Quotes a `{ schema?, name }` table reference using a dialect quote function.
|
|
77
|
+
* @param table Table reference to quote.
|
|
78
|
+
* @param quoteIdentifier Dialect-specific identifier quote function.
|
|
79
|
+
* @returns Quoted table reference.
|
|
80
|
+
*/
|
|
81
|
+
export function quoteTableRef(table, quoteIdentifier) {
|
|
82
|
+
if (table.schema) {
|
|
83
|
+
return quoteIdentifier(table.schema) + '.' + quoteIdentifier(table.name);
|
|
84
|
+
}
|
|
85
|
+
return quoteIdentifier(table.name);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Converts a JavaScript value into a SQL literal string.
|
|
89
|
+
* @param value Value to serialize.
|
|
90
|
+
* @param options Serialization options.
|
|
91
|
+
* @param options.booleansAsIntegers When `true`, booleans render as `1`/`0`.
|
|
92
|
+
* @returns SQL literal text.
|
|
93
|
+
*/
|
|
94
|
+
export function quoteLiteral(value, options) {
|
|
95
|
+
if (value === null) {
|
|
96
|
+
return 'null';
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === 'number' || typeof value === 'bigint') {
|
|
99
|
+
return String(value);
|
|
100
|
+
}
|
|
101
|
+
if (typeof value === 'boolean') {
|
|
102
|
+
if (options?.booleansAsIntegers) {
|
|
103
|
+
return value ? '1' : '0';
|
|
104
|
+
}
|
|
105
|
+
return value ? 'true' : 'false';
|
|
106
|
+
}
|
|
107
|
+
if (value instanceof Date) {
|
|
108
|
+
return quoteLiteral(value.toISOString(), options);
|
|
109
|
+
}
|
|
110
|
+
return "'" + String(value).replace(/'/g, "''") + "'";
|
|
111
|
+
}
|
package/dist/lib/sql.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Parameterized SQL payload
|
|
2
|
+
* Parameterized SQL payload.
|
|
3
|
+
*
|
|
4
|
+
* The `text` may contain positional placeholders (`?`) or dialect-native
|
|
5
|
+
* placeholders (for example `$1`, `$2`) depending on compiler stage.
|
|
3
6
|
*/
|
|
4
7
|
export type SqlStatement = {
|
|
5
8
|
text: string;
|
|
@@ -8,21 +11,35 @@ export type SqlStatement = {
|
|
|
8
11
|
/**
|
|
9
12
|
* Tagged-template helper for building parameterized SQL statements.
|
|
10
13
|
* @param strings Template string parts.
|
|
11
|
-
* @param values Interpolated values or nested
|
|
12
|
-
* @returns A normalized
|
|
14
|
+
* @param values Interpolated values or nested {@link SqlStatement} values.
|
|
15
|
+
* @returns A normalized {@link SqlStatement}.
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { sql } from 'remix/data-table'
|
|
19
|
+
*
|
|
20
|
+
* let email = 'user@example.com'
|
|
21
|
+
* let statement = sql`select * from users where email = ${email}`
|
|
22
|
+
* // => { text: 'select * from users where email = ?', values: ['user@example.com'] }
|
|
23
|
+
* ```
|
|
13
24
|
*/
|
|
14
25
|
export declare function sql(strings: TemplateStringsArray, ...values: unknown[]): SqlStatement;
|
|
15
26
|
/**
|
|
16
|
-
* Returns `true` when a value matches the
|
|
27
|
+
* Returns `true` when a value matches the {@link SqlStatement} shape.
|
|
17
28
|
* @param value Value to inspect.
|
|
18
|
-
* @returns Whether the value is a
|
|
29
|
+
* @returns Whether the value is a {@link SqlStatement} object.
|
|
19
30
|
*/
|
|
20
31
|
export declare function isSqlStatement(value: unknown): value is SqlStatement;
|
|
21
32
|
/**
|
|
22
33
|
* Creates a SQL statement from raw text and values.
|
|
23
|
-
* @param text SQL text containing
|
|
34
|
+
* @param text SQL text containing placeholders expected by the target adapter.
|
|
24
35
|
* @param values Placeholder values.
|
|
25
36
|
* @returns A normalized SQL statement.
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { rawSql } from 'remix/data-table'
|
|
40
|
+
*
|
|
41
|
+
* let statement = rawSql('select * from users where id = ?', [1])
|
|
42
|
+
* ```
|
|
26
43
|
*/
|
|
27
44
|
export declare function rawSql(text: string, values?: unknown[]): SqlStatement;
|
|
28
45
|
//# sourceMappingURL=sql.d.ts.map
|
package/dist/lib/sql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/lib/sql.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/lib/sql.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;CAClB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,YAAY,CA2BrF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAQpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,YAAY,CAEzE"}
|
package/dist/lib/sql.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tagged-template helper for building parameterized SQL statements.
|
|
3
3
|
* @param strings Template string parts.
|
|
4
|
-
* @param values Interpolated values or nested
|
|
5
|
-
* @returns A normalized
|
|
4
|
+
* @param values Interpolated values or nested {@link SqlStatement} values.
|
|
5
|
+
* @returns A normalized {@link SqlStatement}.
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { sql } from 'remix/data-table'
|
|
9
|
+
*
|
|
10
|
+
* let email = 'user@example.com'
|
|
11
|
+
* let statement = sql`select * from users where email = ${email}`
|
|
12
|
+
* // => { text: 'select * from users where email = ?', values: ['user@example.com'] }
|
|
13
|
+
* ```
|
|
6
14
|
*/
|
|
7
15
|
export function sql(strings, ...values) {
|
|
8
16
|
let text = '';
|
|
@@ -29,9 +37,9 @@ export function sql(strings, ...values) {
|
|
|
29
37
|
};
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
|
-
* Returns `true` when a value matches the
|
|
40
|
+
* Returns `true` when a value matches the {@link SqlStatement} shape.
|
|
33
41
|
* @param value Value to inspect.
|
|
34
|
-
* @returns Whether the value is a
|
|
42
|
+
* @returns Whether the value is a {@link SqlStatement} object.
|
|
35
43
|
*/
|
|
36
44
|
export function isSqlStatement(value) {
|
|
37
45
|
if (typeof value !== 'object' || value === null) {
|
|
@@ -42,9 +50,15 @@ export function isSqlStatement(value) {
|
|
|
42
50
|
}
|
|
43
51
|
/**
|
|
44
52
|
* Creates a SQL statement from raw text and values.
|
|
45
|
-
* @param text SQL text containing
|
|
53
|
+
* @param text SQL text containing placeholders expected by the target adapter.
|
|
46
54
|
* @param values Placeholder values.
|
|
47
55
|
* @returns A normalized SQL statement.
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { rawSql } from 'remix/data-table'
|
|
59
|
+
*
|
|
60
|
+
* let statement = rawSql('select * from users where id = ?', [1])
|
|
61
|
+
* ```
|
|
48
62
|
*/
|
|
49
63
|
export function rawSql(text, values = []) {
|
|
50
64
|
return { text, values };
|