@payloadcms/drizzle 4.0.0-canary.35 → 4.0.0-canary.36
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/package.json +3 -3
- package/dist/find/buildPolymorphicJoinQuery.spec.js +0 -384
- package/dist/find/buildPolymorphicJoinQuery.spec.js.map +0 -1
- package/dist/find/buildPolymorphicJoinWhere.spec.js +0 -805
- package/dist/find/buildPolymorphicJoinWhere.spec.js.map +0 -1
- package/dist/find/createPolymorphicJoinWherePlan.spec.js +0 -778
- package/dist/find/createPolymorphicJoinWherePlan.spec.js.map +0 -1
- package/dist/postgres/query/assertOperatorHandlerExtensionsInstalled.spec.js +0 -91
- package/dist/postgres/query/assertOperatorHandlerExtensionsInstalled.spec.js.map +0 -1
- package/dist/postgres/query/postgresUnaccent.spec.js +0 -71
- package/dist/postgres/query/postgresUnaccent.spec.js.map +0 -1
- package/dist/queries/buildOperatorConstraint.spec.js +0 -348
- package/dist/queries/buildOperatorConstraint.spec.js.map +0 -1
- package/dist/queries/validateOperatorHandlers.spec.js +0 -142
- package/dist/queries/validateOperatorHandlers.spec.js.map +0 -1
- package/dist/schema/buildRawSchema.spec.js +0 -290
- package/dist/schema/buildRawSchema.spec.js.map +0 -1
- package/dist/updateOne.spec.js +0 -84
- package/dist/updateOne.spec.js.map +0 -1
- package/dist/upsertRow/conditionalUpdate.spec.js +0 -61
- package/dist/upsertRow/conditionalUpdate.spec.js.map +0 -1
- package/dist/upsertRow/handleUpsertError.spec.js +0 -42
- package/dist/upsertRow/handleUpsertError.spec.js.map +0 -1
- package/dist/utilities/checkTruncatedIdentifiers.spec.js +0 -301
- package/dist/utilities/checkTruncatedIdentifiers.spec.js.map +0 -1
- package/dist/utilities/isValidStringID.spec.js +0 -15
- package/dist/utilities/isValidStringID.spec.js.map +0 -1
- package/dist/utilities/validateIdentifierLength.spec.js +0 -19
- package/dist/utilities/validateIdentifierLength.spec.js.map +0 -1
|
@@ -1,805 +0,0 @@
|
|
|
1
|
-
import { createClient } from '@libsql/client';
|
|
2
|
-
import { like, notLike } from 'drizzle-orm';
|
|
3
|
-
import { drizzle } from 'drizzle-orm/libsql';
|
|
4
|
-
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
|
|
5
|
-
import { afterAll, describe, expect, it, vi } from 'vitest';
|
|
6
|
-
import { operatorMap } from '../queries/operatorMap.js';
|
|
7
|
-
import { createJSONQuery } from '../sqlite/createJSONQuery/index.js';
|
|
8
|
-
import { buildPolymorphicJoinWhere } from './buildPolymorphicJoinWhere.js';
|
|
9
|
-
const articlesTable = sqliteTable('join_articles', {
|
|
10
|
-
id: integer('id').primaryKey(),
|
|
11
|
-
parent: integer('parent_id'),
|
|
12
|
-
score: integer('score'),
|
|
13
|
-
title: text('title')
|
|
14
|
-
});
|
|
15
|
-
const notesTable = sqliteTable('join_notes', {
|
|
16
|
-
id: integer('id').primaryKey(),
|
|
17
|
-
parent: integer('parent_id'),
|
|
18
|
-
title: text('title')
|
|
19
|
-
});
|
|
20
|
-
const client = createClient({
|
|
21
|
-
url: 'file::memory:'
|
|
22
|
-
});
|
|
23
|
-
const db = drizzle(client, {
|
|
24
|
-
schema: {
|
|
25
|
-
articlesTable,
|
|
26
|
-
notesTable
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
const textField = {
|
|
30
|
-
name: 'title',
|
|
31
|
-
type: 'text'
|
|
32
|
-
};
|
|
33
|
-
const numberField = {
|
|
34
|
-
name: 'score',
|
|
35
|
-
type: 'number'
|
|
36
|
-
};
|
|
37
|
-
const hasManySelectField = {
|
|
38
|
-
hasMany: true,
|
|
39
|
-
name: 'tags',
|
|
40
|
-
options: [
|
|
41
|
-
'available',
|
|
42
|
-
'reviewed'
|
|
43
|
-
],
|
|
44
|
-
type: 'select'
|
|
45
|
-
};
|
|
46
|
-
const adapter = {
|
|
47
|
-
createJSONQuery,
|
|
48
|
-
drizzle: db,
|
|
49
|
-
idType: 'serial',
|
|
50
|
-
name: 'sqlite',
|
|
51
|
-
operatorHandlers: [],
|
|
52
|
-
operators: {
|
|
53
|
-
...operatorMap,
|
|
54
|
-
contains: like,
|
|
55
|
-
like,
|
|
56
|
-
not_like: notLike
|
|
57
|
-
},
|
|
58
|
-
payload: {
|
|
59
|
-
collections: {}
|
|
60
|
-
},
|
|
61
|
-
tableNameMap: new Map([
|
|
62
|
-
[
|
|
63
|
-
'articles_tags',
|
|
64
|
-
'join_articles_tags'
|
|
65
|
-
],
|
|
66
|
-
[
|
|
67
|
-
'notes_tags',
|
|
68
|
-
'join_notes_tags'
|
|
69
|
-
]
|
|
70
|
-
]),
|
|
71
|
-
tables: {
|
|
72
|
-
join_articles: articlesTable,
|
|
73
|
-
join_notes: notesTable
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const scalarPlan = ({ collections = [
|
|
77
|
-
'articles'
|
|
78
|
-
], columnPath, field = textField, schemaPath = columnPath })=>new Map([
|
|
79
|
-
[
|
|
80
|
-
schemaPath,
|
|
81
|
-
{
|
|
82
|
-
columnPath,
|
|
83
|
-
fieldsByCollection: new Map(collections.map((collection)=>[
|
|
84
|
-
collection,
|
|
85
|
-
{
|
|
86
|
-
field,
|
|
87
|
-
isUUID: false,
|
|
88
|
-
type: 'scalar'
|
|
89
|
-
}
|
|
90
|
-
])),
|
|
91
|
-
schemaPath,
|
|
92
|
-
type: 'scalar'
|
|
93
|
-
}
|
|
94
|
-
]
|
|
95
|
-
]);
|
|
96
|
-
const hasManySelectPlan = (collections = [
|
|
97
|
-
'articles'
|
|
98
|
-
], schemaPath = 'tags')=>new Map([
|
|
99
|
-
[
|
|
100
|
-
schemaPath,
|
|
101
|
-
{
|
|
102
|
-
columnPath: schemaPath.replaceAll('.', '_'),
|
|
103
|
-
fieldsByCollection: new Map(collections.map((collection)=>[
|
|
104
|
-
collection,
|
|
105
|
-
{
|
|
106
|
-
field: hasManySelectField,
|
|
107
|
-
type: 'hasManySelect'
|
|
108
|
-
}
|
|
109
|
-
])),
|
|
110
|
-
schemaPath,
|
|
111
|
-
type: 'hasManySelect'
|
|
112
|
-
}
|
|
113
|
-
]
|
|
114
|
-
]);
|
|
115
|
-
const renderWhere = ({ collection = 'articles', table = articlesTable, where, wherePlan })=>{
|
|
116
|
-
const condition = buildPolymorphicJoinWhere({
|
|
117
|
-
adapter,
|
|
118
|
-
collection,
|
|
119
|
-
table,
|
|
120
|
-
where,
|
|
121
|
-
wherePlan
|
|
122
|
-
});
|
|
123
|
-
return db.select({
|
|
124
|
-
id: table.id
|
|
125
|
-
}).from(table).where(condition).toSQL();
|
|
126
|
-
};
|
|
127
|
-
afterAll(()=>client.close());
|
|
128
|
-
describe('buildPolymorphicJoinWhere', ()=>{
|
|
129
|
-
it('builds scalar comparisons and preserves nested AND and OR groups', ()=>{
|
|
130
|
-
const wherePlan = scalarPlan({
|
|
131
|
-
columnPath: 'title',
|
|
132
|
-
collections: [
|
|
133
|
-
'articles',
|
|
134
|
-
'notes'
|
|
135
|
-
]
|
|
136
|
-
});
|
|
137
|
-
const query = renderWhere({
|
|
138
|
-
where: {
|
|
139
|
-
AND: [
|
|
140
|
-
{
|
|
141
|
-
title: {
|
|
142
|
-
equals: 'available'
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
OR: [
|
|
147
|
-
{
|
|
148
|
-
title: {
|
|
149
|
-
equals: 'reviewed'
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
title: {
|
|
154
|
-
equals: 'published'
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
]
|
|
158
|
-
}
|
|
159
|
-
]
|
|
160
|
-
},
|
|
161
|
-
wherePlan
|
|
162
|
-
});
|
|
163
|
-
expect(query.sql).toContain('where ("join_articles"."title" = ? and');
|
|
164
|
-
expect(query.sql).toContain('or');
|
|
165
|
-
expect(query.params).toEqual([
|
|
166
|
-
'available',
|
|
167
|
-
'reviewed',
|
|
168
|
-
'published'
|
|
169
|
-
]);
|
|
170
|
-
});
|
|
171
|
-
it('treats a numeric like comparison as equality', ()=>{
|
|
172
|
-
const query = renderWhere({
|
|
173
|
-
where: {
|
|
174
|
-
score: {
|
|
175
|
-
like: '5'
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
wherePlan: scalarPlan({
|
|
179
|
-
columnPath: 'score',
|
|
180
|
-
field: numberField
|
|
181
|
-
})
|
|
182
|
-
});
|
|
183
|
-
expect(query.sql).toContain('"join_articles"."score" = ?');
|
|
184
|
-
expect(query.params).toEqual([
|
|
185
|
-
5
|
|
186
|
-
]);
|
|
187
|
-
});
|
|
188
|
-
it('uses SQL NULL for a scalar field that is absent from the current collection', ()=>{
|
|
189
|
-
const query = renderWhere({
|
|
190
|
-
collection: 'notes',
|
|
191
|
-
table: notesTable,
|
|
192
|
-
where: {
|
|
193
|
-
score: {
|
|
194
|
-
exists: false
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
wherePlan: scalarPlan({
|
|
198
|
-
columnPath: 'score',
|
|
199
|
-
collections: [
|
|
200
|
-
'articles'
|
|
201
|
-
],
|
|
202
|
-
field: numberField
|
|
203
|
-
})
|
|
204
|
-
});
|
|
205
|
-
expect(query.sql).toContain('where null is null');
|
|
206
|
-
expect(query.params).toEqual([]);
|
|
207
|
-
});
|
|
208
|
-
it.each([
|
|
209
|
-
[
|
|
210
|
-
'exists',
|
|
211
|
-
{
|
|
212
|
-
exists: 'false'
|
|
213
|
-
},
|
|
214
|
-
'where null is null',
|
|
215
|
-
[]
|
|
216
|
-
],
|
|
217
|
-
[
|
|
218
|
-
'in',
|
|
219
|
-
{
|
|
220
|
-
in: '1,2'
|
|
221
|
-
},
|
|
222
|
-
'where null in (?, ?)',
|
|
223
|
-
[
|
|
224
|
-
1,
|
|
225
|
-
2
|
|
226
|
-
]
|
|
227
|
-
]
|
|
228
|
-
])('normalizes a REST %s value when a scalar field is absent from the current collection', (_operator, constraint, expectedSQL, expectedParams)=>{
|
|
229
|
-
const query = renderWhere({
|
|
230
|
-
collection: 'notes',
|
|
231
|
-
table: notesTable,
|
|
232
|
-
where: {
|
|
233
|
-
score: constraint
|
|
234
|
-
},
|
|
235
|
-
wherePlan: scalarPlan({
|
|
236
|
-
columnPath: 'score',
|
|
237
|
-
collections: [
|
|
238
|
-
'articles'
|
|
239
|
-
],
|
|
240
|
-
field: numberField
|
|
241
|
-
})
|
|
242
|
-
});
|
|
243
|
-
expect(query.sql).toContain(expectedSQL);
|
|
244
|
-
expect(query.params).toEqual(expectedParams);
|
|
245
|
-
});
|
|
246
|
-
it('keeps contains wildcard handling when a scalar field is absent', ()=>{
|
|
247
|
-
const query = renderWhere({
|
|
248
|
-
collection: 'notes',
|
|
249
|
-
table: notesTable,
|
|
250
|
-
where: {
|
|
251
|
-
score: {
|
|
252
|
-
contains: '5'
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
wherePlan: scalarPlan({
|
|
256
|
-
columnPath: 'score',
|
|
257
|
-
collections: [
|
|
258
|
-
'articles'
|
|
259
|
-
],
|
|
260
|
-
field: numberField
|
|
261
|
-
})
|
|
262
|
-
});
|
|
263
|
-
expect(query.sql).toContain('where null like ?');
|
|
264
|
-
expect(query.params).toEqual([
|
|
265
|
-
'%5%'
|
|
266
|
-
]);
|
|
267
|
-
});
|
|
268
|
-
it('compares relationTo with the current collection slug', ()=>{
|
|
269
|
-
const wherePlan = new Map([
|
|
270
|
-
[
|
|
271
|
-
'relationTo',
|
|
272
|
-
{
|
|
273
|
-
columnPath: 'relationTo',
|
|
274
|
-
fieldsByCollection: new Map(),
|
|
275
|
-
schemaPath: 'relationTo',
|
|
276
|
-
type: 'scalar'
|
|
277
|
-
}
|
|
278
|
-
]
|
|
279
|
-
]);
|
|
280
|
-
const query = renderWhere({
|
|
281
|
-
collection: 'notes',
|
|
282
|
-
table: notesTable,
|
|
283
|
-
where: {
|
|
284
|
-
relationTo: {
|
|
285
|
-
equals: 'notes'
|
|
286
|
-
}
|
|
287
|
-
},
|
|
288
|
-
wherePlan
|
|
289
|
-
});
|
|
290
|
-
expect(query.sql).toContain('where ? = ?');
|
|
291
|
-
expect(query.params).toEqual([
|
|
292
|
-
'notes',
|
|
293
|
-
'notes'
|
|
294
|
-
]);
|
|
295
|
-
});
|
|
296
|
-
it.each([
|
|
297
|
-
[
|
|
298
|
-
'exists',
|
|
299
|
-
{
|
|
300
|
-
exists: 'false'
|
|
301
|
-
},
|
|
302
|
-
'where ? is null',
|
|
303
|
-
[
|
|
304
|
-
'notes'
|
|
305
|
-
]
|
|
306
|
-
],
|
|
307
|
-
[
|
|
308
|
-
'in',
|
|
309
|
-
{
|
|
310
|
-
in: 'articles,notes'
|
|
311
|
-
},
|
|
312
|
-
'where ? in (?, ?)',
|
|
313
|
-
[
|
|
314
|
-
'notes',
|
|
315
|
-
'articles',
|
|
316
|
-
'notes'
|
|
317
|
-
]
|
|
318
|
-
]
|
|
319
|
-
])('normalizes a REST %s value for relationTo', (_operator, constraint, expectedSQL, expectedParams)=>{
|
|
320
|
-
const wherePlan = new Map([
|
|
321
|
-
[
|
|
322
|
-
'relationTo',
|
|
323
|
-
{
|
|
324
|
-
columnPath: 'relationTo',
|
|
325
|
-
fieldsByCollection: new Map(),
|
|
326
|
-
schemaPath: 'relationTo',
|
|
327
|
-
type: 'scalar'
|
|
328
|
-
}
|
|
329
|
-
]
|
|
330
|
-
]);
|
|
331
|
-
const query = renderWhere({
|
|
332
|
-
collection: 'notes',
|
|
333
|
-
table: notesTable,
|
|
334
|
-
where: {
|
|
335
|
-
relationTo: constraint
|
|
336
|
-
},
|
|
337
|
-
wherePlan
|
|
338
|
-
});
|
|
339
|
-
expect(query.sql).toContain(expectedSQL);
|
|
340
|
-
expect(query.params).toEqual(expectedParams);
|
|
341
|
-
});
|
|
342
|
-
it.each([
|
|
343
|
-
[
|
|
344
|
-
'equals null',
|
|
345
|
-
{
|
|
346
|
-
title: {
|
|
347
|
-
equals: null
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
'"join_articles"."title" is null',
|
|
351
|
-
[]
|
|
352
|
-
],
|
|
353
|
-
[
|
|
354
|
-
'not_equals null',
|
|
355
|
-
{
|
|
356
|
-
title: {
|
|
357
|
-
not_equals: null
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
'"join_articles"."title" is not null',
|
|
361
|
-
[]
|
|
362
|
-
],
|
|
363
|
-
[
|
|
364
|
-
'not_equals a value',
|
|
365
|
-
{
|
|
366
|
-
title: {
|
|
367
|
-
not_equals: 'restricted'
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
'"join_articles"."title" is null or "join_articles"."title" <> ?',
|
|
371
|
-
[
|
|
372
|
-
'restricted'
|
|
373
|
-
]
|
|
374
|
-
],
|
|
375
|
-
[
|
|
376
|
-
'in values including null',
|
|
377
|
-
{
|
|
378
|
-
title: {
|
|
379
|
-
in: [
|
|
380
|
-
'available',
|
|
381
|
-
null
|
|
382
|
-
]
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
'"join_articles"."title" is null or "join_articles"."title" in (?)',
|
|
386
|
-
[
|
|
387
|
-
'available'
|
|
388
|
-
]
|
|
389
|
-
]
|
|
390
|
-
])('keeps null semantics for %s', (_description, where, expectedSQL, expectedParams)=>{
|
|
391
|
-
const query = renderWhere({
|
|
392
|
-
where: where,
|
|
393
|
-
wherePlan: scalarPlan({
|
|
394
|
-
columnPath: 'title'
|
|
395
|
-
})
|
|
396
|
-
});
|
|
397
|
-
expect(query.sql).toContain(expectedSQL);
|
|
398
|
-
expect(query.params).toEqual(expectedParams);
|
|
399
|
-
});
|
|
400
|
-
it('requires every word in a scalar like comparison', ()=>{
|
|
401
|
-
const query = renderWhere({
|
|
402
|
-
where: {
|
|
403
|
-
title: {
|
|
404
|
-
like: 'available child'
|
|
405
|
-
}
|
|
406
|
-
},
|
|
407
|
-
wherePlan: scalarPlan({
|
|
408
|
-
columnPath: 'title'
|
|
409
|
-
})
|
|
410
|
-
});
|
|
411
|
-
expect(query.sql).toContain('"join_articles"."title" like ? and');
|
|
412
|
-
expect(query.params).toEqual([
|
|
413
|
-
'%available%',
|
|
414
|
-
'%child%'
|
|
415
|
-
]);
|
|
416
|
-
});
|
|
417
|
-
it('applies configured operator handlers to scalar comparisons', ()=>{
|
|
418
|
-
const transformOperands = vi.fn(({ column, value })=>({
|
|
419
|
-
column,
|
|
420
|
-
value: `handled-${String(value)}`
|
|
421
|
-
}));
|
|
422
|
-
const handler = {
|
|
423
|
-
name: 'prefix-value',
|
|
424
|
-
operators: [
|
|
425
|
-
'equals'
|
|
426
|
-
],
|
|
427
|
-
transformOperands
|
|
428
|
-
};
|
|
429
|
-
const adapterWithHandler = {
|
|
430
|
-
...adapter,
|
|
431
|
-
operatorHandlers: [
|
|
432
|
-
handler
|
|
433
|
-
]
|
|
434
|
-
};
|
|
435
|
-
const condition = buildPolymorphicJoinWhere({
|
|
436
|
-
adapter: adapterWithHandler,
|
|
437
|
-
collection: 'articles',
|
|
438
|
-
table: articlesTable,
|
|
439
|
-
where: {
|
|
440
|
-
title: {
|
|
441
|
-
equals: 'available'
|
|
442
|
-
}
|
|
443
|
-
},
|
|
444
|
-
wherePlan: scalarPlan({
|
|
445
|
-
columnPath: 'title'
|
|
446
|
-
})
|
|
447
|
-
});
|
|
448
|
-
const query = db.select({
|
|
449
|
-
id: articlesTable.id
|
|
450
|
-
}).from(articlesTable).where(condition).toSQL();
|
|
451
|
-
expect(transformOperands).toHaveBeenCalledWith(expect.objectContaining({
|
|
452
|
-
path: 'title',
|
|
453
|
-
resolvedOperator: 'equals',
|
|
454
|
-
storage: 'column'
|
|
455
|
-
}));
|
|
456
|
-
expect(query.params).toEqual([
|
|
457
|
-
'handled-available'
|
|
458
|
-
]);
|
|
459
|
-
});
|
|
460
|
-
it('queries a has-many select through its value table', ()=>{
|
|
461
|
-
const query = renderWhere({
|
|
462
|
-
where: {
|
|
463
|
-
tags: {
|
|
464
|
-
contains: 'available'
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
wherePlan: hasManySelectPlan()
|
|
468
|
-
});
|
|
469
|
-
expect(query.sql).toContain('json_group_array(join_articles_tags.value)');
|
|
470
|
-
expect(query.sql).toContain("value = 'available'");
|
|
471
|
-
});
|
|
472
|
-
it.each([
|
|
473
|
-
[
|
|
474
|
-
'a REST exists value',
|
|
475
|
-
{
|
|
476
|
-
exists: 'false'
|
|
477
|
-
},
|
|
478
|
-
[
|
|
479
|
-
'NOT EXISTS (SELECT 1 FROM json_each(',
|
|
480
|
-
'AS tags_alias_0)'
|
|
481
|
-
]
|
|
482
|
-
],
|
|
483
|
-
[
|
|
484
|
-
'a comma-delimited REST in value',
|
|
485
|
-
{
|
|
486
|
-
in: 'available,reviewed'
|
|
487
|
-
},
|
|
488
|
-
[
|
|
489
|
-
"tags_alias_0.value = 'available'",
|
|
490
|
-
' OR ',
|
|
491
|
-
"tags_alias_0.value = 'reviewed'"
|
|
492
|
-
]
|
|
493
|
-
]
|
|
494
|
-
])('normalizes %s for has-many selects', (_description, constraint, fragments)=>{
|
|
495
|
-
const query = renderWhere({
|
|
496
|
-
where: {
|
|
497
|
-
tags: constraint
|
|
498
|
-
},
|
|
499
|
-
wherePlan: hasManySelectPlan()
|
|
500
|
-
});
|
|
501
|
-
for (const fragment of fragments){
|
|
502
|
-
expect(query.sql).toContain(fragment);
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
it.each([
|
|
506
|
-
[
|
|
507
|
-
'without a schema',
|
|
508
|
-
undefined
|
|
509
|
-
],
|
|
510
|
-
[
|
|
511
|
-
'with a schema',
|
|
512
|
-
'content'
|
|
513
|
-
]
|
|
514
|
-
])('rejects a missing has-many select value table mapping %s', (_description, schemaName)=>{
|
|
515
|
-
const adapterWithoutValueTable = {
|
|
516
|
-
...adapter,
|
|
517
|
-
schemaName,
|
|
518
|
-
tableNameMap: new Map()
|
|
519
|
-
};
|
|
520
|
-
expect(()=>buildPolymorphicJoinWhere({
|
|
521
|
-
adapter: adapterWithoutValueTable,
|
|
522
|
-
collection: 'articles',
|
|
523
|
-
table: articlesTable,
|
|
524
|
-
where: {
|
|
525
|
-
tags: {
|
|
526
|
-
equals: 'available'
|
|
527
|
-
}
|
|
528
|
-
},
|
|
529
|
-
wherePlan: hasManySelectPlan()
|
|
530
|
-
})).toThrow('Polymorphic join collection "articles" has no value table for "tags"');
|
|
531
|
-
});
|
|
532
|
-
it('uses an empty JSON array when a has-many select is absent from this collection', ()=>{
|
|
533
|
-
const query = renderWhere({
|
|
534
|
-
collection: 'notes',
|
|
535
|
-
table: notesTable,
|
|
536
|
-
where: {
|
|
537
|
-
tags: {
|
|
538
|
-
exists: false
|
|
539
|
-
}
|
|
540
|
-
},
|
|
541
|
-
wherePlan: hasManySelectPlan([
|
|
542
|
-
'articles'
|
|
543
|
-
])
|
|
544
|
-
});
|
|
545
|
-
expect(query.sql).toContain("json_each('[]')");
|
|
546
|
-
expect(query.sql).not.toContain('join_notes_tags');
|
|
547
|
-
});
|
|
548
|
-
it('builds a false condition for an empty has-many in comparison', ()=>{
|
|
549
|
-
const query = renderWhere({
|
|
550
|
-
where: {
|
|
551
|
-
tags: {
|
|
552
|
-
in: []
|
|
553
|
-
}
|
|
554
|
-
},
|
|
555
|
-
wherePlan: hasManySelectPlan()
|
|
556
|
-
});
|
|
557
|
-
expect(query.sql).toContain('where false');
|
|
558
|
-
});
|
|
559
|
-
it('builds a false condition for an empty scalar in comparison', ()=>{
|
|
560
|
-
const query = renderWhere({
|
|
561
|
-
where: {
|
|
562
|
-
title: {
|
|
563
|
-
in: []
|
|
564
|
-
}
|
|
565
|
-
},
|
|
566
|
-
wherePlan: scalarPlan({
|
|
567
|
-
columnPath: 'title'
|
|
568
|
-
})
|
|
569
|
-
});
|
|
570
|
-
expect(query.sql).toContain('where false');
|
|
571
|
-
});
|
|
572
|
-
it.each([
|
|
573
|
-
[
|
|
574
|
-
'a scalar in value that cannot be normalized',
|
|
575
|
-
{
|
|
576
|
-
title: {
|
|
577
|
-
in: true
|
|
578
|
-
}
|
|
579
|
-
},
|
|
580
|
-
scalarPlan({
|
|
581
|
-
columnPath: 'title'
|
|
582
|
-
})
|
|
583
|
-
],
|
|
584
|
-
[
|
|
585
|
-
'an undefined scalar in value',
|
|
586
|
-
{
|
|
587
|
-
title: {
|
|
588
|
-
in: undefined
|
|
589
|
-
}
|
|
590
|
-
},
|
|
591
|
-
scalarPlan({
|
|
592
|
-
columnPath: 'title'
|
|
593
|
-
})
|
|
594
|
-
],
|
|
595
|
-
[
|
|
596
|
-
'a has-many select in value that cannot be normalized',
|
|
597
|
-
{
|
|
598
|
-
tags: {
|
|
599
|
-
in: true
|
|
600
|
-
}
|
|
601
|
-
},
|
|
602
|
-
hasManySelectPlan()
|
|
603
|
-
]
|
|
604
|
-
])('rejects %s', (_description, where, wherePlan)=>{
|
|
605
|
-
expect(()=>renderWhere({
|
|
606
|
-
where: where,
|
|
607
|
-
wherePlan
|
|
608
|
-
})).toThrow(Object.keys(where)[0] === 'tags' ? 'tags.in' : 'title.in');
|
|
609
|
-
});
|
|
610
|
-
it.each([
|
|
611
|
-
'AND',
|
|
612
|
-
'OR'
|
|
613
|
-
])('rejects a value that cannot be normalized inside an %s group', (booleanOperator)=>{
|
|
614
|
-
expect(()=>renderWhere({
|
|
615
|
-
where: {
|
|
616
|
-
[booleanOperator]: [
|
|
617
|
-
{
|
|
618
|
-
title: {
|
|
619
|
-
in: true
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
{
|
|
623
|
-
title: {
|
|
624
|
-
equals: 'available'
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
]
|
|
628
|
-
},
|
|
629
|
-
wherePlan: scalarPlan({
|
|
630
|
-
columnPath: 'title'
|
|
631
|
-
})
|
|
632
|
-
})).toThrow('title.in');
|
|
633
|
-
});
|
|
634
|
-
it.each([
|
|
635
|
-
[
|
|
636
|
-
'equals',
|
|
637
|
-
{
|
|
638
|
-
equals: 'available'
|
|
639
|
-
},
|
|
640
|
-
[
|
|
641
|
-
"tags_alias_0.value = 'available'"
|
|
642
|
-
]
|
|
643
|
-
],
|
|
644
|
-
[
|
|
645
|
-
'exists',
|
|
646
|
-
{
|
|
647
|
-
exists: true
|
|
648
|
-
},
|
|
649
|
-
[
|
|
650
|
-
'EXISTS (SELECT 1 FROM json_each(',
|
|
651
|
-
'AS tags_alias_0)'
|
|
652
|
-
]
|
|
653
|
-
],
|
|
654
|
-
[
|
|
655
|
-
'in',
|
|
656
|
-
{
|
|
657
|
-
in: [
|
|
658
|
-
'available',
|
|
659
|
-
'reviewed'
|
|
660
|
-
]
|
|
661
|
-
},
|
|
662
|
-
[
|
|
663
|
-
"tags_alias_0.value = 'available'",
|
|
664
|
-
' OR ',
|
|
665
|
-
"tags_alias_0.value = 'reviewed'"
|
|
666
|
-
]
|
|
667
|
-
],
|
|
668
|
-
[
|
|
669
|
-
'like',
|
|
670
|
-
{
|
|
671
|
-
like: 'avail'
|
|
672
|
-
},
|
|
673
|
-
[
|
|
674
|
-
"tags_alias_0.value like '%avail%'"
|
|
675
|
-
]
|
|
676
|
-
]
|
|
677
|
-
])('supports the %s operator for has-many selects', (_operator, constraint, expectedSQLFragments)=>{
|
|
678
|
-
const query = renderWhere({
|
|
679
|
-
where: {
|
|
680
|
-
tags: constraint
|
|
681
|
-
},
|
|
682
|
-
wherePlan: hasManySelectPlan()
|
|
683
|
-
});
|
|
684
|
-
expect(query.sql).toContain('join_articles_tags');
|
|
685
|
-
for (const expectedSQLFragment of expectedSQLFragments){
|
|
686
|
-
expect(query.sql).toContain(expectedSQLFragment);
|
|
687
|
-
}
|
|
688
|
-
expect(query.params).toEqual([]);
|
|
689
|
-
});
|
|
690
|
-
it('accepts a string $raw constraint', ()=>{
|
|
691
|
-
const query = renderWhere({
|
|
692
|
-
where: {
|
|
693
|
-
title: {
|
|
694
|
-
$raw: 'length(title) > 3'
|
|
695
|
-
}
|
|
696
|
-
},
|
|
697
|
-
wherePlan: scalarPlan({
|
|
698
|
-
columnPath: 'title'
|
|
699
|
-
})
|
|
700
|
-
});
|
|
701
|
-
expect(query.sql).toContain('where length(title) > 3');
|
|
702
|
-
});
|
|
703
|
-
it.each([
|
|
704
|
-
[
|
|
705
|
-
'an invalid path plan',
|
|
706
|
-
{
|
|
707
|
-
title: {
|
|
708
|
-
equals: 'available'
|
|
709
|
-
}
|
|
710
|
-
},
|
|
711
|
-
new Map([
|
|
712
|
-
[
|
|
713
|
-
'title',
|
|
714
|
-
{
|
|
715
|
-
columnPath: 'title',
|
|
716
|
-
fieldsByCollection: new Map(),
|
|
717
|
-
schemaPath: 'title',
|
|
718
|
-
type: 'invalid'
|
|
719
|
-
}
|
|
720
|
-
]
|
|
721
|
-
]),
|
|
722
|
-
'title.equals'
|
|
723
|
-
],
|
|
724
|
-
[
|
|
725
|
-
'a missing path plan',
|
|
726
|
-
{
|
|
727
|
-
title: {
|
|
728
|
-
equals: 'available'
|
|
729
|
-
}
|
|
730
|
-
},
|
|
731
|
-
new Map(),
|
|
732
|
-
'title'
|
|
733
|
-
],
|
|
734
|
-
[
|
|
735
|
-
'an unsupported has-many operator',
|
|
736
|
-
{
|
|
737
|
-
tags: {
|
|
738
|
-
not_equals: 'available'
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
hasManySelectPlan(),
|
|
742
|
-
'tags.not_equals'
|
|
743
|
-
],
|
|
744
|
-
[
|
|
745
|
-
'an unknown scalar operator',
|
|
746
|
-
{
|
|
747
|
-
title: {
|
|
748
|
-
near: 'available'
|
|
749
|
-
}
|
|
750
|
-
},
|
|
751
|
-
scalarPlan({
|
|
752
|
-
columnPath: 'title'
|
|
753
|
-
}),
|
|
754
|
-
'title.near'
|
|
755
|
-
],
|
|
756
|
-
[
|
|
757
|
-
'a non-string $raw value',
|
|
758
|
-
{
|
|
759
|
-
title: {
|
|
760
|
-
$raw: 1
|
|
761
|
-
}
|
|
762
|
-
},
|
|
763
|
-
scalarPlan({
|
|
764
|
-
columnPath: 'title'
|
|
765
|
-
}),
|
|
766
|
-
'title.$raw'
|
|
767
|
-
]
|
|
768
|
-
])('rejects %s', (_description, where, wherePlan, errorPath)=>{
|
|
769
|
-
expect(()=>buildPolymorphicJoinWhere({
|
|
770
|
-
adapter,
|
|
771
|
-
collection: 'articles',
|
|
772
|
-
table: articlesTable,
|
|
773
|
-
where: where,
|
|
774
|
-
wherePlan: wherePlan
|
|
775
|
-
})).toThrow(errorPath);
|
|
776
|
-
});
|
|
777
|
-
it('returns no condition for empty boolean groups', ()=>{
|
|
778
|
-
expect(buildPolymorphicJoinWhere({
|
|
779
|
-
adapter,
|
|
780
|
-
collection: 'articles',
|
|
781
|
-
table: articlesTable,
|
|
782
|
-
where: {
|
|
783
|
-
AND: [],
|
|
784
|
-
OR: []
|
|
785
|
-
},
|
|
786
|
-
wherePlan: new Map()
|
|
787
|
-
})).toBeUndefined();
|
|
788
|
-
});
|
|
789
|
-
it.each([
|
|
790
|
-
'AND',
|
|
791
|
-
'OR'
|
|
792
|
-
])('rejects a malformed %s group', (operator)=>{
|
|
793
|
-
expect(()=>buildPolymorphicJoinWhere({
|
|
794
|
-
adapter,
|
|
795
|
-
collection: 'articles',
|
|
796
|
-
table: articlesTable,
|
|
797
|
-
where: {
|
|
798
|
-
[operator]: 'not-an-array'
|
|
799
|
-
},
|
|
800
|
-
wherePlan: new Map()
|
|
801
|
-
})).toThrow(operator);
|
|
802
|
-
});
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
//# sourceMappingURL=buildPolymorphicJoinWhere.spec.js.map
|