@xata.io/drizzle 0.0.0-alpha.ve2d648b → 0.0.0-alpha.ve304656137a7c41a3f10b52dcfd4784695e12cb2
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/.turbo/turbo-build.log +16 -5
- package/CHANGELOG.md +133 -2
- package/README.md +3 -0
- package/dist/index.cjs +98 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +47 -65
- package/dist/index.mjs +96 -123
- package/dist/index.mjs.map +1 -1
- package/dist/pg.cjs +215 -0
- package/dist/pg.cjs.map +1 -0
- package/dist/pg.d.ts +61 -0
- package/dist/pg.mjs +209 -0
- package/dist/pg.mjs.map +1 -0
- package/package.json +12 -4
- package/test/core.schema.ts +93 -0
- package/test/core.test.ts +3768 -0
- package/test/drizzle.test.ts +6293 -0
- package/test/migrate/0000_puzzling_flatman.sql +37 -0
- package/test/migrate/0001_test.sql +5 -0
- package/test/migrate/meta/0000_snapshot.json +275 -0
- package/test/migrate/meta/_journal.json +20 -0
- package/test/relational.schema.ts +85 -0
- package/test/relational.test.ts +6294 -0
- package/test/schema.ts +85 -0
@@ -0,0 +1,3768 @@
|
|
1
|
+
import { BaseClient, HostProvider, parseProviderString, XataApiClient } from '@xata.io/client';
|
2
|
+
import 'dotenv/config';
|
3
|
+
import {
|
4
|
+
and,
|
5
|
+
arrayContained,
|
6
|
+
arrayContains,
|
7
|
+
arrayOverlaps,
|
8
|
+
asc,
|
9
|
+
avg,
|
10
|
+
avgDistinct,
|
11
|
+
count,
|
12
|
+
countDistinct,
|
13
|
+
eq,
|
14
|
+
exists,
|
15
|
+
gt,
|
16
|
+
gte,
|
17
|
+
inArray,
|
18
|
+
lt,
|
19
|
+
max,
|
20
|
+
min,
|
21
|
+
name,
|
22
|
+
placeholder,
|
23
|
+
sql,
|
24
|
+
sum,
|
25
|
+
sumDistinct,
|
26
|
+
TransactionRollbackError,
|
27
|
+
type SQL,
|
28
|
+
type SQLWrapper
|
29
|
+
} from 'drizzle-orm';
|
30
|
+
import { drizzle as drizzlePg, type NodePgDatabase } from 'drizzle-orm/node-postgres';
|
31
|
+
import { migrate as migratePg } from 'drizzle-orm/node-postgres/migrator';
|
32
|
+
import {
|
33
|
+
alias,
|
34
|
+
boolean,
|
35
|
+
char,
|
36
|
+
date,
|
37
|
+
except,
|
38
|
+
exceptAll,
|
39
|
+
foreignKey,
|
40
|
+
getMaterializedViewConfig,
|
41
|
+
getTableConfig,
|
42
|
+
getViewConfig,
|
43
|
+
integer,
|
44
|
+
intersect,
|
45
|
+
intersectAll,
|
46
|
+
interval,
|
47
|
+
jsonb,
|
48
|
+
numeric,
|
49
|
+
pgEnum,
|
50
|
+
pgMaterializedView,
|
51
|
+
pgTable,
|
52
|
+
pgTableCreator,
|
53
|
+
uuid as pgUuid,
|
54
|
+
pgView,
|
55
|
+
primaryKey,
|
56
|
+
serial,
|
57
|
+
text,
|
58
|
+
time,
|
59
|
+
timestamp,
|
60
|
+
union,
|
61
|
+
unionAll,
|
62
|
+
unique,
|
63
|
+
uniqueKeyName,
|
64
|
+
varchar,
|
65
|
+
type PgColumn
|
66
|
+
} from 'drizzle-orm/pg-core';
|
67
|
+
import { Client } from 'pg';
|
68
|
+
import { v4 as uuid } from 'uuid';
|
69
|
+
import { afterAll, afterEach, assert, beforeAll, beforeEach, describe, test } from 'vitest';
|
70
|
+
import * as schema from './core.schema';
|
71
|
+
import { drizzle as drizzleHttp } from '../src/http';
|
72
|
+
import { migrate as migrateHttp } from '../src/http/migrator';
|
73
|
+
import path from 'path';
|
74
|
+
|
75
|
+
const {
|
76
|
+
usersTable,
|
77
|
+
cities2Table,
|
78
|
+
users2Table,
|
79
|
+
aggregateTable,
|
80
|
+
orders,
|
81
|
+
citiesTable,
|
82
|
+
courseCategoriesTable,
|
83
|
+
coursesTable,
|
84
|
+
network,
|
85
|
+
salEmp,
|
86
|
+
usersMigratorTable
|
87
|
+
} = schema;
|
88
|
+
|
89
|
+
const notSupported = test.skip;
|
90
|
+
const fixme = test.skip;
|
91
|
+
|
92
|
+
const ENABLE_LOGGING = true;
|
93
|
+
|
94
|
+
declare module 'vitest' {
|
95
|
+
export interface TestContext {
|
96
|
+
db2: NodePgDatabase<typeof schema>; // | XataHttpDatabase<typeof schema>;
|
97
|
+
client?: Client;
|
98
|
+
branch: string;
|
99
|
+
migrate: typeof migratePg; // | typeof migrateHttp;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
const apiKey = process.env.XATA_API_KEY ?? '';
|
104
|
+
if (apiKey === '') throw new Error('XATA_API_KEY environment variable is not set');
|
105
|
+
|
106
|
+
const workspace = (process.env.XATA_WORKSPACE ?? '').split('-').pop() ?? '';
|
107
|
+
if (workspace === '') throw new Error('XATA_WORKSPACE environment variable is not set');
|
108
|
+
|
109
|
+
const host = parseProviderString(process.env.XATA_API_PROVIDER) ?? 'production';
|
110
|
+
|
111
|
+
// TODO: Branches for pgroll only work in some regions for now
|
112
|
+
// const region = process.env.XATA_REGION || 'us-east-1';
|
113
|
+
const region =
|
114
|
+
host === 'production' ? 'us-east-1' : host === 'staging' ? 'eu-west-1' : process.env.XATA_REGION || 'us-east-1';
|
115
|
+
|
116
|
+
const database = `drizzle-test-${Math.random().toString(36).substring(7)}`;
|
117
|
+
|
118
|
+
const api = new XataApiClient({ apiKey, host, clientName: 'sdk-tests' });
|
119
|
+
|
120
|
+
function getDomain(host: HostProvider) {
|
121
|
+
switch (host) {
|
122
|
+
case 'production':
|
123
|
+
return 'xata.sh';
|
124
|
+
case 'staging':
|
125
|
+
return 'staging-xata.dev';
|
126
|
+
case 'dev':
|
127
|
+
return 'dev-xata.dev';
|
128
|
+
case 'local':
|
129
|
+
return 'localhost:6001';
|
130
|
+
default:
|
131
|
+
return host.workspaces;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
function getDrizzleClient(type: string, branch: string) {
|
136
|
+
if (type === 'http') {
|
137
|
+
const xata = new BaseClient({
|
138
|
+
apiKey,
|
139
|
+
host,
|
140
|
+
clientName: 'sdk-tests',
|
141
|
+
databaseURL: `https://${workspace}.${region}.${getDomain(host)}/db/${database}`,
|
142
|
+
branch
|
143
|
+
});
|
144
|
+
|
145
|
+
return { db: drizzleHttp(xata, { schema, logger: ENABLE_LOGGING }) };
|
146
|
+
} else if (type === 'pg') {
|
147
|
+
const client = new Client({
|
148
|
+
connectionString: `postgresql://${workspace}:${apiKey}@${region}.sql.${getDomain(
|
149
|
+
host
|
150
|
+
)}:5432/${database}:${branch}`,
|
151
|
+
ssl: true
|
152
|
+
});
|
153
|
+
|
154
|
+
return { db: drizzlePg(client, { schema, logger: ENABLE_LOGGING }), client };
|
155
|
+
} else {
|
156
|
+
throw new Error(`Unknown type: ${type}`);
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
async function setupSetOperationSuite(db: NodePgDatabase<typeof schema>) {
|
161
|
+
await db.execute(sql`drop table if exists users2`);
|
162
|
+
await db.execute(sql`drop table if exists cities`);
|
163
|
+
await db.execute(
|
164
|
+
sql`
|
165
|
+
create table cities (
|
166
|
+
id serial primary key,
|
167
|
+
name text not null
|
168
|
+
)
|
169
|
+
`
|
170
|
+
);
|
171
|
+
await db.execute(
|
172
|
+
sql`
|
173
|
+
create table users2 (
|
174
|
+
id serial primary key,
|
175
|
+
name text not null,
|
176
|
+
city_id integer references cities(id)
|
177
|
+
)
|
178
|
+
`
|
179
|
+
);
|
180
|
+
|
181
|
+
await db.insert(cities2Table).values([
|
182
|
+
{ id: 1, name: 'New York' },
|
183
|
+
{ id: 2, name: 'London' },
|
184
|
+
{ id: 3, name: 'Tampa' }
|
185
|
+
]);
|
186
|
+
|
187
|
+
await db.insert(users2Table).values([
|
188
|
+
{ id: 1, name: 'John', cityId: 1 },
|
189
|
+
{ id: 2, name: 'Jane', cityId: 2 },
|
190
|
+
{ id: 3, name: 'Jack', cityId: 3 },
|
191
|
+
{ id: 4, name: 'Peter', cityId: 3 },
|
192
|
+
{ id: 5, name: 'Ben', cityId: 2 },
|
193
|
+
{ id: 6, name: 'Jill', cityId: 1 },
|
194
|
+
{ id: 7, name: 'Mary', cityId: 2 },
|
195
|
+
{ id: 8, name: 'Sally', cityId: 1 }
|
196
|
+
]);
|
197
|
+
}
|
198
|
+
|
199
|
+
async function setupAggregateFunctionsSuite(db: NodePgDatabase<typeof schema>) {
|
200
|
+
await db.execute(sql`drop table if exists "aggregate_table"`);
|
201
|
+
await db.execute(
|
202
|
+
sql`
|
203
|
+
create table "aggregate_table" (
|
204
|
+
"id" serial not null,
|
205
|
+
"name" text not null,
|
206
|
+
"a" integer,
|
207
|
+
"b" integer,
|
208
|
+
"c" integer,
|
209
|
+
"null_only" integer
|
210
|
+
);
|
211
|
+
`
|
212
|
+
);
|
213
|
+
await db.insert(aggregateTable).values([
|
214
|
+
{ name: 'value 1', a: 5, b: 10, c: 20 },
|
215
|
+
{ name: 'value 1', a: 5, b: 20, c: 30 },
|
216
|
+
{ name: 'value 2', a: 10, b: 50, c: 60 },
|
217
|
+
{ name: 'value 3', a: 20, b: 20, c: null },
|
218
|
+
{ name: 'value 4', a: null, b: 90, c: 120 },
|
219
|
+
{ name: 'value 5', a: 80, b: 10, c: null },
|
220
|
+
{ name: 'value 6', a: null, b: null, c: 150 }
|
221
|
+
]);
|
222
|
+
}
|
223
|
+
|
224
|
+
describe.concurrent.each([{ type: 'pg' }, { type: 'http' }])('Drizzle core $type', ({ type }) => {
|
225
|
+
beforeAll(async () => {
|
226
|
+
await api.database.createDatabase({
|
227
|
+
workspace,
|
228
|
+
database,
|
229
|
+
data: { region, branchName: 'main' },
|
230
|
+
headers: { 'X-Features': 'feat-pgroll-migrations=1' }
|
231
|
+
});
|
232
|
+
|
233
|
+
await waitForReplication();
|
234
|
+
|
235
|
+
// For now, run the migrations via wire protocol
|
236
|
+
const { client, db } = getDrizzleClient('pg', 'main');
|
237
|
+
await client?.connect();
|
238
|
+
|
239
|
+
await db.execute(sql`drop schema public cascade`);
|
240
|
+
await db.execute(sql`create schema public`);
|
241
|
+
await db.execute(
|
242
|
+
sql`
|
243
|
+
create table users (
|
244
|
+
id serial primary key,
|
245
|
+
name text not null,
|
246
|
+
verified boolean not null default false,
|
247
|
+
jsonb jsonb,
|
248
|
+
created_at timestamptz not null default now()
|
249
|
+
)
|
250
|
+
`
|
251
|
+
);
|
252
|
+
await db.execute(
|
253
|
+
sql`
|
254
|
+
create table cities (
|
255
|
+
id serial primary key,
|
256
|
+
name text not null,
|
257
|
+
state char(2)
|
258
|
+
)
|
259
|
+
`
|
260
|
+
);
|
261
|
+
await db.execute(
|
262
|
+
sql`
|
263
|
+
create table users2 (
|
264
|
+
id serial primary key,
|
265
|
+
name text not null,
|
266
|
+
city_id integer references cities(id)
|
267
|
+
)
|
268
|
+
`
|
269
|
+
);
|
270
|
+
await db.execute(
|
271
|
+
sql`
|
272
|
+
create table course_categories (
|
273
|
+
id serial primary key,
|
274
|
+
name text not null
|
275
|
+
)
|
276
|
+
`
|
277
|
+
);
|
278
|
+
await db.execute(
|
279
|
+
sql`
|
280
|
+
create table courses (
|
281
|
+
id serial primary key,
|
282
|
+
name text not null,
|
283
|
+
category_id integer references course_categories(id)
|
284
|
+
)
|
285
|
+
`
|
286
|
+
);
|
287
|
+
await db.execute(
|
288
|
+
sql`
|
289
|
+
create table orders (
|
290
|
+
id serial primary key,
|
291
|
+
region text not null,
|
292
|
+
product text not null,
|
293
|
+
amount integer not null,
|
294
|
+
quantity integer not null
|
295
|
+
)
|
296
|
+
`
|
297
|
+
);
|
298
|
+
await db.execute(
|
299
|
+
sql`
|
300
|
+
create table network_table (
|
301
|
+
inet inet not null,
|
302
|
+
cidr cidr not null,
|
303
|
+
macaddr macaddr not null,
|
304
|
+
macaddr8 macaddr8 not null
|
305
|
+
)
|
306
|
+
`
|
307
|
+
);
|
308
|
+
await db.execute(
|
309
|
+
sql`
|
310
|
+
create table sal_emp (
|
311
|
+
name text not null,
|
312
|
+
pay_by_quarter integer[] not null,
|
313
|
+
schedule text[][] not null
|
314
|
+
)
|
315
|
+
`
|
316
|
+
);
|
317
|
+
await db.execute(
|
318
|
+
sql`
|
319
|
+
create table tictactoe (
|
320
|
+
squares integer[3][3] not null
|
321
|
+
)
|
322
|
+
`
|
323
|
+
);
|
324
|
+
|
325
|
+
await client?.end();
|
326
|
+
});
|
327
|
+
|
328
|
+
afterAll(async () => {
|
329
|
+
await api.database.deleteDatabase({ workspace, database });
|
330
|
+
});
|
331
|
+
|
332
|
+
beforeEach(async (ctx) => {
|
333
|
+
ctx.branch = `test-${Math.random().toString(36).substring(7)}`;
|
334
|
+
await api.branches.createBranch({ workspace, database, region, branch: ctx.branch, from: 'main' });
|
335
|
+
|
336
|
+
const { db, client } = getDrizzleClient(type, ctx.branch);
|
337
|
+
await client?.connect();
|
338
|
+
|
339
|
+
// @ts-expect-error
|
340
|
+
ctx.db2 = db;
|
341
|
+
ctx.client = client;
|
342
|
+
// @ts-expect-error
|
343
|
+
ctx.migrate = type === 'pg' ? migratePg : migrateHttp;
|
344
|
+
});
|
345
|
+
|
346
|
+
afterEach(async (ctx) => {
|
347
|
+
await ctx.client?.end();
|
348
|
+
await api.branches.deleteBranch({ workspace, database, region, branch: ctx.branch });
|
349
|
+
});
|
350
|
+
|
351
|
+
/*
|
352
|
+
[Find Many] One relation users+posts
|
353
|
+
*/
|
354
|
+
|
355
|
+
test('table configs: unique third param', async (ctx) => {
|
356
|
+
const cities1Table = pgTable(
|
357
|
+
'cities1',
|
358
|
+
{
|
359
|
+
id: serial('id').primaryKey(),
|
360
|
+
name: text('name').notNull(),
|
361
|
+
state: char('state', { length: 2 })
|
362
|
+
},
|
363
|
+
(t) => ({
|
364
|
+
f: unique('custom_name').on(t.name, t.state).nullsNotDistinct(),
|
365
|
+
f1: unique('custom_name1').on(t.name, t.state)
|
366
|
+
})
|
367
|
+
);
|
368
|
+
|
369
|
+
const tableConfig = getTableConfig(cities1Table);
|
370
|
+
|
371
|
+
ctx.expect(tableConfig.uniqueConstraints.length === 2);
|
372
|
+
|
373
|
+
ctx.expect(tableConfig.uniqueConstraints[0]?.name === 'custom_name');
|
374
|
+
ctx.expect(tableConfig.uniqueConstraints[0]?.nullsNotDistinct);
|
375
|
+
assert.deepEqual(
|
376
|
+
tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name),
|
377
|
+
['name', 'state']
|
378
|
+
);
|
379
|
+
|
380
|
+
ctx.expect(tableConfig.uniqueConstraints[1]?.name, 'custom_name1');
|
381
|
+
ctx.expect(!tableConfig.uniqueConstraints[1]?.nullsNotDistinct);
|
382
|
+
assert.deepEqual(
|
383
|
+
tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name),
|
384
|
+
['name', 'state']
|
385
|
+
);
|
386
|
+
});
|
387
|
+
|
388
|
+
test('table configs: unique in column', async (ctx) => {
|
389
|
+
const cities1Table = pgTable('cities1', {
|
390
|
+
id: serial('id').primaryKey(),
|
391
|
+
name: text('name').notNull().unique(),
|
392
|
+
state: char('state', { length: 2 }).unique('custom'),
|
393
|
+
field: char('field', { length: 2 }).unique('custom_field', { nulls: 'not distinct' })
|
394
|
+
});
|
395
|
+
|
396
|
+
const tableConfig = getTableConfig(cities1Table);
|
397
|
+
|
398
|
+
const columnName = tableConfig.columns.find((it) => it.name === 'name');
|
399
|
+
ctx.expect(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name]));
|
400
|
+
ctx.expect(columnName?.isUnique);
|
401
|
+
|
402
|
+
const columnState = tableConfig.columns.find((it) => it.name === 'state');
|
403
|
+
ctx.expect(columnState?.uniqueName === 'custom');
|
404
|
+
ctx.expect(columnState?.isUnique);
|
405
|
+
|
406
|
+
const columnField = tableConfig.columns.find((it) => it.name === 'field');
|
407
|
+
ctx.expect(columnField?.uniqueName === 'custom_field');
|
408
|
+
ctx.expect(columnField?.isUnique);
|
409
|
+
ctx.expect(columnField?.uniqueType === 'not distinct');
|
410
|
+
});
|
411
|
+
|
412
|
+
test('table config: foreign keys name', async (ctx) => {
|
413
|
+
const table = pgTable(
|
414
|
+
'cities',
|
415
|
+
{
|
416
|
+
id: serial('id').primaryKey(),
|
417
|
+
name: text('name').notNull(),
|
418
|
+
state: text('state')
|
419
|
+
},
|
420
|
+
(t) => ({
|
421
|
+
f: foreignKey({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk' })
|
422
|
+
})
|
423
|
+
);
|
424
|
+
|
425
|
+
const tableConfig = getTableConfig(table);
|
426
|
+
|
427
|
+
ctx.expect(tableConfig.foreignKeys.length === 1);
|
428
|
+
ctx.expect(tableConfig.foreignKeys[0]!.getName() === 'custom_fk');
|
429
|
+
});
|
430
|
+
|
431
|
+
test('table config: primary keys name', async (ctx) => {
|
432
|
+
const table = pgTable(
|
433
|
+
'cities',
|
434
|
+
{
|
435
|
+
id: serial('id').primaryKey(),
|
436
|
+
name: text('name').notNull(),
|
437
|
+
state: text('state')
|
438
|
+
},
|
439
|
+
(t) => ({
|
440
|
+
f: primaryKey({ columns: [t.id, t.name], name: 'custom_pk' })
|
441
|
+
})
|
442
|
+
);
|
443
|
+
|
444
|
+
const tableConfig = getTableConfig(table);
|
445
|
+
|
446
|
+
ctx.expect(tableConfig.primaryKeys.length === 1);
|
447
|
+
ctx.expect(tableConfig.primaryKeys[0]!.getName() === 'custom_pk');
|
448
|
+
});
|
449
|
+
|
450
|
+
test('select all fields', async (ctx) => {
|
451
|
+
const now = Date.now();
|
452
|
+
|
453
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
454
|
+
const result = await ctx.db2.select().from(usersTable);
|
455
|
+
|
456
|
+
ctx.expect(result[0]!.createdAt instanceof Date);
|
457
|
+
ctx.expect(Math.abs(result[0]!.createdAt.getTime() - now) < 100);
|
458
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]);
|
459
|
+
});
|
460
|
+
|
461
|
+
test('select sql', async (ctx) => {
|
462
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
463
|
+
const users = await ctx.db2
|
464
|
+
.select({
|
465
|
+
name: sql`upper(${usersTable.name})`
|
466
|
+
})
|
467
|
+
.from(usersTable);
|
468
|
+
|
469
|
+
assert.deepEqual(users, [{ name: 'JOHN' }]);
|
470
|
+
});
|
471
|
+
|
472
|
+
test('select typed sql', async (ctx) => {
|
473
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
474
|
+
|
475
|
+
const users = await ctx.db2
|
476
|
+
.select({
|
477
|
+
name: sql<string>`upper(${usersTable.name})`
|
478
|
+
})
|
479
|
+
.from(usersTable);
|
480
|
+
|
481
|
+
assert.deepEqual(users, [{ name: 'JOHN' }]);
|
482
|
+
});
|
483
|
+
|
484
|
+
test('$default function', async (ctx) => {
|
485
|
+
const insertedOrder = await ctx.db2
|
486
|
+
.insert(orders)
|
487
|
+
.values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 })
|
488
|
+
.returning();
|
489
|
+
const selectedOrder = await ctx.db2.select().from(orders);
|
490
|
+
|
491
|
+
assert.deepEqual(insertedOrder, [
|
492
|
+
{
|
493
|
+
id: 1,
|
494
|
+
amount: 1,
|
495
|
+
quantity: 1,
|
496
|
+
region: 'Ukraine',
|
497
|
+
product: 'random_string'
|
498
|
+
}
|
499
|
+
]);
|
500
|
+
|
501
|
+
assert.deepEqual(selectedOrder, [
|
502
|
+
{
|
503
|
+
id: 1,
|
504
|
+
amount: 1,
|
505
|
+
quantity: 1,
|
506
|
+
region: 'Ukraine',
|
507
|
+
product: 'random_string'
|
508
|
+
}
|
509
|
+
]);
|
510
|
+
});
|
511
|
+
|
512
|
+
test('select distinct', async (ctx) => {
|
513
|
+
const usersDistinctTable = pgTable('users_distinct', {
|
514
|
+
id: integer('id').notNull(),
|
515
|
+
name: text('name').notNull(),
|
516
|
+
age: integer('age').notNull()
|
517
|
+
});
|
518
|
+
|
519
|
+
await ctx.db2.execute(sql`drop table if exists ${usersDistinctTable}`);
|
520
|
+
await ctx.db2.execute(sql`create table ${usersDistinctTable} (id integer, name text, age integer)`);
|
521
|
+
|
522
|
+
await ctx.db2.insert(usersDistinctTable).values([
|
523
|
+
{ id: 1, name: 'John', age: 24 },
|
524
|
+
{ id: 1, name: 'John', age: 24 },
|
525
|
+
{ id: 2, name: 'John', age: 25 },
|
526
|
+
{ id: 1, name: 'Jane', age: 24 },
|
527
|
+
{ id: 1, name: 'Jane', age: 26 }
|
528
|
+
]);
|
529
|
+
const users1 = await ctx.db2
|
530
|
+
.selectDistinct()
|
531
|
+
.from(usersDistinctTable)
|
532
|
+
.orderBy(usersDistinctTable.id, usersDistinctTable.name);
|
533
|
+
const users2 = await ctx.db2
|
534
|
+
.selectDistinctOn([usersDistinctTable.id])
|
535
|
+
.from(usersDistinctTable)
|
536
|
+
.orderBy(usersDistinctTable.id);
|
537
|
+
const users3 = await ctx.db2
|
538
|
+
.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name })
|
539
|
+
.from(usersDistinctTable)
|
540
|
+
.orderBy(usersDistinctTable.name);
|
541
|
+
const users4 = await ctx.db2
|
542
|
+
.selectDistinctOn([usersDistinctTable.id, usersDistinctTable.age])
|
543
|
+
.from(usersDistinctTable)
|
544
|
+
.orderBy(usersDistinctTable.id, usersDistinctTable.age);
|
545
|
+
|
546
|
+
await ctx.db2.execute(sql`drop table ${usersDistinctTable}`);
|
547
|
+
|
548
|
+
assert.deepEqual(users1, [
|
549
|
+
{ id: 1, name: 'Jane', age: 24 },
|
550
|
+
{ id: 1, name: 'Jane', age: 26 },
|
551
|
+
{ id: 1, name: 'John', age: 24 },
|
552
|
+
{ id: 2, name: 'John', age: 25 }
|
553
|
+
]);
|
554
|
+
|
555
|
+
assert.deepEqual(users2.length, 2);
|
556
|
+
assert.deepEqual(users2[0]?.id, 1);
|
557
|
+
assert.deepEqual(users2[1]?.id, 2);
|
558
|
+
|
559
|
+
assert.deepEqual(users3.length, 2);
|
560
|
+
assert.deepEqual(users3[0]?.name, 'Jane');
|
561
|
+
assert.deepEqual(users3[1]?.name, 'John');
|
562
|
+
|
563
|
+
assert.deepEqual(users4, [
|
564
|
+
{ id: 1, name: 'John', age: 24 },
|
565
|
+
{ id: 1, name: 'Jane', age: 26 },
|
566
|
+
{ id: 2, name: 'John', age: 25 }
|
567
|
+
]);
|
568
|
+
});
|
569
|
+
|
570
|
+
test('insert returning sql', async (ctx) => {
|
571
|
+
const users = await ctx.db2
|
572
|
+
.insert(usersTable)
|
573
|
+
.values({ name: 'John' })
|
574
|
+
.returning({
|
575
|
+
name: sql`upper(${usersTable.name})`
|
576
|
+
});
|
577
|
+
|
578
|
+
assert.deepEqual(users, [{ name: 'JOHN' }]);
|
579
|
+
});
|
580
|
+
|
581
|
+
test('delete returning sql', async (ctx) => {
|
582
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
583
|
+
const users = await ctx.db2
|
584
|
+
.delete(usersTable)
|
585
|
+
.where(eq(usersTable.name, 'John'))
|
586
|
+
.returning({
|
587
|
+
name: sql`upper(${usersTable.name})`
|
588
|
+
});
|
589
|
+
|
590
|
+
assert.deepEqual(users, [{ name: 'JOHN' }]);
|
591
|
+
});
|
592
|
+
|
593
|
+
test('update returning sql', async (ctx) => {
|
594
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
595
|
+
const users = await ctx.db2
|
596
|
+
.update(usersTable)
|
597
|
+
.set({ name: 'Jane' })
|
598
|
+
.where(eq(usersTable.name, 'John'))
|
599
|
+
.returning({
|
600
|
+
name: sql`upper(${usersTable.name})`
|
601
|
+
});
|
602
|
+
|
603
|
+
assert.deepEqual(users, [{ name: 'JANE' }]);
|
604
|
+
});
|
605
|
+
|
606
|
+
test('update with returning all fields', async (ctx) => {
|
607
|
+
const now = Date.now();
|
608
|
+
|
609
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
610
|
+
const users = await ctx.db2.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning();
|
611
|
+
|
612
|
+
ctx.expect(users[0]!.createdAt instanceof Date);
|
613
|
+
ctx.expect(Math.abs(users[0]!.createdAt.getTime() - now) < 100);
|
614
|
+
assert.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]);
|
615
|
+
});
|
616
|
+
|
617
|
+
test('update with returning partial', async (ctx) => {
|
618
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
619
|
+
const users = await ctx.db2.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({
|
620
|
+
id: usersTable.id,
|
621
|
+
name: usersTable.name
|
622
|
+
});
|
623
|
+
|
624
|
+
assert.deepEqual(users, [{ id: 1, name: 'Jane' }]);
|
625
|
+
});
|
626
|
+
|
627
|
+
test('delete with returning all fields', async (ctx) => {
|
628
|
+
const now = Date.now();
|
629
|
+
|
630
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
631
|
+
const users = await ctx.db2.delete(usersTable).where(eq(usersTable.name, 'John')).returning();
|
632
|
+
|
633
|
+
ctx.expect(users[0]!.createdAt instanceof Date);
|
634
|
+
ctx.expect(Math.abs(users[0]!.createdAt.getTime() - now) < 100);
|
635
|
+
assert.deepEqual(users, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]);
|
636
|
+
});
|
637
|
+
|
638
|
+
test('delete with returning partial', async (ctx) => {
|
639
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
640
|
+
const users = await ctx.db2.delete(usersTable).where(eq(usersTable.name, 'John')).returning({
|
641
|
+
id: usersTable.id,
|
642
|
+
name: usersTable.name
|
643
|
+
});
|
644
|
+
|
645
|
+
assert.deepEqual(users, [{ id: 1, name: 'John' }]);
|
646
|
+
});
|
647
|
+
|
648
|
+
test('insert + select', async (ctx) => {
|
649
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
650
|
+
const result = await ctx.db2.select().from(usersTable);
|
651
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]);
|
652
|
+
|
653
|
+
await ctx.db2.insert(usersTable).values({ name: 'Jane' });
|
654
|
+
const result2 = await ctx.db2.select().from(usersTable);
|
655
|
+
assert.deepEqual(result2, [
|
656
|
+
{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt },
|
657
|
+
{ id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }
|
658
|
+
]);
|
659
|
+
});
|
660
|
+
|
661
|
+
test('json insert', async (ctx) => {
|
662
|
+
await ctx.db2.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] });
|
663
|
+
const result = await ctx.db2
|
664
|
+
.select({
|
665
|
+
id: usersTable.id,
|
666
|
+
name: usersTable.name,
|
667
|
+
jsonb: usersTable.jsonb
|
668
|
+
})
|
669
|
+
.from(usersTable);
|
670
|
+
|
671
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]);
|
672
|
+
});
|
673
|
+
|
674
|
+
test('char insert', async (ctx) => {
|
675
|
+
await ctx.db2.insert(citiesTable).values({ name: 'Austin', state: 'TX' });
|
676
|
+
const result = await ctx.db2
|
677
|
+
.select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state })
|
678
|
+
.from(citiesTable);
|
679
|
+
|
680
|
+
assert.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]);
|
681
|
+
});
|
682
|
+
|
683
|
+
test('char update', async (ctx) => {
|
684
|
+
await ctx.db2.insert(citiesTable).values({ name: 'Austin', state: 'TX' });
|
685
|
+
await ctx.db2.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1));
|
686
|
+
const result = await ctx.db2
|
687
|
+
.select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state })
|
688
|
+
.from(citiesTable);
|
689
|
+
|
690
|
+
assert.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]);
|
691
|
+
});
|
692
|
+
|
693
|
+
test('char delete', async (ctx) => {
|
694
|
+
await ctx.db2.insert(citiesTable).values({ name: 'Austin', state: 'TX' });
|
695
|
+
await ctx.db2.delete(citiesTable).where(eq(citiesTable.state, 'TX'));
|
696
|
+
const result = await ctx.db2
|
697
|
+
.select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state })
|
698
|
+
.from(citiesTable);
|
699
|
+
|
700
|
+
assert.deepEqual(result, []);
|
701
|
+
});
|
702
|
+
|
703
|
+
test('insert with overridden default values', async (ctx) => {
|
704
|
+
await ctx.db2.insert(usersTable).values({ name: 'John', verified: true });
|
705
|
+
const result = await ctx.db2.select().from(usersTable);
|
706
|
+
|
707
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]);
|
708
|
+
});
|
709
|
+
|
710
|
+
test('insert many', async (ctx) => {
|
711
|
+
await ctx.db2
|
712
|
+
.insert(usersTable)
|
713
|
+
.values([
|
714
|
+
{ name: 'John' },
|
715
|
+
{ name: 'Bruce', jsonb: ['foo', 'bar'] },
|
716
|
+
{ name: 'Jane' },
|
717
|
+
{ name: 'Austin', verified: true }
|
718
|
+
]);
|
719
|
+
const result = await ctx.db2
|
720
|
+
.select({
|
721
|
+
id: usersTable.id,
|
722
|
+
name: usersTable.name,
|
723
|
+
jsonb: usersTable.jsonb,
|
724
|
+
verified: usersTable.verified
|
725
|
+
})
|
726
|
+
.from(usersTable);
|
727
|
+
|
728
|
+
assert.deepEqual(result, [
|
729
|
+
{ id: 1, name: 'John', jsonb: null, verified: false },
|
730
|
+
{ id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false },
|
731
|
+
{ id: 3, name: 'Jane', jsonb: null, verified: false },
|
732
|
+
{ id: 4, name: 'Austin', jsonb: null, verified: true }
|
733
|
+
]);
|
734
|
+
});
|
735
|
+
|
736
|
+
test('insert many with returning', async (ctx) => {
|
737
|
+
const result = await ctx.db2
|
738
|
+
.insert(usersTable)
|
739
|
+
.values([
|
740
|
+
{ name: 'John' },
|
741
|
+
{ name: 'Bruce', jsonb: ['foo', 'bar'] },
|
742
|
+
{ name: 'Jane' },
|
743
|
+
{ name: 'Austin', verified: true }
|
744
|
+
])
|
745
|
+
.returning({
|
746
|
+
id: usersTable.id,
|
747
|
+
name: usersTable.name,
|
748
|
+
jsonb: usersTable.jsonb,
|
749
|
+
verified: usersTable.verified
|
750
|
+
});
|
751
|
+
|
752
|
+
assert.deepEqual(result, [
|
753
|
+
{ id: 1, name: 'John', jsonb: null, verified: false },
|
754
|
+
{ id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false },
|
755
|
+
{ id: 3, name: 'Jane', jsonb: null, verified: false },
|
756
|
+
{ id: 4, name: 'Austin', jsonb: null, verified: true }
|
757
|
+
]);
|
758
|
+
});
|
759
|
+
|
760
|
+
test('select with group by as field', async (ctx) => {
|
761
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
762
|
+
|
763
|
+
const result = await ctx.db2.select({ name: usersTable.name }).from(usersTable).groupBy(usersTable.name);
|
764
|
+
|
765
|
+
assert.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]);
|
766
|
+
});
|
767
|
+
|
768
|
+
test('select with exists', async (ctx) => {
|
769
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
770
|
+
|
771
|
+
const user = alias(usersTable, 'user');
|
772
|
+
const result = await ctx.db2
|
773
|
+
.select({ name: usersTable.name })
|
774
|
+
.from(usersTable)
|
775
|
+
.where(
|
776
|
+
exists(
|
777
|
+
ctx.db2
|
778
|
+
.select({ one: sql`1` })
|
779
|
+
.from(user)
|
780
|
+
.where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))
|
781
|
+
)
|
782
|
+
);
|
783
|
+
|
784
|
+
assert.deepEqual(result, [{ name: 'John' }]);
|
785
|
+
});
|
786
|
+
|
787
|
+
test('select with group by as sql', async (ctx) => {
|
788
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
789
|
+
|
790
|
+
const result = await ctx.db2
|
791
|
+
.select({ name: usersTable.name })
|
792
|
+
.from(usersTable)
|
793
|
+
.groupBy(sql`${usersTable.name}`);
|
794
|
+
|
795
|
+
assert.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]);
|
796
|
+
});
|
797
|
+
|
798
|
+
test('select with group by as sql + column', async (ctx) => {
|
799
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
800
|
+
|
801
|
+
const result = await ctx.db2
|
802
|
+
.select({ name: usersTable.name })
|
803
|
+
.from(usersTable)
|
804
|
+
.groupBy(sql`${usersTable.name}`, usersTable.id);
|
805
|
+
|
806
|
+
assert.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]);
|
807
|
+
});
|
808
|
+
|
809
|
+
test('select with group by as column + sql', async (ctx) => {
|
810
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
811
|
+
|
812
|
+
const result = await ctx.db2
|
813
|
+
.select({ name: usersTable.name })
|
814
|
+
.from(usersTable)
|
815
|
+
.groupBy(usersTable.id, sql`${usersTable.name}`);
|
816
|
+
|
817
|
+
assert.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]);
|
818
|
+
});
|
819
|
+
|
820
|
+
test('select with group by complex query', async (ctx) => {
|
821
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
822
|
+
|
823
|
+
const result = await ctx.db2
|
824
|
+
.select({ name: usersTable.name })
|
825
|
+
.from(usersTable)
|
826
|
+
.groupBy(usersTable.id, sql`${usersTable.name}`)
|
827
|
+
.orderBy(asc(usersTable.name))
|
828
|
+
.limit(1);
|
829
|
+
|
830
|
+
assert.deepEqual(result, [{ name: 'Jane' }]);
|
831
|
+
});
|
832
|
+
|
833
|
+
test('build query', async (ctx) => {
|
834
|
+
const query = ctx.db2
|
835
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
836
|
+
.from(usersTable)
|
837
|
+
.groupBy(usersTable.id, usersTable.name)
|
838
|
+
.toSQL();
|
839
|
+
|
840
|
+
assert.deepEqual(query, {
|
841
|
+
sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"',
|
842
|
+
params: []
|
843
|
+
});
|
844
|
+
});
|
845
|
+
|
846
|
+
test('insert sql', async (ctx) => {
|
847
|
+
await ctx.db2.insert(usersTable).values({ name: sql`${'John'}` });
|
848
|
+
const result = await ctx.db2.select({ id: usersTable.id, name: usersTable.name }).from(usersTable);
|
849
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
850
|
+
});
|
851
|
+
|
852
|
+
test('partial join with alias', async (ctx) => {
|
853
|
+
const customerAlias = alias(usersTable, 'customer');
|
854
|
+
|
855
|
+
await ctx.db2.insert(usersTable).values([
|
856
|
+
{ id: 10, name: 'Ivan' },
|
857
|
+
{ id: 11, name: 'Hans' }
|
858
|
+
]);
|
859
|
+
const result = await ctx.db2
|
860
|
+
.select({
|
861
|
+
user: {
|
862
|
+
id: usersTable.id,
|
863
|
+
name: usersTable.name
|
864
|
+
},
|
865
|
+
customer: {
|
866
|
+
id: customerAlias.id,
|
867
|
+
name: customerAlias.name
|
868
|
+
}
|
869
|
+
})
|
870
|
+
.from(usersTable)
|
871
|
+
.leftJoin(customerAlias, eq(customerAlias.id, 11))
|
872
|
+
.where(eq(usersTable.id, 10));
|
873
|
+
|
874
|
+
assert.deepEqual(result, [
|
875
|
+
{
|
876
|
+
user: { id: 10, name: 'Ivan' },
|
877
|
+
customer: { id: 11, name: 'Hans' }
|
878
|
+
}
|
879
|
+
]);
|
880
|
+
});
|
881
|
+
|
882
|
+
test('full join with alias', async (ctx) => {
|
883
|
+
const pgTable = pgTableCreator((name) => `prefixed_${name}`);
|
884
|
+
|
885
|
+
const users = pgTable('users', {
|
886
|
+
id: serial('id').primaryKey(),
|
887
|
+
name: text('name').notNull()
|
888
|
+
});
|
889
|
+
|
890
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
891
|
+
await ctx.db2.execute(sql`create table ${users} (id serial primary key, name text not null)`);
|
892
|
+
|
893
|
+
const customers = alias(users, 'customer');
|
894
|
+
|
895
|
+
await ctx.db2.insert(users).values([
|
896
|
+
{ id: 10, name: 'Ivan' },
|
897
|
+
{ id: 11, name: 'Hans' }
|
898
|
+
]);
|
899
|
+
const result = await ctx.db2.select().from(users).leftJoin(customers, eq(customers.id, 11)).where(eq(users.id, 10));
|
900
|
+
|
901
|
+
assert.deepEqual(result, [
|
902
|
+
{
|
903
|
+
users: {
|
904
|
+
id: 10,
|
905
|
+
name: 'Ivan'
|
906
|
+
},
|
907
|
+
customer: {
|
908
|
+
id: 11,
|
909
|
+
name: 'Hans'
|
910
|
+
}
|
911
|
+
}
|
912
|
+
]);
|
913
|
+
|
914
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
915
|
+
});
|
916
|
+
|
917
|
+
test('select from alias', async (ctx) => {
|
918
|
+
const pgTable = pgTableCreator((name) => `prefixed_${name}`);
|
919
|
+
|
920
|
+
const users = pgTable('users', {
|
921
|
+
id: serial('id').primaryKey(),
|
922
|
+
name: text('name').notNull()
|
923
|
+
});
|
924
|
+
|
925
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
926
|
+
await ctx.db2.execute(sql`create table ${users} (id serial primary key, name text not null)`);
|
927
|
+
|
928
|
+
const user = alias(users, 'user');
|
929
|
+
const customers = alias(users, 'customer');
|
930
|
+
|
931
|
+
await ctx.db2.insert(users).values([
|
932
|
+
{ id: 10, name: 'Ivan' },
|
933
|
+
{ id: 11, name: 'Hans' }
|
934
|
+
]);
|
935
|
+
const result = await ctx.db2.select().from(user).leftJoin(customers, eq(customers.id, 11)).where(eq(user.id, 10));
|
936
|
+
|
937
|
+
assert.deepEqual(result, [
|
938
|
+
{
|
939
|
+
user: {
|
940
|
+
id: 10,
|
941
|
+
name: 'Ivan'
|
942
|
+
},
|
943
|
+
customer: {
|
944
|
+
id: 11,
|
945
|
+
name: 'Hans'
|
946
|
+
}
|
947
|
+
}
|
948
|
+
]);
|
949
|
+
|
950
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
951
|
+
});
|
952
|
+
|
953
|
+
test('insert with spaces', async (ctx) => {
|
954
|
+
await ctx.db2.insert(usersTable).values({ name: sql`'Jo h n'` });
|
955
|
+
const result = await ctx.db2.select({ id: usersTable.id, name: usersTable.name }).from(usersTable);
|
956
|
+
|
957
|
+
assert.deepEqual(result, [{ id: 1, name: 'Jo h n' }]);
|
958
|
+
});
|
959
|
+
|
960
|
+
test('prepared statement', async (ctx) => {
|
961
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
962
|
+
const statement = ctx.db2
|
963
|
+
.select({
|
964
|
+
id: usersTable.id,
|
965
|
+
name: usersTable.name
|
966
|
+
})
|
967
|
+
.from(usersTable)
|
968
|
+
.prepare('statement1');
|
969
|
+
const result = await statement.execute();
|
970
|
+
|
971
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
972
|
+
});
|
973
|
+
|
974
|
+
test('prepared statement reuse', async (ctx) => {
|
975
|
+
const stmt = ctx.db2
|
976
|
+
.insert(usersTable)
|
977
|
+
.values({
|
978
|
+
verified: true,
|
979
|
+
name: placeholder('name')
|
980
|
+
})
|
981
|
+
.prepare('stmt2');
|
982
|
+
|
983
|
+
for (let i = 0; i < 10; i++) {
|
984
|
+
await stmt.execute({ name: `John ${i}` });
|
985
|
+
}
|
986
|
+
|
987
|
+
const result = await ctx.db2
|
988
|
+
.select({
|
989
|
+
id: usersTable.id,
|
990
|
+
name: usersTable.name,
|
991
|
+
verified: usersTable.verified
|
992
|
+
})
|
993
|
+
.from(usersTable);
|
994
|
+
|
995
|
+
assert.deepEqual(result, [
|
996
|
+
{ id: 1, name: 'John 0', verified: true },
|
997
|
+
{ id: 2, name: 'John 1', verified: true },
|
998
|
+
{ id: 3, name: 'John 2', verified: true },
|
999
|
+
{ id: 4, name: 'John 3', verified: true },
|
1000
|
+
{ id: 5, name: 'John 4', verified: true },
|
1001
|
+
{ id: 6, name: 'John 5', verified: true },
|
1002
|
+
{ id: 7, name: 'John 6', verified: true },
|
1003
|
+
{ id: 8, name: 'John 7', verified: true },
|
1004
|
+
{ id: 9, name: 'John 8', verified: true },
|
1005
|
+
{ id: 10, name: 'John 9', verified: true }
|
1006
|
+
]);
|
1007
|
+
});
|
1008
|
+
|
1009
|
+
test('prepared statement with placeholder in .where', async (ctx) => {
|
1010
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
1011
|
+
const stmt = ctx.db2
|
1012
|
+
.select({
|
1013
|
+
id: usersTable.id,
|
1014
|
+
name: usersTable.name
|
1015
|
+
})
|
1016
|
+
.from(usersTable)
|
1017
|
+
.where(eq(usersTable.id, placeholder('id')))
|
1018
|
+
.prepare('stmt3');
|
1019
|
+
const result = await stmt.execute({ id: 1 });
|
1020
|
+
|
1021
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
1022
|
+
});
|
1023
|
+
|
1024
|
+
test('prepared statement with placeholder in .limit', async (ctx) => {
|
1025
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
1026
|
+
const stmt = ctx.db2
|
1027
|
+
.select({
|
1028
|
+
id: usersTable.id,
|
1029
|
+
name: usersTable.name
|
1030
|
+
})
|
1031
|
+
.from(usersTable)
|
1032
|
+
.where(eq(usersTable.id, placeholder('id')))
|
1033
|
+
.limit(placeholder('limit'))
|
1034
|
+
.prepare('stmt_limit');
|
1035
|
+
|
1036
|
+
const result = await stmt.execute({ id: 1, limit: 1 });
|
1037
|
+
|
1038
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
1039
|
+
ctx.expect(result.length === 1);
|
1040
|
+
});
|
1041
|
+
|
1042
|
+
test('prepared statement with placeholder in .offset', async (ctx) => {
|
1043
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]);
|
1044
|
+
const stmt = ctx.db2
|
1045
|
+
.select({
|
1046
|
+
id: usersTable.id,
|
1047
|
+
name: usersTable.name
|
1048
|
+
})
|
1049
|
+
.from(usersTable)
|
1050
|
+
.offset(placeholder('offset'))
|
1051
|
+
.prepare('stmt_offset');
|
1052
|
+
|
1053
|
+
const result = await stmt.execute({ offset: 1 });
|
1054
|
+
|
1055
|
+
assert.deepEqual(result, [{ id: 2, name: 'John1' }]);
|
1056
|
+
});
|
1057
|
+
|
1058
|
+
// TODO change tests to new structure
|
1059
|
+
fixme('migrator : default migration strategy', async (ctx) => {
|
1060
|
+
await ctx.db2.execute(sql`drop table if exists all_columns`);
|
1061
|
+
await ctx.db2.execute(sql`drop table if exists users12`);
|
1062
|
+
await ctx.db2.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`);
|
1063
|
+
|
1064
|
+
await ctx.migrate(ctx.db2, { migrationsFolder: path.join(__dirname, 'migrate') });
|
1065
|
+
|
1066
|
+
await ctx.db2.insert(usersMigratorTable).values({ name: 'John', email: 'email' });
|
1067
|
+
|
1068
|
+
const result = await ctx.db2.select().from(usersMigratorTable);
|
1069
|
+
|
1070
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]);
|
1071
|
+
|
1072
|
+
await ctx.db2.execute(sql`drop table all_columns`);
|
1073
|
+
await ctx.db2.execute(sql`drop table users12`);
|
1074
|
+
await ctx.db2.execute(sql`drop table "drizzle"."__drizzle_migrations"`);
|
1075
|
+
});
|
1076
|
+
|
1077
|
+
fixme('migrator : migrate with custom schema', async (ctx) => {
|
1078
|
+
const customSchema = randomString();
|
1079
|
+
await ctx.db2.execute(sql`drop table if exists all_columns`);
|
1080
|
+
await ctx.db2.execute(sql`drop table if exists users12`);
|
1081
|
+
await ctx.db2.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`);
|
1082
|
+
|
1083
|
+
await ctx.migrate(ctx.db2, { migrationsFolder: path.join(__dirname, 'migrate'), migrationsSchema: customSchema });
|
1084
|
+
|
1085
|
+
// test if the custom migrations table was created
|
1086
|
+
const { rowCount } = await ctx.db2.execute(
|
1087
|
+
sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`
|
1088
|
+
);
|
1089
|
+
ctx.expect(rowCount !== null && rowCount > 0);
|
1090
|
+
|
1091
|
+
// test if the migrated table are working as expected
|
1092
|
+
await ctx.db2.insert(usersMigratorTable).values({ name: 'John', email: 'email' });
|
1093
|
+
const result = await ctx.db2.select().from(usersMigratorTable);
|
1094
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]);
|
1095
|
+
|
1096
|
+
await ctx.db2.execute(sql`drop table all_columns`);
|
1097
|
+
await ctx.db2.execute(sql`drop table users12`);
|
1098
|
+
await ctx.db2.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`);
|
1099
|
+
});
|
1100
|
+
|
1101
|
+
fixme('migrator : migrate with custom table', async (ctx) => {
|
1102
|
+
const customTable = randomString();
|
1103
|
+
await ctx.db2.execute(sql`drop table if exists all_columns`);
|
1104
|
+
await ctx.db2.execute(sql`drop table if exists users12`);
|
1105
|
+
await ctx.db2.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`);
|
1106
|
+
|
1107
|
+
await ctx.migrate(ctx.db2, { migrationsFolder: path.join(__dirname, 'migrate'), migrationsTable: customTable });
|
1108
|
+
|
1109
|
+
// test if the custom migrations table was created
|
1110
|
+
const { rowCount } = await ctx.db2.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`);
|
1111
|
+
ctx.expect(rowCount !== null && rowCount > 0);
|
1112
|
+
|
1113
|
+
// test if the migrated table are working as expected
|
1114
|
+
await ctx.db2.insert(usersMigratorTable).values({ name: 'John', email: 'email' });
|
1115
|
+
const result = await ctx.db2.select().from(usersMigratorTable);
|
1116
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]);
|
1117
|
+
|
1118
|
+
await ctx.db2.execute(sql`drop table all_columns`);
|
1119
|
+
await ctx.db2.execute(sql`drop table users12`);
|
1120
|
+
await ctx.db2.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`);
|
1121
|
+
});
|
1122
|
+
|
1123
|
+
fixme('migrator : migrate with custom table and custom schema', async (ctx) => {
|
1124
|
+
const customTable = randomString();
|
1125
|
+
const customSchema = randomString();
|
1126
|
+
await ctx.db2.execute(sql`drop table if exists all_columns`);
|
1127
|
+
await ctx.db2.execute(sql`drop table if exists users12`);
|
1128
|
+
await ctx.db2.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`);
|
1129
|
+
|
1130
|
+
await ctx.migrate(ctx.db2, {
|
1131
|
+
migrationsFolder: path.join(__dirname, 'migrate'),
|
1132
|
+
migrationsTable: customTable,
|
1133
|
+
migrationsSchema: customSchema
|
1134
|
+
});
|
1135
|
+
|
1136
|
+
// test if the custom migrations table was created
|
1137
|
+
const { rowCount } = await ctx.db2.execute(
|
1138
|
+
sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`
|
1139
|
+
);
|
1140
|
+
ctx.expect(rowCount !== null && rowCount > 0);
|
1141
|
+
|
1142
|
+
// test if the migrated table are working as expected
|
1143
|
+
await ctx.db2.insert(usersMigratorTable).values({ name: 'John', email: 'email' });
|
1144
|
+
const result = await ctx.db2.select().from(usersMigratorTable);
|
1145
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]);
|
1146
|
+
|
1147
|
+
await ctx.db2.execute(sql`drop table all_columns`);
|
1148
|
+
await ctx.db2.execute(sql`drop table users12`);
|
1149
|
+
await ctx.db2.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`);
|
1150
|
+
});
|
1151
|
+
|
1152
|
+
test('insert via ctx.db2.execute + select via ctx.db2.execute', async (ctx) => {
|
1153
|
+
await ctx.db2.execute(sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`);
|
1154
|
+
|
1155
|
+
const result = await ctx.db2.execute<{ id: number; name: string }>(sql`select id, name from "users"`);
|
1156
|
+
assert.deepEqual(result.rows, [{ id: 1, name: 'John' }]);
|
1157
|
+
});
|
1158
|
+
|
1159
|
+
test('insert via ctx.db2.execute + returning', async (ctx) => {
|
1160
|
+
const inserted = await ctx.db2.execute<{ id: number; name: string }>(
|
1161
|
+
sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'}) returning ${usersTable.id}, ${
|
1162
|
+
usersTable.name
|
1163
|
+
}`
|
1164
|
+
);
|
1165
|
+
assert.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]);
|
1166
|
+
});
|
1167
|
+
|
1168
|
+
test('insert via ctx.db2.execute w/ query builder', async (ctx) => {
|
1169
|
+
const inserted = await ctx.db2.execute<Pick<typeof usersTable.$inferSelect, 'id' | 'name'>>(
|
1170
|
+
ctx.db2.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name })
|
1171
|
+
);
|
1172
|
+
assert.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]);
|
1173
|
+
});
|
1174
|
+
|
1175
|
+
test('Query check: Insert all defaults in 1 row', async (ctx) => {
|
1176
|
+
const users = pgTable('users', {
|
1177
|
+
id: serial('id').primaryKey(),
|
1178
|
+
name: text('name').default('Dan'),
|
1179
|
+
state: text('state')
|
1180
|
+
});
|
1181
|
+
|
1182
|
+
const query = ctx.db2.insert(users).values({}).toSQL();
|
1183
|
+
|
1184
|
+
assert.deepEqual(query, {
|
1185
|
+
sql: 'insert into "users" ("id", "name", "state") values (default, default, default)',
|
1186
|
+
params: []
|
1187
|
+
});
|
1188
|
+
});
|
1189
|
+
|
1190
|
+
test('Query check: Insert all defaults in multiple rows', async (ctx) => {
|
1191
|
+
const users = pgTable('users', {
|
1192
|
+
id: serial('id').primaryKey(),
|
1193
|
+
name: text('name').default('Dan'),
|
1194
|
+
state: text('state').default('UA')
|
1195
|
+
});
|
1196
|
+
|
1197
|
+
const query = ctx.db2.insert(users).values([{}, {}]).toSQL();
|
1198
|
+
|
1199
|
+
assert.deepEqual(query, {
|
1200
|
+
sql: 'insert into "users" ("id", "name", "state") values (default, default, default), (default, default, default)',
|
1201
|
+
params: []
|
1202
|
+
});
|
1203
|
+
});
|
1204
|
+
|
1205
|
+
test('Insert all defaults in 1 row', async (ctx) => {
|
1206
|
+
const users = pgTable('empty_insert_single', {
|
1207
|
+
id: serial('id').primaryKey(),
|
1208
|
+
name: text('name').default('Dan'),
|
1209
|
+
state: text('state')
|
1210
|
+
});
|
1211
|
+
|
1212
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
1213
|
+
|
1214
|
+
await ctx.db2.execute(sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`);
|
1215
|
+
|
1216
|
+
await ctx.db2.insert(users).values({});
|
1217
|
+
|
1218
|
+
const res = await ctx.db2.select().from(users);
|
1219
|
+
|
1220
|
+
assert.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]);
|
1221
|
+
});
|
1222
|
+
|
1223
|
+
test('Insert all defaults in multiple rows', async (ctx) => {
|
1224
|
+
const users = pgTable('empty_insert_multiple', {
|
1225
|
+
id: serial('id').primaryKey(),
|
1226
|
+
name: text('name').default('Dan'),
|
1227
|
+
state: text('state')
|
1228
|
+
});
|
1229
|
+
|
1230
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
1231
|
+
|
1232
|
+
await ctx.db2.execute(sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`);
|
1233
|
+
|
1234
|
+
await ctx.db2.insert(users).values([{}, {}]);
|
1235
|
+
|
1236
|
+
const res = await ctx.db2.select().from(users);
|
1237
|
+
|
1238
|
+
assert.deepEqual(res, [
|
1239
|
+
{ id: 1, name: 'Dan', state: null },
|
1240
|
+
{ id: 2, name: 'Dan', state: null }
|
1241
|
+
]);
|
1242
|
+
});
|
1243
|
+
|
1244
|
+
test('build query insert with onConflict do update', async (ctx) => {
|
1245
|
+
const query = ctx.db2
|
1246
|
+
.insert(usersTable)
|
1247
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
1248
|
+
.onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } })
|
1249
|
+
.toSQL();
|
1250
|
+
|
1251
|
+
assert.deepEqual(query, {
|
1252
|
+
sql: 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3',
|
1253
|
+
params: ['John', '["foo","bar"]', 'John1']
|
1254
|
+
});
|
1255
|
+
});
|
1256
|
+
|
1257
|
+
test('build query insert with onConflict do update / multiple columns', async (ctx) => {
|
1258
|
+
const query = ctx.db2
|
1259
|
+
.insert(usersTable)
|
1260
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
1261
|
+
.onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } })
|
1262
|
+
.toSQL();
|
1263
|
+
|
1264
|
+
assert.deepEqual(query, {
|
1265
|
+
sql: 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id","name") do update set "name" = $3',
|
1266
|
+
params: ['John', '["foo","bar"]', 'John1']
|
1267
|
+
});
|
1268
|
+
});
|
1269
|
+
|
1270
|
+
test('build query insert with onConflict do nothing', async (ctx) => {
|
1271
|
+
const query = ctx.db2
|
1272
|
+
.insert(usersTable)
|
1273
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
1274
|
+
.onConflictDoNothing()
|
1275
|
+
.toSQL();
|
1276
|
+
|
1277
|
+
assert.deepEqual(query, {
|
1278
|
+
sql: 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing',
|
1279
|
+
params: ['John', '["foo","bar"]']
|
1280
|
+
});
|
1281
|
+
});
|
1282
|
+
|
1283
|
+
test('build query insert with onConflict do nothing + target', async (ctx) => {
|
1284
|
+
const query = ctx.db2
|
1285
|
+
.insert(usersTable)
|
1286
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
1287
|
+
.onConflictDoNothing({ target: usersTable.id })
|
1288
|
+
.toSQL();
|
1289
|
+
|
1290
|
+
assert.deepEqual(query, {
|
1291
|
+
sql: 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing',
|
1292
|
+
params: ['John', '["foo","bar"]']
|
1293
|
+
});
|
1294
|
+
});
|
1295
|
+
|
1296
|
+
test('insert with onConflict do update', async (ctx) => {
|
1297
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
1298
|
+
|
1299
|
+
await ctx.db2
|
1300
|
+
.insert(usersTable)
|
1301
|
+
.values({ id: 1, name: 'John' })
|
1302
|
+
.onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } });
|
1303
|
+
|
1304
|
+
const res = await ctx.db2
|
1305
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
1306
|
+
.from(usersTable)
|
1307
|
+
.where(eq(usersTable.id, 1));
|
1308
|
+
|
1309
|
+
assert.deepEqual(res, [{ id: 1, name: 'John1' }]);
|
1310
|
+
});
|
1311
|
+
|
1312
|
+
test('insert with onConflict do nothing', async (ctx) => {
|
1313
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
1314
|
+
|
1315
|
+
await ctx.db2.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing();
|
1316
|
+
|
1317
|
+
const res = await ctx.db2
|
1318
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
1319
|
+
.from(usersTable)
|
1320
|
+
.where(eq(usersTable.id, 1));
|
1321
|
+
|
1322
|
+
assert.deepEqual(res, [{ id: 1, name: 'John' }]);
|
1323
|
+
});
|
1324
|
+
|
1325
|
+
test('insert with onConflict do nothing + target', async (ctx) => {
|
1326
|
+
await ctx.db2.insert(usersTable).values({ name: 'John' });
|
1327
|
+
|
1328
|
+
await ctx.db2.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing({ target: usersTable.id });
|
1329
|
+
|
1330
|
+
const res = await ctx.db2
|
1331
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
1332
|
+
.from(usersTable)
|
1333
|
+
.where(eq(usersTable.id, 1));
|
1334
|
+
|
1335
|
+
assert.deepEqual(res, [{ id: 1, name: 'John' }]);
|
1336
|
+
});
|
1337
|
+
|
1338
|
+
test('left join (flat object fields)', async (ctx) => {
|
1339
|
+
const { id: cityId } = await ctx.db2
|
1340
|
+
.insert(citiesTable)
|
1341
|
+
.values([{ name: 'Paris' }, { name: 'London' }])
|
1342
|
+
.returning({ id: citiesTable.id })
|
1343
|
+
.then((rows) => rows[0]!);
|
1344
|
+
|
1345
|
+
await ctx.db2.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]);
|
1346
|
+
|
1347
|
+
const res = await ctx.db2
|
1348
|
+
.select({
|
1349
|
+
userId: users2Table.id,
|
1350
|
+
userName: users2Table.name,
|
1351
|
+
cityId: citiesTable.id,
|
1352
|
+
cityName: citiesTable.name
|
1353
|
+
})
|
1354
|
+
.from(users2Table)
|
1355
|
+
.leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id));
|
1356
|
+
|
1357
|
+
assert.deepEqual(res, [
|
1358
|
+
{ userId: 1, userName: 'John', cityId, cityName: 'Paris' },
|
1359
|
+
{ userId: 2, userName: 'Jane', cityId: null, cityName: null }
|
1360
|
+
]);
|
1361
|
+
});
|
1362
|
+
|
1363
|
+
test('left join (grouped fields)', async (ctx) => {
|
1364
|
+
const { id: cityId } = await ctx.db2
|
1365
|
+
.insert(citiesTable)
|
1366
|
+
.values([{ name: 'Paris' }, { name: 'London' }])
|
1367
|
+
.returning({ id: citiesTable.id })
|
1368
|
+
.then((rows) => rows[0]!);
|
1369
|
+
|
1370
|
+
await ctx.db2.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]);
|
1371
|
+
|
1372
|
+
const res = await ctx.db2
|
1373
|
+
.select({
|
1374
|
+
id: users2Table.id,
|
1375
|
+
user: {
|
1376
|
+
name: users2Table.name,
|
1377
|
+
nameUpper: sql<string>`upper(${users2Table.name})`
|
1378
|
+
},
|
1379
|
+
city: {
|
1380
|
+
id: citiesTable.id,
|
1381
|
+
name: citiesTable.name,
|
1382
|
+
nameUpper: sql<string>`upper(${citiesTable.name})`
|
1383
|
+
}
|
1384
|
+
})
|
1385
|
+
.from(users2Table)
|
1386
|
+
.leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id));
|
1387
|
+
|
1388
|
+
assert.deepEqual(res, [
|
1389
|
+
{
|
1390
|
+
id: 1,
|
1391
|
+
user: { name: 'John', nameUpper: 'JOHN' },
|
1392
|
+
city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }
|
1393
|
+
},
|
1394
|
+
{
|
1395
|
+
id: 2,
|
1396
|
+
user: { name: 'Jane', nameUpper: 'JANE' },
|
1397
|
+
city: null
|
1398
|
+
}
|
1399
|
+
]);
|
1400
|
+
});
|
1401
|
+
|
1402
|
+
test('left join (all fields)', async (ctx) => {
|
1403
|
+
const { id: cityId } = await ctx.db2
|
1404
|
+
.insert(citiesTable)
|
1405
|
+
.values([{ name: 'Paris' }, { name: 'London' }])
|
1406
|
+
.returning({ id: citiesTable.id })
|
1407
|
+
.then((rows) => rows[0]!);
|
1408
|
+
|
1409
|
+
await ctx.db2.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]);
|
1410
|
+
|
1411
|
+
const res = await ctx.db2.select().from(users2Table).leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id));
|
1412
|
+
|
1413
|
+
assert.deepEqual(res, [
|
1414
|
+
{
|
1415
|
+
users2: {
|
1416
|
+
id: 1,
|
1417
|
+
name: 'John',
|
1418
|
+
cityId
|
1419
|
+
},
|
1420
|
+
cities: {
|
1421
|
+
id: cityId,
|
1422
|
+
name: 'Paris',
|
1423
|
+
state: null
|
1424
|
+
}
|
1425
|
+
},
|
1426
|
+
{
|
1427
|
+
users2: {
|
1428
|
+
id: 2,
|
1429
|
+
name: 'Jane',
|
1430
|
+
cityId: null
|
1431
|
+
},
|
1432
|
+
cities: null
|
1433
|
+
}
|
1434
|
+
]);
|
1435
|
+
});
|
1436
|
+
|
1437
|
+
test('join subquery', async (ctx) => {
|
1438
|
+
await ctx.db2
|
1439
|
+
.insert(courseCategoriesTable)
|
1440
|
+
.values([{ name: 'Category 1' }, { name: 'Category 2' }, { name: 'Category 3' }, { name: 'Category 4' }]);
|
1441
|
+
|
1442
|
+
await ctx.db2.insert(coursesTable).values([
|
1443
|
+
{ name: 'Development', categoryId: 2 },
|
1444
|
+
{ name: 'IT & Software', categoryId: 3 },
|
1445
|
+
{ name: 'Marketing', categoryId: 4 },
|
1446
|
+
{ name: 'Design', categoryId: 1 }
|
1447
|
+
]);
|
1448
|
+
|
1449
|
+
const sq2 = ctx.db2
|
1450
|
+
.select({
|
1451
|
+
categoryId: courseCategoriesTable.id,
|
1452
|
+
category: courseCategoriesTable.name,
|
1453
|
+
total: sql<number>`count(${courseCategoriesTable.id})`
|
1454
|
+
})
|
1455
|
+
.from(courseCategoriesTable)
|
1456
|
+
.groupBy(courseCategoriesTable.id, courseCategoriesTable.name)
|
1457
|
+
.as('sq2');
|
1458
|
+
|
1459
|
+
const res = await ctx.db2
|
1460
|
+
.select({
|
1461
|
+
courseName: coursesTable.name,
|
1462
|
+
categoryId: sq2.categoryId
|
1463
|
+
})
|
1464
|
+
.from(coursesTable)
|
1465
|
+
.leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId))
|
1466
|
+
.orderBy(coursesTable.name);
|
1467
|
+
|
1468
|
+
assert.deepEqual(res, [
|
1469
|
+
{ courseName: 'Design', categoryId: 1 },
|
1470
|
+
{ courseName: 'Development', categoryId: 2 },
|
1471
|
+
{ courseName: 'IT & Software', categoryId: 3 },
|
1472
|
+
{ courseName: 'Marketing', categoryId: 4 }
|
1473
|
+
]);
|
1474
|
+
});
|
1475
|
+
|
1476
|
+
test('with ... select', async (ctx) => {
|
1477
|
+
await ctx.db2.insert(orders).values([
|
1478
|
+
{ region: 'Europe', product: 'A', amount: 10, quantity: 1 },
|
1479
|
+
{ region: 'Europe', product: 'A', amount: 20, quantity: 2 },
|
1480
|
+
{ region: 'Europe', product: 'B', amount: 20, quantity: 2 },
|
1481
|
+
{ region: 'Europe', product: 'B', amount: 30, quantity: 3 },
|
1482
|
+
{ region: 'US', product: 'A', amount: 30, quantity: 3 },
|
1483
|
+
{ region: 'US', product: 'A', amount: 40, quantity: 4 },
|
1484
|
+
{ region: 'US', product: 'B', amount: 40, quantity: 4 },
|
1485
|
+
{ region: 'US', product: 'B', amount: 50, quantity: 5 }
|
1486
|
+
]);
|
1487
|
+
|
1488
|
+
const regionalSales = ctx.db2.$with('regional_sales').as(
|
1489
|
+
ctx.db2
|
1490
|
+
.select({
|
1491
|
+
region: orders.region,
|
1492
|
+
totalSales: sql<number>`sum(${orders.amount})`.as('total_sales')
|
1493
|
+
})
|
1494
|
+
.from(orders)
|
1495
|
+
.groupBy(orders.region)
|
1496
|
+
);
|
1497
|
+
|
1498
|
+
const topRegions = ctx.db2.$with('top_regions').as(
|
1499
|
+
ctx.db2
|
1500
|
+
.select({
|
1501
|
+
region: regionalSales.region
|
1502
|
+
})
|
1503
|
+
.from(regionalSales)
|
1504
|
+
.where(
|
1505
|
+
gt(
|
1506
|
+
regionalSales.totalSales,
|
1507
|
+
ctx.db2.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales)
|
1508
|
+
)
|
1509
|
+
)
|
1510
|
+
);
|
1511
|
+
|
1512
|
+
const result1 = await ctx.db2
|
1513
|
+
.with(regionalSales, topRegions)
|
1514
|
+
.select({
|
1515
|
+
region: orders.region,
|
1516
|
+
product: orders.product,
|
1517
|
+
productUnits: sql<number>`sum(${orders.quantity})::int`,
|
1518
|
+
productSales: sql<number>`sum(${orders.amount})::int`
|
1519
|
+
})
|
1520
|
+
.from(orders)
|
1521
|
+
.where(inArray(orders.region, ctx.db2.select({ region: topRegions.region }).from(topRegions)))
|
1522
|
+
.groupBy(orders.region, orders.product)
|
1523
|
+
.orderBy(orders.region, orders.product);
|
1524
|
+
const result2 = await ctx.db2
|
1525
|
+
.with(regionalSales, topRegions)
|
1526
|
+
.selectDistinct({
|
1527
|
+
region: orders.region,
|
1528
|
+
product: orders.product,
|
1529
|
+
productUnits: sql<number>`sum(${orders.quantity})::int`,
|
1530
|
+
productSales: sql<number>`sum(${orders.amount})::int`
|
1531
|
+
})
|
1532
|
+
.from(orders)
|
1533
|
+
.where(inArray(orders.region, ctx.db2.select({ region: topRegions.region }).from(topRegions)))
|
1534
|
+
.groupBy(orders.region, orders.product)
|
1535
|
+
.orderBy(orders.region, orders.product);
|
1536
|
+
const result3 = await ctx.db2
|
1537
|
+
.with(regionalSales, topRegions)
|
1538
|
+
.selectDistinctOn([orders.region], {
|
1539
|
+
region: orders.region,
|
1540
|
+
productUnits: sql<number>`sum(${orders.quantity})::int`,
|
1541
|
+
productSales: sql<number>`sum(${orders.amount})::int`
|
1542
|
+
})
|
1543
|
+
.from(orders)
|
1544
|
+
.where(inArray(orders.region, ctx.db2.select({ region: topRegions.region }).from(topRegions)))
|
1545
|
+
.groupBy(orders.region)
|
1546
|
+
.orderBy(orders.region);
|
1547
|
+
|
1548
|
+
assert.deepEqual(result1, [
|
1549
|
+
{
|
1550
|
+
region: 'Europe',
|
1551
|
+
product: 'A',
|
1552
|
+
productUnits: 3,
|
1553
|
+
productSales: 30
|
1554
|
+
},
|
1555
|
+
{
|
1556
|
+
region: 'Europe',
|
1557
|
+
product: 'B',
|
1558
|
+
productUnits: 5,
|
1559
|
+
productSales: 50
|
1560
|
+
},
|
1561
|
+
{
|
1562
|
+
region: 'US',
|
1563
|
+
product: 'A',
|
1564
|
+
productUnits: 7,
|
1565
|
+
productSales: 70
|
1566
|
+
},
|
1567
|
+
{
|
1568
|
+
region: 'US',
|
1569
|
+
product: 'B',
|
1570
|
+
productUnits: 9,
|
1571
|
+
productSales: 90
|
1572
|
+
}
|
1573
|
+
]);
|
1574
|
+
assert.deepEqual(result2, result1);
|
1575
|
+
assert.deepEqual(result3, [
|
1576
|
+
{
|
1577
|
+
region: 'Europe',
|
1578
|
+
productUnits: 8,
|
1579
|
+
productSales: 80
|
1580
|
+
},
|
1581
|
+
{
|
1582
|
+
region: 'US',
|
1583
|
+
productUnits: 16,
|
1584
|
+
productSales: 160
|
1585
|
+
}
|
1586
|
+
]);
|
1587
|
+
});
|
1588
|
+
|
1589
|
+
test('with ... update', async (ctx) => {
|
1590
|
+
const products = pgTable('products', {
|
1591
|
+
id: serial('id').primaryKey(),
|
1592
|
+
price: numeric('price').notNull(),
|
1593
|
+
cheap: boolean('cheap').notNull().default(false)
|
1594
|
+
});
|
1595
|
+
|
1596
|
+
await ctx.db2.execute(sql`drop table if exists ${products}`);
|
1597
|
+
await ctx.db2.execute(sql`
|
1598
|
+
create table ${products} (
|
1599
|
+
id serial primary key,
|
1600
|
+
price numeric not null,
|
1601
|
+
cheap boolean not null default false
|
1602
|
+
)
|
1603
|
+
`);
|
1604
|
+
|
1605
|
+
await ctx.db2
|
1606
|
+
.insert(products)
|
1607
|
+
.values([{ price: '10.99' }, { price: '25.85' }, { price: '32.99' }, { price: '2.50' }, { price: '4.59' }]);
|
1608
|
+
|
1609
|
+
const averagePrice = ctx.db2.$with('average_price').as(
|
1610
|
+
ctx.db2
|
1611
|
+
.select({
|
1612
|
+
value: sql`avg(${products.price})`.as('value')
|
1613
|
+
})
|
1614
|
+
.from(products)
|
1615
|
+
);
|
1616
|
+
|
1617
|
+
const result = await ctx.db2
|
1618
|
+
.with(averagePrice)
|
1619
|
+
.update(products)
|
1620
|
+
.set({
|
1621
|
+
cheap: true
|
1622
|
+
})
|
1623
|
+
.where(lt(products.price, sql`(select * from ${averagePrice})`))
|
1624
|
+
.returning({
|
1625
|
+
id: products.id
|
1626
|
+
});
|
1627
|
+
|
1628
|
+
assert.deepEqual(result, [{ id: 1 }, { id: 4 }, { id: 5 }]);
|
1629
|
+
});
|
1630
|
+
|
1631
|
+
test('with ... insert', async (ctx) => {
|
1632
|
+
const users = pgTable('users', {
|
1633
|
+
username: text('username').notNull(),
|
1634
|
+
admin: boolean('admin').notNull()
|
1635
|
+
});
|
1636
|
+
|
1637
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
1638
|
+
await ctx.db2.execute(sql`create table ${users} (username text not null, admin boolean not null default false)`);
|
1639
|
+
|
1640
|
+
const userCount = ctx.db2.$with('user_count').as(
|
1641
|
+
ctx.db2
|
1642
|
+
.select({
|
1643
|
+
value: sql`count(*)`.as('value')
|
1644
|
+
})
|
1645
|
+
.from(users)
|
1646
|
+
);
|
1647
|
+
|
1648
|
+
const result = await ctx.db2
|
1649
|
+
.with(userCount)
|
1650
|
+
.insert(users)
|
1651
|
+
.values([{ username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }])
|
1652
|
+
.returning({
|
1653
|
+
admin: users.admin
|
1654
|
+
});
|
1655
|
+
|
1656
|
+
assert.deepEqual(result, [{ admin: true }]);
|
1657
|
+
});
|
1658
|
+
|
1659
|
+
test('with ... delete', async (ctx) => {
|
1660
|
+
await ctx.db2.insert(orders).values([
|
1661
|
+
{ region: 'Europe', product: 'A', amount: 10, quantity: 1 },
|
1662
|
+
{ region: 'Europe', product: 'A', amount: 20, quantity: 2 },
|
1663
|
+
{ region: 'Europe', product: 'B', amount: 20, quantity: 2 },
|
1664
|
+
{ region: 'Europe', product: 'B', amount: 30, quantity: 3 },
|
1665
|
+
{ region: 'US', product: 'A', amount: 30, quantity: 3 },
|
1666
|
+
{ region: 'US', product: 'A', amount: 40, quantity: 4 },
|
1667
|
+
{ region: 'US', product: 'B', amount: 40, quantity: 4 },
|
1668
|
+
{ region: 'US', product: 'B', amount: 50, quantity: 5 }
|
1669
|
+
]);
|
1670
|
+
|
1671
|
+
const averageAmount = ctx.db2.$with('average_amount').as(
|
1672
|
+
ctx.db2
|
1673
|
+
.select({
|
1674
|
+
value: sql`avg(${orders.amount})`.as('value')
|
1675
|
+
})
|
1676
|
+
.from(orders)
|
1677
|
+
);
|
1678
|
+
|
1679
|
+
const result = await ctx.db2
|
1680
|
+
.with(averageAmount)
|
1681
|
+
.delete(orders)
|
1682
|
+
.where(gt(orders.amount, sql`(select * from ${averageAmount})`))
|
1683
|
+
.returning({
|
1684
|
+
id: orders.id
|
1685
|
+
});
|
1686
|
+
|
1687
|
+
assert.deepEqual(result, [{ id: 6 }, { id: 7 }, { id: 8 }]);
|
1688
|
+
});
|
1689
|
+
|
1690
|
+
test('select from subquery sql', async (ctx) => {
|
1691
|
+
await ctx.db2.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]);
|
1692
|
+
|
1693
|
+
const sq = ctx.db2
|
1694
|
+
.select({ name: sql<string>`${users2Table.name} || ' modified'`.as('name') })
|
1695
|
+
.from(users2Table)
|
1696
|
+
.as('sq');
|
1697
|
+
|
1698
|
+
const res = await ctx.db2.select({ name: sq.name }).from(sq);
|
1699
|
+
|
1700
|
+
assert.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]);
|
1701
|
+
});
|
1702
|
+
|
1703
|
+
test('select a field without joining its table', (ctx) => {
|
1704
|
+
ctx.expect(() => ctx.db2.select({ name: users2Table.name }).from(usersTable).prepare('query')).toThrowError();
|
1705
|
+
});
|
1706
|
+
|
1707
|
+
test('select all fields from subquery without alias', (ctx) => {
|
1708
|
+
const sq = ctx.db2
|
1709
|
+
.$with('sq')
|
1710
|
+
.as(ctx.db2.select({ name: sql<string>`upper(${users2Table.name})` }).from(users2Table));
|
1711
|
+
|
1712
|
+
ctx.expect(() => ctx.db2.select().from(sq).prepare('query')).toThrowError();
|
1713
|
+
});
|
1714
|
+
|
1715
|
+
test('select count()', async (ctx) => {
|
1716
|
+
// AssertionError: expected [ { count: 2 } ] to deeply equal [ { count: '2' } ]
|
1717
|
+
if (type === 'http') return ctx.skip();
|
1718
|
+
|
1719
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]);
|
1720
|
+
|
1721
|
+
const res = await ctx.db2.select({ count: sql`count(*)` }).from(usersTable);
|
1722
|
+
|
1723
|
+
assert.deepEqual(res, [{ count: '2' }]);
|
1724
|
+
});
|
1725
|
+
|
1726
|
+
test('select count w/ custom mapper', async (ctx) => {
|
1727
|
+
function count(value: PgColumn | SQLWrapper): SQL<number>;
|
1728
|
+
function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased<number>;
|
1729
|
+
function count(value: PgColumn | SQLWrapper, alias?: string): SQL<number> | SQL.Aliased<number> {
|
1730
|
+
const result = sql`count(${value})`.mapWith(Number);
|
1731
|
+
if (!alias) {
|
1732
|
+
return result;
|
1733
|
+
}
|
1734
|
+
return result.as(alias);
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
await ctx.db2.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]);
|
1738
|
+
|
1739
|
+
const res = await ctx.db2.select({ count: count(sql`*`) }).from(usersTable);
|
1740
|
+
|
1741
|
+
assert.deepEqual(res, [{ count: 2 }]);
|
1742
|
+
});
|
1743
|
+
|
1744
|
+
test('network types', async (ctx) => {
|
1745
|
+
const value: typeof network.$inferSelect = {
|
1746
|
+
inet: '127.0.0.1',
|
1747
|
+
cidr: '192.168.100.128/25',
|
1748
|
+
macaddr: '08:00:2b:01:02:03',
|
1749
|
+
macaddr8: '08:00:2b:01:02:03:04:05'
|
1750
|
+
};
|
1751
|
+
|
1752
|
+
await ctx.db2.insert(network).values(value);
|
1753
|
+
|
1754
|
+
const res = await ctx.db2.select().from(network);
|
1755
|
+
|
1756
|
+
assert.deepEqual(res, [value]);
|
1757
|
+
});
|
1758
|
+
|
1759
|
+
test('array types', async (ctx) => {
|
1760
|
+
// Array support
|
1761
|
+
if (type === 'http') return ctx.skip();
|
1762
|
+
|
1763
|
+
const values: (typeof salEmp.$inferSelect)[] = [
|
1764
|
+
{
|
1765
|
+
name: 'John',
|
1766
|
+
payByQuarter: [10000, 10000, 10000, 10000],
|
1767
|
+
schedule: [
|
1768
|
+
['meeting', 'lunch'],
|
1769
|
+
['training', 'presentation']
|
1770
|
+
]
|
1771
|
+
},
|
1772
|
+
{
|
1773
|
+
name: 'Carol',
|
1774
|
+
payByQuarter: [20000, 25000, 25000, 25000],
|
1775
|
+
schedule: [
|
1776
|
+
['breakfast', 'consulting'],
|
1777
|
+
['meeting', 'lunch']
|
1778
|
+
]
|
1779
|
+
}
|
1780
|
+
];
|
1781
|
+
|
1782
|
+
await ctx.db2.insert(salEmp).values(values);
|
1783
|
+
|
1784
|
+
const res = await ctx.db2.select().from(salEmp);
|
1785
|
+
|
1786
|
+
assert.deepEqual(res, values);
|
1787
|
+
});
|
1788
|
+
|
1789
|
+
test('select for ...', (ctx) => {
|
1790
|
+
{
|
1791
|
+
const query = ctx.db2.select().from(users2Table).for('update').toSQL();
|
1792
|
+
|
1793
|
+
ctx.expect(query.sql).toMatch(/ for update$/);
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
{
|
1797
|
+
const query = ctx.db2
|
1798
|
+
.select()
|
1799
|
+
.from(users2Table)
|
1800
|
+
.for('update', { of: [users2Table, coursesTable] })
|
1801
|
+
.toSQL();
|
1802
|
+
|
1803
|
+
ctx.expect(query.sql).toMatch(/ for update of "users2", "courses"$/);
|
1804
|
+
}
|
1805
|
+
|
1806
|
+
{
|
1807
|
+
const query = ctx.db2.select().from(users2Table).for('no key update', { of: users2Table }).toSQL();
|
1808
|
+
|
1809
|
+
ctx.expect(query.sql).toMatch(/for no key update of "users2"$/);
|
1810
|
+
}
|
1811
|
+
|
1812
|
+
{
|
1813
|
+
const query = ctx.db2
|
1814
|
+
.select()
|
1815
|
+
.from(users2Table)
|
1816
|
+
.for('no key update', { of: users2Table, skipLocked: true })
|
1817
|
+
.toSQL();
|
1818
|
+
|
1819
|
+
ctx.expect(query.sql).toMatch(/ for no key update of "users2" skip locked$/);
|
1820
|
+
}
|
1821
|
+
|
1822
|
+
{
|
1823
|
+
const query = ctx.db2.select().from(users2Table).for('share', { of: users2Table, noWait: true }).toSQL();
|
1824
|
+
|
1825
|
+
ctx.expect(query.sql).toMatch(/for share of "users2" no wait$/);
|
1826
|
+
}
|
1827
|
+
});
|
1828
|
+
|
1829
|
+
test('having', async (ctx) => {
|
1830
|
+
await ctx.db2.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]);
|
1831
|
+
|
1832
|
+
await ctx.db2.insert(users2Table).values([
|
1833
|
+
{ name: 'John', cityId: 1 },
|
1834
|
+
{ name: 'Jane', cityId: 1 },
|
1835
|
+
{
|
1836
|
+
name: 'Jack',
|
1837
|
+
cityId: 2
|
1838
|
+
}
|
1839
|
+
]);
|
1840
|
+
|
1841
|
+
const result = await ctx.db2
|
1842
|
+
.select({
|
1843
|
+
id: citiesTable.id,
|
1844
|
+
name: sql<string>`upper(${citiesTable.name})`.as('upper_name'),
|
1845
|
+
usersCount: sql<number>`count(${users2Table.id})::int`.as('users_count')
|
1846
|
+
})
|
1847
|
+
.from(citiesTable)
|
1848
|
+
.leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id))
|
1849
|
+
.where(({ name }) => sql`length(${name}) >= 3`)
|
1850
|
+
.groupBy(citiesTable.id)
|
1851
|
+
.having(({ usersCount }) => sql`${usersCount} > 0`)
|
1852
|
+
.orderBy(({ name }) => name);
|
1853
|
+
|
1854
|
+
assert.deepEqual(result, [
|
1855
|
+
{
|
1856
|
+
id: 1,
|
1857
|
+
name: 'LONDON',
|
1858
|
+
usersCount: 2
|
1859
|
+
},
|
1860
|
+
{
|
1861
|
+
id: 2,
|
1862
|
+
name: 'PARIS',
|
1863
|
+
usersCount: 1
|
1864
|
+
}
|
1865
|
+
]);
|
1866
|
+
});
|
1867
|
+
|
1868
|
+
notSupported('view', async (ctx) => {
|
1869
|
+
const newYorkers1 = pgView('new_yorkers').as((qb) =>
|
1870
|
+
qb.select().from(users2Table).where(eq(users2Table.cityId, 1))
|
1871
|
+
);
|
1872
|
+
|
1873
|
+
const newYorkers2 = pgView('new_yorkers', {
|
1874
|
+
id: serial('id').primaryKey(),
|
1875
|
+
name: text('name').notNull(),
|
1876
|
+
cityId: integer('city_id').notNull()
|
1877
|
+
}).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`);
|
1878
|
+
|
1879
|
+
const newYorkers3 = pgView('new_yorkers', {
|
1880
|
+
id: serial('id').primaryKey(),
|
1881
|
+
name: text('name').notNull(),
|
1882
|
+
cityId: integer('city_id').notNull()
|
1883
|
+
}).existing();
|
1884
|
+
|
1885
|
+
await ctx.db2.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`);
|
1886
|
+
|
1887
|
+
await ctx.db2.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]);
|
1888
|
+
|
1889
|
+
await ctx.db2.insert(users2Table).values([
|
1890
|
+
{ name: 'John', cityId: 1 },
|
1891
|
+
{ name: 'Jane', cityId: 1 },
|
1892
|
+
{ name: 'Jack', cityId: 2 }
|
1893
|
+
]);
|
1894
|
+
|
1895
|
+
{
|
1896
|
+
const result = await ctx.db2.select().from(newYorkers1);
|
1897
|
+
assert.deepEqual(result, [
|
1898
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1899
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1900
|
+
]);
|
1901
|
+
}
|
1902
|
+
|
1903
|
+
{
|
1904
|
+
const result = await ctx.db2.select().from(newYorkers2);
|
1905
|
+
assert.deepEqual(result, [
|
1906
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1907
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1908
|
+
]);
|
1909
|
+
}
|
1910
|
+
|
1911
|
+
{
|
1912
|
+
const result = await ctx.db2.select().from(newYorkers3);
|
1913
|
+
assert.deepEqual(result, [
|
1914
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1915
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1916
|
+
]);
|
1917
|
+
}
|
1918
|
+
|
1919
|
+
{
|
1920
|
+
const result = await ctx.db2.select({ name: newYorkers1.name }).from(newYorkers1);
|
1921
|
+
assert.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]);
|
1922
|
+
}
|
1923
|
+
|
1924
|
+
await ctx.db2.execute(sql`drop view ${newYorkers1}`);
|
1925
|
+
});
|
1926
|
+
|
1927
|
+
notSupported('materialized view', async (ctx) => {
|
1928
|
+
const newYorkers1 = pgMaterializedView('new_yorkers').as((qb) =>
|
1929
|
+
qb.select().from(users2Table).where(eq(users2Table.cityId, 1))
|
1930
|
+
);
|
1931
|
+
|
1932
|
+
const newYorkers2 = pgMaterializedView('new_yorkers', {
|
1933
|
+
id: serial('id').primaryKey(),
|
1934
|
+
name: text('name').notNull(),
|
1935
|
+
cityId: integer('city_id').notNull()
|
1936
|
+
}).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`);
|
1937
|
+
|
1938
|
+
const newYorkers3 = pgMaterializedView('new_yorkers', {
|
1939
|
+
id: serial('id').primaryKey(),
|
1940
|
+
name: text('name').notNull(),
|
1941
|
+
cityId: integer('city_id').notNull()
|
1942
|
+
}).existing();
|
1943
|
+
|
1944
|
+
await ctx.db2.execute(
|
1945
|
+
sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`
|
1946
|
+
);
|
1947
|
+
|
1948
|
+
await ctx.db2.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]);
|
1949
|
+
|
1950
|
+
await ctx.db2.insert(users2Table).values([
|
1951
|
+
{ name: 'John', cityId: 1 },
|
1952
|
+
{ name: 'Jane', cityId: 1 },
|
1953
|
+
{ name: 'Jack', cityId: 2 }
|
1954
|
+
]);
|
1955
|
+
|
1956
|
+
{
|
1957
|
+
const result = await ctx.db2.select().from(newYorkers1);
|
1958
|
+
assert.deepEqual(result, []);
|
1959
|
+
}
|
1960
|
+
|
1961
|
+
await ctx.db2.refreshMaterializedView(newYorkers1);
|
1962
|
+
|
1963
|
+
{
|
1964
|
+
const result = await ctx.db2.select().from(newYorkers1);
|
1965
|
+
assert.deepEqual(result, [
|
1966
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1967
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1968
|
+
]);
|
1969
|
+
}
|
1970
|
+
|
1971
|
+
{
|
1972
|
+
const result = await ctx.db2.select().from(newYorkers2);
|
1973
|
+
assert.deepEqual(result, [
|
1974
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1975
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1976
|
+
]);
|
1977
|
+
}
|
1978
|
+
|
1979
|
+
{
|
1980
|
+
const result = await ctx.db2.select().from(newYorkers3);
|
1981
|
+
assert.deepEqual(result, [
|
1982
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1983
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1984
|
+
]);
|
1985
|
+
}
|
1986
|
+
|
1987
|
+
{
|
1988
|
+
const result = await ctx.db2.select({ name: newYorkers1.name }).from(newYorkers1);
|
1989
|
+
assert.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]);
|
1990
|
+
}
|
1991
|
+
|
1992
|
+
await ctx.db2.execute(sql`drop materialized view ${newYorkers1}`);
|
1993
|
+
});
|
1994
|
+
|
1995
|
+
// TODO: copy to SQLite and MySQL, add to docs
|
1996
|
+
test('select from raw sql', async (ctx) => {
|
1997
|
+
const result = await ctx.db2
|
1998
|
+
.select({
|
1999
|
+
id: sql<number>`id`,
|
2000
|
+
name: sql<string>`name`
|
2001
|
+
})
|
2002
|
+
.from(sql`(select 1 as id, 'John' as name) as users`);
|
2003
|
+
|
2004
|
+
Expect<Equal<{ id: number; name: string }[], typeof result>>;
|
2005
|
+
|
2006
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
2007
|
+
});
|
2008
|
+
|
2009
|
+
test('select from raw sql with joins', async (ctx) => {
|
2010
|
+
const result = await ctx.db2
|
2011
|
+
.select({
|
2012
|
+
id: sql<number>`users.id`,
|
2013
|
+
name: sql<string>`users.name`,
|
2014
|
+
userCity: sql<string>`users.city`,
|
2015
|
+
cityName: sql<string>`cities.name`
|
2016
|
+
})
|
2017
|
+
.from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`)
|
2018
|
+
.leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`);
|
2019
|
+
|
2020
|
+
Expect<Equal<{ id: number; name: string; userCity: string; cityName: string }[], typeof result>>;
|
2021
|
+
|
2022
|
+
assert.deepEqual(result, [{ id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }]);
|
2023
|
+
});
|
2024
|
+
|
2025
|
+
test('join on aliased sql from select', async (ctx) => {
|
2026
|
+
const result = await ctx.db2
|
2027
|
+
.select({
|
2028
|
+
userId: sql<number>`users.id`.as('userId'),
|
2029
|
+
name: sql<string>`users.name`,
|
2030
|
+
userCity: sql<string>`users.city`,
|
2031
|
+
cityId: sql<number>`cities.id`.as('cityId'),
|
2032
|
+
cityName: sql<string>`cities.name`
|
2033
|
+
})
|
2034
|
+
.from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`)
|
2035
|
+
.leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId));
|
2036
|
+
|
2037
|
+
Expect<
|
2038
|
+
Equal<{ userId: number; name: string; userCity: string; cityId: number; cityName: string }[], typeof result>
|
2039
|
+
>;
|
2040
|
+
|
2041
|
+
assert.deepEqual(result, [{ userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }]);
|
2042
|
+
});
|
2043
|
+
|
2044
|
+
test('join on aliased sql from with clause', async (ctx) => {
|
2045
|
+
const users = ctx.db2.$with('users').as(
|
2046
|
+
ctx.db2
|
2047
|
+
.select({
|
2048
|
+
id: sql<number>`id`.as('userId'),
|
2049
|
+
name: sql<string>`name`.as('userName'),
|
2050
|
+
city: sql<string>`city`.as('city')
|
2051
|
+
})
|
2052
|
+
.from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`)
|
2053
|
+
);
|
2054
|
+
|
2055
|
+
const cities = ctx.db2.$with('cities').as(
|
2056
|
+
ctx.db2
|
2057
|
+
.select({
|
2058
|
+
id: sql<number>`id`.as('cityId'),
|
2059
|
+
name: sql<string>`name`.as('cityName')
|
2060
|
+
})
|
2061
|
+
.from(sql`(select 1 as id, 'Paris' as name) as cities`)
|
2062
|
+
);
|
2063
|
+
|
2064
|
+
const result = await ctx.db2
|
2065
|
+
.with(users, cities)
|
2066
|
+
.select({
|
2067
|
+
userId: users.id,
|
2068
|
+
name: users.name,
|
2069
|
+
userCity: users.city,
|
2070
|
+
cityId: cities.id,
|
2071
|
+
cityName: cities.name
|
2072
|
+
})
|
2073
|
+
.from(users)
|
2074
|
+
.leftJoin(cities, (cols) => eq(cols.cityId, cols.userId));
|
2075
|
+
|
2076
|
+
Expect<
|
2077
|
+
Equal<{ userId: number; name: string; userCity: string; cityId: number; cityName: string }[], typeof result>
|
2078
|
+
>;
|
2079
|
+
|
2080
|
+
assert.deepEqual(result, [{ userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }]);
|
2081
|
+
});
|
2082
|
+
|
2083
|
+
test('prefixed table', async (ctx) => {
|
2084
|
+
const pgTable = pgTableCreator((name) => `myprefix_${name}`);
|
2085
|
+
|
2086
|
+
const users = pgTable('test_prefixed_table_with_unique_name', {
|
2087
|
+
id: integer('id').primaryKey(),
|
2088
|
+
name: text('name').notNull()
|
2089
|
+
});
|
2090
|
+
|
2091
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
2092
|
+
|
2093
|
+
await ctx.db2.execute(
|
2094
|
+
sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`
|
2095
|
+
);
|
2096
|
+
|
2097
|
+
await ctx.db2.insert(users).values({ id: 1, name: 'John' });
|
2098
|
+
|
2099
|
+
const result = await ctx.db2.select().from(users);
|
2100
|
+
|
2101
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
2102
|
+
|
2103
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
2104
|
+
});
|
2105
|
+
|
2106
|
+
notSupported('select from enum', async (ctx) => {
|
2107
|
+
const muscleEnum = pgEnum('muscle', [
|
2108
|
+
'abdominals',
|
2109
|
+
'hamstrings',
|
2110
|
+
'adductors',
|
2111
|
+
'quadriceps',
|
2112
|
+
'biceps',
|
2113
|
+
'shoulders',
|
2114
|
+
'chest',
|
2115
|
+
'middle_back',
|
2116
|
+
'calves',
|
2117
|
+
'glutes',
|
2118
|
+
'lower_back',
|
2119
|
+
'lats',
|
2120
|
+
'triceps',
|
2121
|
+
'traps',
|
2122
|
+
'forearms',
|
2123
|
+
'neck',
|
2124
|
+
'abductors'
|
2125
|
+
]);
|
2126
|
+
|
2127
|
+
const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']);
|
2128
|
+
|
2129
|
+
const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']);
|
2130
|
+
|
2131
|
+
const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']);
|
2132
|
+
|
2133
|
+
const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']);
|
2134
|
+
|
2135
|
+
const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']);
|
2136
|
+
|
2137
|
+
const exercises = pgTable('exercises', {
|
2138
|
+
id: serial('id').primaryKey(),
|
2139
|
+
name: varchar('name').notNull(),
|
2140
|
+
force: forceEnum('force'),
|
2141
|
+
level: levelEnum('level'),
|
2142
|
+
mechanic: mechanicEnum('mechanic'),
|
2143
|
+
equipment: equipmentEnum('equipment'),
|
2144
|
+
instructions: text('instructions'),
|
2145
|
+
category: categoryEnum('category'),
|
2146
|
+
primaryMuscles: muscleEnum('primary_muscles').array(),
|
2147
|
+
secondaryMuscles: muscleEnum('secondary_muscles').array(),
|
2148
|
+
createdAt: timestamp('created_at')
|
2149
|
+
.notNull()
|
2150
|
+
.default(sql`now()`),
|
2151
|
+
updatedAt: timestamp('updated_at')
|
2152
|
+
.notNull()
|
2153
|
+
.default(sql`now()`)
|
2154
|
+
});
|
2155
|
+
|
2156
|
+
await ctx.db2.execute(sql`drop table if exists ${exercises}`);
|
2157
|
+
await ctx.db2.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`);
|
2158
|
+
await ctx.db2.execute(sql`drop type if exists ${name(forceEnum.enumName)}`);
|
2159
|
+
await ctx.db2.execute(sql`drop type if exists ${name(levelEnum.enumName)}`);
|
2160
|
+
await ctx.db2.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`);
|
2161
|
+
await ctx.db2.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`);
|
2162
|
+
await ctx.db2.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`);
|
2163
|
+
|
2164
|
+
await ctx.db2.execute(
|
2165
|
+
sql`create type ${name(
|
2166
|
+
muscleEnum.enumName
|
2167
|
+
)} as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`
|
2168
|
+
);
|
2169
|
+
await ctx.db2.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`);
|
2170
|
+
await ctx.db2.execute(
|
2171
|
+
sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`
|
2172
|
+
);
|
2173
|
+
await ctx.db2.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`);
|
2174
|
+
await ctx.db2.execute(
|
2175
|
+
sql`create type ${name(
|
2176
|
+
equipmentEnum.enumName
|
2177
|
+
)} as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`
|
2178
|
+
);
|
2179
|
+
await ctx.db2.execute(
|
2180
|
+
sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`
|
2181
|
+
);
|
2182
|
+
await ctx.db2.execute(sql`
|
2183
|
+
create table ${exercises} (
|
2184
|
+
id serial primary key,
|
2185
|
+
name varchar not null,
|
2186
|
+
force force,
|
2187
|
+
level level,
|
2188
|
+
mechanic mechanic,
|
2189
|
+
equipment equipment,
|
2190
|
+
instructions text,
|
2191
|
+
category category,
|
2192
|
+
primary_muscles muscle[],
|
2193
|
+
secondary_muscles muscle[],
|
2194
|
+
created_at timestamp not null default now(),
|
2195
|
+
updated_at timestamp not null default now()
|
2196
|
+
)
|
2197
|
+
`);
|
2198
|
+
|
2199
|
+
await ctx.db2.insert(exercises).values({
|
2200
|
+
name: 'Bench Press',
|
2201
|
+
force: 'isotonic',
|
2202
|
+
level: 'beginner',
|
2203
|
+
mechanic: 'compound',
|
2204
|
+
equipment: 'barbell',
|
2205
|
+
instructions:
|
2206
|
+
'Lie on your back on a flat bench. Grasp the barbell with an overhand grip, slightly wider than shoulder width. Unrack the barbell and hold it over you with your arms locked. Lower the barbell to your chest. Press the barbell back to the starting position.',
|
2207
|
+
category: 'upper_body',
|
2208
|
+
primaryMuscles: ['chest', 'triceps'],
|
2209
|
+
secondaryMuscles: ['shoulders', 'traps']
|
2210
|
+
});
|
2211
|
+
|
2212
|
+
const result = await ctx.db2.select().from(exercises);
|
2213
|
+
|
2214
|
+
assert.deepEqual(result, [
|
2215
|
+
{
|
2216
|
+
id: 1,
|
2217
|
+
name: 'Bench Press',
|
2218
|
+
force: 'isotonic',
|
2219
|
+
level: 'beginner',
|
2220
|
+
mechanic: 'compound',
|
2221
|
+
equipment: 'barbell',
|
2222
|
+
instructions:
|
2223
|
+
'Lie on your back on a flat bench. Grasp the barbell with an overhand grip, slightly wider than shoulder width. Unrack the barbell and hold it over you with your arms locked. Lower the barbell to your chest. Press the barbell back to the starting position.',
|
2224
|
+
category: 'upper_body',
|
2225
|
+
primaryMuscles: ['chest', 'triceps'],
|
2226
|
+
secondaryMuscles: ['shoulders', 'traps'],
|
2227
|
+
createdAt: result[0]!.createdAt,
|
2228
|
+
updatedAt: result[0]!.updatedAt
|
2229
|
+
}
|
2230
|
+
]);
|
2231
|
+
|
2232
|
+
await ctx.db2.execute(sql`drop table ${exercises}`);
|
2233
|
+
await ctx.db2.execute(sql`drop type ${name(muscleEnum.enumName)}`);
|
2234
|
+
await ctx.db2.execute(sql`drop type ${name(forceEnum.enumName)}`);
|
2235
|
+
await ctx.db2.execute(sql`drop type ${name(levelEnum.enumName)}`);
|
2236
|
+
await ctx.db2.execute(sql`drop type ${name(mechanicEnum.enumName)}`);
|
2237
|
+
await ctx.db2.execute(sql`drop type ${name(equipmentEnum.enumName)}`);
|
2238
|
+
await ctx.db2.execute(sql`drop type ${name(categoryEnum.enumName)}`);
|
2239
|
+
});
|
2240
|
+
|
2241
|
+
test('all date and time columns', async (ctx) => {
|
2242
|
+
// Dates support
|
2243
|
+
if (type === 'http') return ctx.skip();
|
2244
|
+
|
2245
|
+
const table = pgTable('all_columns', {
|
2246
|
+
id: serial('id').primaryKey(),
|
2247
|
+
dateString: date('date_string', { mode: 'string' }).notNull(),
|
2248
|
+
time: time('time', { precision: 3 }).notNull(),
|
2249
|
+
datetime: timestamp('datetime').notNull(),
|
2250
|
+
datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(),
|
2251
|
+
datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(),
|
2252
|
+
datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(),
|
2253
|
+
datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(),
|
2254
|
+
interval: interval('interval').notNull()
|
2255
|
+
});
|
2256
|
+
|
2257
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2258
|
+
|
2259
|
+
await ctx.db2.execute(sql`
|
2260
|
+
create table ${table} (
|
2261
|
+
id serial primary key,
|
2262
|
+
date_string date not null,
|
2263
|
+
time time(3) not null,
|
2264
|
+
datetime timestamp not null,
|
2265
|
+
datetime_wtz timestamp with time zone not null,
|
2266
|
+
datetime_string timestamp not null,
|
2267
|
+
datetime_full_precision timestamp(6) not null,
|
2268
|
+
datetime_wtz_string timestamp with time zone not null,
|
2269
|
+
interval interval not null
|
2270
|
+
)
|
2271
|
+
`);
|
2272
|
+
|
2273
|
+
const someDatetime = new Date('2022-01-01T00:00:00.123Z');
|
2274
|
+
const fullPrecision = '2022-01-01T00:00:00.123456Z';
|
2275
|
+
const someTime = '23:23:12.432';
|
2276
|
+
|
2277
|
+
await ctx.db2.insert(table).values({
|
2278
|
+
dateString: '2022-01-01',
|
2279
|
+
time: someTime,
|
2280
|
+
datetime: someDatetime,
|
2281
|
+
datetimeWTZ: someDatetime,
|
2282
|
+
datetimeString: '2022-01-01T00:00:00.123Z',
|
2283
|
+
datetimeFullPrecision: fullPrecision,
|
2284
|
+
datetimeWTZString: '2022-01-01T00:00:00.123Z',
|
2285
|
+
interval: '1 day'
|
2286
|
+
});
|
2287
|
+
|
2288
|
+
const result = await ctx.db2.select().from(table);
|
2289
|
+
|
2290
|
+
Expect<
|
2291
|
+
Equal<
|
2292
|
+
{
|
2293
|
+
id: number;
|
2294
|
+
dateString: string;
|
2295
|
+
time: string;
|
2296
|
+
datetime: Date;
|
2297
|
+
datetimeWTZ: Date;
|
2298
|
+
datetimeString: string;
|
2299
|
+
datetimeFullPrecision: string;
|
2300
|
+
datetimeWTZString: string;
|
2301
|
+
interval: string;
|
2302
|
+
}[],
|
2303
|
+
typeof result
|
2304
|
+
>
|
2305
|
+
>;
|
2306
|
+
|
2307
|
+
Expect<
|
2308
|
+
Equal<
|
2309
|
+
{
|
2310
|
+
dateString: string;
|
2311
|
+
time: string;
|
2312
|
+
datetime: Date;
|
2313
|
+
datetimeWTZ: Date;
|
2314
|
+
datetimeString: string;
|
2315
|
+
datetimeFullPrecision: string;
|
2316
|
+
datetimeWTZString: string;
|
2317
|
+
interval: string;
|
2318
|
+
id?: number | undefined;
|
2319
|
+
},
|
2320
|
+
typeof table.$inferInsert
|
2321
|
+
>
|
2322
|
+
>;
|
2323
|
+
|
2324
|
+
assert.deepEqual(result, [
|
2325
|
+
{
|
2326
|
+
id: 1,
|
2327
|
+
dateString: '2022-01-01',
|
2328
|
+
time: someTime,
|
2329
|
+
datetime: someDatetime,
|
2330
|
+
datetimeWTZ: someDatetime,
|
2331
|
+
datetimeString: '2022-01-01 00:00:00.123',
|
2332
|
+
datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''),
|
2333
|
+
datetimeWTZString: '2022-01-01 00:00:00.123+00',
|
2334
|
+
interval: '1 day'
|
2335
|
+
}
|
2336
|
+
]);
|
2337
|
+
|
2338
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2339
|
+
});
|
2340
|
+
|
2341
|
+
test('all date and time columns with timezone second case mode date', async (ctx) => {
|
2342
|
+
const table = pgTable('all_columns', {
|
2343
|
+
id: serial('id').primaryKey(),
|
2344
|
+
timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull()
|
2345
|
+
});
|
2346
|
+
|
2347
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2348
|
+
|
2349
|
+
await ctx.db2.execute(sql`
|
2350
|
+
create table ${table} (
|
2351
|
+
id serial primary key,
|
2352
|
+
timestamp_string timestamp(3) with time zone not null
|
2353
|
+
)
|
2354
|
+
`);
|
2355
|
+
|
2356
|
+
const insertedDate = new Date();
|
2357
|
+
|
2358
|
+
// 1. Insert date as new date
|
2359
|
+
await ctx.db2.insert(table).values([{ timestamp: insertedDate }]);
|
2360
|
+
|
2361
|
+
// 2, Select as date and check that timezones are the same
|
2362
|
+
// There is no way to check timezone in Date object, as it is always represented internally in UTC
|
2363
|
+
const result = await ctx.db2.select().from(table);
|
2364
|
+
|
2365
|
+
assert.deepEqual(result, [{ id: 1, timestamp: insertedDate }]);
|
2366
|
+
|
2367
|
+
// 3. Compare both dates
|
2368
|
+
assert.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime());
|
2369
|
+
|
2370
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2371
|
+
});
|
2372
|
+
|
2373
|
+
test('all date and time columns with timezone third case mode date', async (ctx) => {
|
2374
|
+
const table = pgTable('all_columns', {
|
2375
|
+
id: serial('id').primaryKey(),
|
2376
|
+
timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull()
|
2377
|
+
});
|
2378
|
+
|
2379
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2380
|
+
|
2381
|
+
await ctx.db2.execute(sql`
|
2382
|
+
create table ${table} (
|
2383
|
+
id serial primary key,
|
2384
|
+
timestamp_string timestamp(3) with time zone not null
|
2385
|
+
)
|
2386
|
+
`);
|
2387
|
+
|
2388
|
+
const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC
|
2389
|
+
const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones
|
2390
|
+
|
2391
|
+
// 1. Insert date as new dates with different time zones
|
2392
|
+
await ctx.db2.insert(table).values([{ timestamp: insertedDate }, { timestamp: insertedDate2 }]);
|
2393
|
+
|
2394
|
+
// 2, Select and compare both dates
|
2395
|
+
const result = await ctx.db2.select().from(table);
|
2396
|
+
|
2397
|
+
assert.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime());
|
2398
|
+
|
2399
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2400
|
+
});
|
2401
|
+
|
2402
|
+
test('all date and time columns without timezone first case mode string', async (ctx) => {
|
2403
|
+
// Dates support
|
2404
|
+
if (type === 'http') return ctx.skip();
|
2405
|
+
|
2406
|
+
const table = pgTable('all_columns', {
|
2407
|
+
id: serial('id').primaryKey(),
|
2408
|
+
timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull()
|
2409
|
+
});
|
2410
|
+
|
2411
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2412
|
+
|
2413
|
+
await ctx.db2.execute(sql`
|
2414
|
+
create table ${table} (
|
2415
|
+
id serial primary key,
|
2416
|
+
timestamp_string timestamp(6) not null
|
2417
|
+
)
|
2418
|
+
`);
|
2419
|
+
|
2420
|
+
// 1. Insert date in string format without timezone in it
|
2421
|
+
await ctx.db2.insert(table).values([{ timestamp: '2022-01-01 02:00:00.123456' }]);
|
2422
|
+
|
2423
|
+
// 2, Select in string format and check that values are the same
|
2424
|
+
const result = await ctx.db2.select().from(table);
|
2425
|
+
|
2426
|
+
assert.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]);
|
2427
|
+
|
2428
|
+
// 3. Select as raw query and check that values are the same
|
2429
|
+
const result2 = await ctx.db2.execute<{
|
2430
|
+
id: number;
|
2431
|
+
timestamp_string: string;
|
2432
|
+
}>(sql`select * from ${table}`);
|
2433
|
+
|
2434
|
+
assert.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]);
|
2435
|
+
|
2436
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2437
|
+
});
|
2438
|
+
|
2439
|
+
test('all date and time columns without timezone second case mode string', async (ctx) => {
|
2440
|
+
// Dates support
|
2441
|
+
if (type === 'http') return ctx.skip();
|
2442
|
+
|
2443
|
+
const table = pgTable('all_columns', {
|
2444
|
+
id: serial('id').primaryKey(),
|
2445
|
+
timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull()
|
2446
|
+
});
|
2447
|
+
|
2448
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2449
|
+
|
2450
|
+
await ctx.db2.execute(sql`
|
2451
|
+
create table ${table} (
|
2452
|
+
id serial primary key,
|
2453
|
+
timestamp_string timestamp(6) not null
|
2454
|
+
)
|
2455
|
+
`);
|
2456
|
+
|
2457
|
+
// 1. Insert date in string format with timezone in it
|
2458
|
+
await ctx.db2.insert(table).values([{ timestamp: '2022-01-01T02:00:00.123456-02' }]);
|
2459
|
+
|
2460
|
+
// 2, Select as raw query and check that values are the same
|
2461
|
+
const result = await ctx.db2.execute<{
|
2462
|
+
id: number;
|
2463
|
+
timestamp_string: string;
|
2464
|
+
}>(sql`select * from ${table}`);
|
2465
|
+
|
2466
|
+
assert.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]);
|
2467
|
+
|
2468
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2469
|
+
});
|
2470
|
+
|
2471
|
+
test('all date and time columns without timezone third case mode date', async (ctx) => {
|
2472
|
+
// Dates support
|
2473
|
+
if (type === 'http') return ctx.skip();
|
2474
|
+
|
2475
|
+
const table = pgTable('all_columns', {
|
2476
|
+
id: serial('id').primaryKey(),
|
2477
|
+
timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull()
|
2478
|
+
});
|
2479
|
+
|
2480
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2481
|
+
|
2482
|
+
await ctx.db2.execute(sql`
|
2483
|
+
create table ${table} (
|
2484
|
+
id serial primary key,
|
2485
|
+
timestamp_string timestamp(3) not null
|
2486
|
+
)
|
2487
|
+
`);
|
2488
|
+
|
2489
|
+
const insertedDate = new Date('2022-01-01 20:00:00.123+04');
|
2490
|
+
|
2491
|
+
// 1. Insert date as new date
|
2492
|
+
await ctx.db2.insert(table).values([{ timestamp: insertedDate }]);
|
2493
|
+
|
2494
|
+
// 2, Select as raw query as string
|
2495
|
+
const result = await ctx.db2.execute<{
|
2496
|
+
id: number;
|
2497
|
+
timestamp_string: string;
|
2498
|
+
}>(sql`select * from ${table}`);
|
2499
|
+
|
2500
|
+
// 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC
|
2501
|
+
assert.deepEqual(new Date(result.rows[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime());
|
2502
|
+
|
2503
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2504
|
+
});
|
2505
|
+
|
2506
|
+
test('test mode string for timestamp with timezone', async (ctx) => {
|
2507
|
+
// Dates support
|
2508
|
+
if (type === 'http') return ctx.skip();
|
2509
|
+
|
2510
|
+
const table = pgTable('all_columns', {
|
2511
|
+
id: serial('id').primaryKey(),
|
2512
|
+
timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull()
|
2513
|
+
});
|
2514
|
+
|
2515
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2516
|
+
|
2517
|
+
await ctx.db2.execute(sql`
|
2518
|
+
create table ${table} (
|
2519
|
+
id serial primary key,
|
2520
|
+
timestamp_string timestamp(6) with time zone not null
|
2521
|
+
)
|
2522
|
+
`);
|
2523
|
+
|
2524
|
+
const timestampString = '2022-01-01 00:00:00.123456-0200';
|
2525
|
+
|
2526
|
+
// 1. Insert date in string format with timezone in it
|
2527
|
+
await ctx.db2.insert(table).values([{ timestamp: timestampString }]);
|
2528
|
+
|
2529
|
+
// 2. Select date in string format and check that the values are the same
|
2530
|
+
const result = await ctx.db2.select().from(table);
|
2531
|
+
|
2532
|
+
// 2.1 Notice that postgres will return the date in UTC, but it is exactly the same
|
2533
|
+
assert.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]);
|
2534
|
+
|
2535
|
+
// 3. Select as raw query and checke that values are the same
|
2536
|
+
const result2 = await ctx.db2.execute<{
|
2537
|
+
id: number;
|
2538
|
+
timestamp_string: string;
|
2539
|
+
}>(sql`select * from ${table}`);
|
2540
|
+
|
2541
|
+
// 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same
|
2542
|
+
assert.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]);
|
2543
|
+
|
2544
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2545
|
+
});
|
2546
|
+
|
2547
|
+
test('test mode date for timestamp with timezone', async (ctx) => {
|
2548
|
+
// Dates support
|
2549
|
+
if (type === 'http') return ctx.skip();
|
2550
|
+
|
2551
|
+
const table = pgTable('all_columns', {
|
2552
|
+
id: serial('id').primaryKey(),
|
2553
|
+
timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull()
|
2554
|
+
});
|
2555
|
+
|
2556
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2557
|
+
|
2558
|
+
await ctx.db2.execute(sql`
|
2559
|
+
create table ${table} (
|
2560
|
+
id serial primary key,
|
2561
|
+
timestamp_string timestamp(3) with time zone not null
|
2562
|
+
)
|
2563
|
+
`);
|
2564
|
+
|
2565
|
+
const timestampString = new Date('2022-01-01 00:00:00.456-0200');
|
2566
|
+
|
2567
|
+
// 1. Insert date in string format with timezone in it
|
2568
|
+
await ctx.db2.insert(table).values([{ timestamp: timestampString }]);
|
2569
|
+
|
2570
|
+
// 2. Select date in string format and check that the values are the same
|
2571
|
+
const result = await ctx.db2.select().from(table);
|
2572
|
+
|
2573
|
+
// 2.1 Notice that postgres will return the date in UTC, but it is exactly the same
|
2574
|
+
assert.deepEqual(result, [{ id: 1, timestamp: timestampString }]);
|
2575
|
+
|
2576
|
+
// 3. Select as raw query and checke that values are the same
|
2577
|
+
const result2 = await ctx.db2.execute<{
|
2578
|
+
id: number;
|
2579
|
+
timestamp_string: string;
|
2580
|
+
}>(sql`select * from ${table}`);
|
2581
|
+
|
2582
|
+
// 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same
|
2583
|
+
assert.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]);
|
2584
|
+
|
2585
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2586
|
+
});
|
2587
|
+
|
2588
|
+
test('test mode string for timestamp with timezone in UTC timezone', async (ctx) => {
|
2589
|
+
// DDL SET TIME ZONE unsupported
|
2590
|
+
if (type === 'http') return ctx.skip();
|
2591
|
+
|
2592
|
+
// get current timezone from db
|
2593
|
+
const timezone = await ctx.db2.execute<{ TimeZone: string }>(sql`show timezone`);
|
2594
|
+
|
2595
|
+
// set timezone to UTC
|
2596
|
+
await ctx.db2.execute(sql`set time zone 'UTC'`);
|
2597
|
+
|
2598
|
+
const table = pgTable('all_columns', {
|
2599
|
+
id: serial('id').primaryKey(),
|
2600
|
+
timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull()
|
2601
|
+
});
|
2602
|
+
|
2603
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2604
|
+
|
2605
|
+
await ctx.db2.execute(sql`
|
2606
|
+
create table ${table} (
|
2607
|
+
id serial primary key,
|
2608
|
+
timestamp_string timestamp(6) with time zone not null
|
2609
|
+
)
|
2610
|
+
`);
|
2611
|
+
|
2612
|
+
const timestampString = '2022-01-01 00:00:00.123456-0200';
|
2613
|
+
|
2614
|
+
// 1. Insert date in string format with timezone in it
|
2615
|
+
await ctx.db2.insert(table).values([{ timestamp: timestampString }]);
|
2616
|
+
|
2617
|
+
// 2. Select date in string format and check that the values are the same
|
2618
|
+
const result = await ctx.db2.select().from(table);
|
2619
|
+
|
2620
|
+
// 2.1 Notice that postgres will return the date in UTC, but it is exactly the same
|
2621
|
+
assert.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]);
|
2622
|
+
|
2623
|
+
// 3. Select as raw query and checke that values are the same
|
2624
|
+
const result2 = await ctx.db2.execute<{
|
2625
|
+
id: number;
|
2626
|
+
timestamp_string: string;
|
2627
|
+
}>(sql`select * from ${table}`);
|
2628
|
+
|
2629
|
+
// 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same
|
2630
|
+
assert.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]);
|
2631
|
+
|
2632
|
+
await ctx.db2.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`);
|
2633
|
+
|
2634
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2635
|
+
});
|
2636
|
+
|
2637
|
+
test('test mode string for timestamp with timezone in different timezone', async (ctx) => {
|
2638
|
+
// DDL SET TIME ZONE unsupported
|
2639
|
+
if (type === 'http') return ctx.skip();
|
2640
|
+
|
2641
|
+
// get current timezone from db
|
2642
|
+
const timezone = await ctx.db2.execute<{ TimeZone: string }>(sql`show timezone`);
|
2643
|
+
|
2644
|
+
// set timezone to HST (UTC - 10)
|
2645
|
+
await ctx.db2.execute(sql`set time zone 'HST'`);
|
2646
|
+
|
2647
|
+
const table = pgTable('all_columns', {
|
2648
|
+
id: serial('id').primaryKey(),
|
2649
|
+
timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull()
|
2650
|
+
});
|
2651
|
+
|
2652
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2653
|
+
|
2654
|
+
await ctx.db2.execute(sql`
|
2655
|
+
create table ${table} (
|
2656
|
+
id serial primary key,
|
2657
|
+
timestamp_string timestamp(6) with time zone not null
|
2658
|
+
)
|
2659
|
+
`);
|
2660
|
+
|
2661
|
+
const timestampString = '2022-01-01 00:00:00.123456-1000';
|
2662
|
+
|
2663
|
+
// 1. Insert date in string format with timezone in it
|
2664
|
+
await ctx.db2.insert(table).values([{ timestamp: timestampString }]);
|
2665
|
+
|
2666
|
+
// 2. Select date in string format and check that the values are the same
|
2667
|
+
const result = await ctx.db2.select().from(table);
|
2668
|
+
|
2669
|
+
assert.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]);
|
2670
|
+
|
2671
|
+
// 3. Select as raw query and checke that values are the same
|
2672
|
+
const result2 = await ctx.db2.execute<{
|
2673
|
+
id: number;
|
2674
|
+
timestamp_string: string;
|
2675
|
+
}>(sql`select * from ${table}`);
|
2676
|
+
|
2677
|
+
assert.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456-10' }]);
|
2678
|
+
|
2679
|
+
await ctx.db2.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`);
|
2680
|
+
|
2681
|
+
await ctx.db2.execute(sql`drop table if exists ${table}`);
|
2682
|
+
});
|
2683
|
+
|
2684
|
+
test('orderBy with aliased column', (ctx) => {
|
2685
|
+
const query = ctx.db2
|
2686
|
+
.select({
|
2687
|
+
test: sql`something`.as('test')
|
2688
|
+
})
|
2689
|
+
.from(users2Table)
|
2690
|
+
.orderBy((fields) => fields.test)
|
2691
|
+
.toSQL();
|
2692
|
+
|
2693
|
+
assert.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"');
|
2694
|
+
});
|
2695
|
+
|
2696
|
+
test('select from sql', async (ctx) => {
|
2697
|
+
const metricEntry = pgTable('metric_entry', {
|
2698
|
+
id: pgUuid('id').notNull(),
|
2699
|
+
createdAt: timestamp('created_at').notNull()
|
2700
|
+
});
|
2701
|
+
|
2702
|
+
await ctx.db2.execute(sql`drop table if exists ${metricEntry}`);
|
2703
|
+
await ctx.db2.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`);
|
2704
|
+
|
2705
|
+
const metricId = uuid();
|
2706
|
+
|
2707
|
+
const intervals = ctx.db2.$with('intervals').as(
|
2708
|
+
ctx.db2
|
2709
|
+
.select({
|
2710
|
+
startTime: sql<string>`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'),
|
2711
|
+
endTime: sql<string>`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time')
|
2712
|
+
})
|
2713
|
+
.from(sql`generate_series(0, 29, 1) as t(x)`)
|
2714
|
+
);
|
2715
|
+
|
2716
|
+
ctx
|
2717
|
+
.expect(() =>
|
2718
|
+
ctx.db2
|
2719
|
+
.with(intervals)
|
2720
|
+
.select({
|
2721
|
+
startTime: intervals.startTime,
|
2722
|
+
endTime: intervals.endTime,
|
2723
|
+
count: sql<number>`count(${metricEntry})`
|
2724
|
+
})
|
2725
|
+
.from(metricEntry)
|
2726
|
+
.rightJoin(
|
2727
|
+
intervals,
|
2728
|
+
and(
|
2729
|
+
eq(metricEntry.id, metricId),
|
2730
|
+
gte(metricEntry.createdAt, intervals.startTime),
|
2731
|
+
lt(metricEntry.createdAt, intervals.endTime)
|
2732
|
+
)
|
2733
|
+
)
|
2734
|
+
.groupBy(intervals.startTime, intervals.endTime)
|
2735
|
+
.orderBy(asc(intervals.startTime))
|
2736
|
+
)
|
2737
|
+
.not.toThrowError();
|
2738
|
+
});
|
2739
|
+
|
2740
|
+
test('timestamp timezone', async (ctx) => {
|
2741
|
+
const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', {
|
2742
|
+
id: serial('id').primaryKey(),
|
2743
|
+
name: text('name').notNull(),
|
2744
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
2745
|
+
updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow()
|
2746
|
+
});
|
2747
|
+
|
2748
|
+
await ctx.db2.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`);
|
2749
|
+
|
2750
|
+
await ctx.db2.execute(
|
2751
|
+
sql`
|
2752
|
+
create table users_test_with_and_without_timezone (
|
2753
|
+
id serial not null primary key,
|
2754
|
+
name text not null,
|
2755
|
+
created_at timestamptz not null default now(),
|
2756
|
+
updated_at timestamp not null default now()
|
2757
|
+
)
|
2758
|
+
`
|
2759
|
+
);
|
2760
|
+
|
2761
|
+
const date = new Date(Date.parse('2020-01-01T00:00:00+04:00'));
|
2762
|
+
|
2763
|
+
await ctx.db2.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' });
|
2764
|
+
await ctx.db2.insert(usersTableWithAndWithoutTimezone).values({
|
2765
|
+
name: 'Without default times',
|
2766
|
+
createdAt: date,
|
2767
|
+
updatedAt: date
|
2768
|
+
});
|
2769
|
+
const users = await ctx.db2.select().from(usersTableWithAndWithoutTimezone);
|
2770
|
+
|
2771
|
+
// check that the timestamps are set correctly for default times
|
2772
|
+
ctx.expect(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000);
|
2773
|
+
ctx.expect(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000);
|
2774
|
+
|
2775
|
+
// check that the timestamps are set correctly for non default times
|
2776
|
+
ctx.expect(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000);
|
2777
|
+
ctx.expect(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000);
|
2778
|
+
});
|
2779
|
+
|
2780
|
+
test('transaction', async (ctx) => {
|
2781
|
+
// Transactions are not supported in HTTP
|
2782
|
+
if (type === 'http') return ctx.skip();
|
2783
|
+
|
2784
|
+
const users = pgTable('users_transactions', {
|
2785
|
+
id: serial('id').primaryKey(),
|
2786
|
+
balance: integer('balance').notNull()
|
2787
|
+
});
|
2788
|
+
const products = pgTable('products_transactions', {
|
2789
|
+
id: serial('id').primaryKey(),
|
2790
|
+
price: integer('price').notNull(),
|
2791
|
+
stock: integer('stock').notNull()
|
2792
|
+
});
|
2793
|
+
|
2794
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
2795
|
+
await ctx.db2.execute(sql`drop table if exists ${products}`);
|
2796
|
+
|
2797
|
+
await ctx.db2.execute(
|
2798
|
+
sql`create table users_transactions (id serial not null primary key, balance integer not null)`
|
2799
|
+
);
|
2800
|
+
await ctx.db2.execute(
|
2801
|
+
sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`
|
2802
|
+
);
|
2803
|
+
|
2804
|
+
const user = await ctx.db2
|
2805
|
+
.insert(users)
|
2806
|
+
.values({ balance: 100 })
|
2807
|
+
.returning()
|
2808
|
+
.then((rows) => rows[0]!);
|
2809
|
+
const product = await ctx.db2
|
2810
|
+
.insert(products)
|
2811
|
+
.values({ price: 10, stock: 10 })
|
2812
|
+
.returning()
|
2813
|
+
.then((rows) => rows[0]!);
|
2814
|
+
|
2815
|
+
await ctx.db2.transaction(async (tx) => {
|
2816
|
+
await tx
|
2817
|
+
.update(users)
|
2818
|
+
.set({ balance: user.balance - product.price })
|
2819
|
+
.where(eq(users.id, user.id));
|
2820
|
+
await tx
|
2821
|
+
.update(products)
|
2822
|
+
.set({ stock: product.stock - 1 })
|
2823
|
+
.where(eq(products.id, product.id));
|
2824
|
+
});
|
2825
|
+
|
2826
|
+
const result = await ctx.db2.select().from(users);
|
2827
|
+
|
2828
|
+
assert.deepEqual(result, [{ id: 1, balance: 90 }]);
|
2829
|
+
|
2830
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
2831
|
+
await ctx.db2.execute(sql`drop table ${products}`);
|
2832
|
+
});
|
2833
|
+
|
2834
|
+
test('transaction rollback', async (ctx) => {
|
2835
|
+
// Transactions are not supported in HTTP
|
2836
|
+
if (type === 'http') return ctx.skip();
|
2837
|
+
|
2838
|
+
const users = pgTable('users_transactions_rollback', {
|
2839
|
+
id: serial('id').primaryKey(),
|
2840
|
+
balance: integer('balance').notNull()
|
2841
|
+
});
|
2842
|
+
|
2843
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
2844
|
+
|
2845
|
+
await ctx.db2.execute(
|
2846
|
+
sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`
|
2847
|
+
);
|
2848
|
+
|
2849
|
+
await ctx
|
2850
|
+
.expect(
|
2851
|
+
ctx.db2.transaction(async (tx) => {
|
2852
|
+
await tx.insert(users).values({ balance: 100 });
|
2853
|
+
tx.rollback();
|
2854
|
+
})
|
2855
|
+
)
|
2856
|
+
.rejects.toThrow(TransactionRollbackError);
|
2857
|
+
|
2858
|
+
const result = await ctx.db2.select().from(users);
|
2859
|
+
|
2860
|
+
assert.deepEqual(result, []);
|
2861
|
+
|
2862
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
2863
|
+
});
|
2864
|
+
|
2865
|
+
test('nested transaction', async (ctx) => {
|
2866
|
+
// Transactions are not supported in HTTP
|
2867
|
+
if (type === 'http') return ctx.skip();
|
2868
|
+
|
2869
|
+
const users = pgTable('users_nested_transactions', {
|
2870
|
+
id: serial('id').primaryKey(),
|
2871
|
+
balance: integer('balance').notNull()
|
2872
|
+
});
|
2873
|
+
|
2874
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
2875
|
+
|
2876
|
+
await ctx.db2.execute(
|
2877
|
+
sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`
|
2878
|
+
);
|
2879
|
+
|
2880
|
+
await ctx.db2.transaction(async (tx) => {
|
2881
|
+
await tx.insert(users).values({ balance: 100 });
|
2882
|
+
|
2883
|
+
await tx.transaction(async (tx) => {
|
2884
|
+
await tx.update(users).set({ balance: 200 });
|
2885
|
+
});
|
2886
|
+
});
|
2887
|
+
|
2888
|
+
const result = await ctx.db2.select().from(users);
|
2889
|
+
|
2890
|
+
assert.deepEqual(result, [{ id: 1, balance: 200 }]);
|
2891
|
+
|
2892
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
2893
|
+
});
|
2894
|
+
|
2895
|
+
test('nested transaction rollback', async (ctx) => {
|
2896
|
+
// Transactions are not supported in HTTP
|
2897
|
+
if (type === 'http') return ctx.skip();
|
2898
|
+
|
2899
|
+
const users = pgTable('users_nested_transactions_rollback', {
|
2900
|
+
id: serial('id').primaryKey(),
|
2901
|
+
balance: integer('balance').notNull()
|
2902
|
+
});
|
2903
|
+
|
2904
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
2905
|
+
|
2906
|
+
await ctx.db2.execute(
|
2907
|
+
sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`
|
2908
|
+
);
|
2909
|
+
|
2910
|
+
await ctx.db2.transaction(async (tx) => {
|
2911
|
+
await tx.insert(users).values({ balance: 100 });
|
2912
|
+
|
2913
|
+
await ctx
|
2914
|
+
.expect(
|
2915
|
+
tx.transaction(async (tx) => {
|
2916
|
+
await tx.update(users).set({ balance: 200 });
|
2917
|
+
tx.rollback();
|
2918
|
+
})
|
2919
|
+
)
|
2920
|
+
.rejects.toThrow(TransactionRollbackError);
|
2921
|
+
});
|
2922
|
+
|
2923
|
+
const result = await ctx.db2.select().from(users);
|
2924
|
+
|
2925
|
+
assert.deepEqual(result, [{ id: 1, balance: 100 }]);
|
2926
|
+
|
2927
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
2928
|
+
});
|
2929
|
+
|
2930
|
+
test('join subquery with join', async (ctx) => {
|
2931
|
+
const internalStaff = pgTable('internal_staff', {
|
2932
|
+
userId: integer('user_id').notNull()
|
2933
|
+
});
|
2934
|
+
|
2935
|
+
const customUser = pgTable('custom_user', {
|
2936
|
+
id: integer('id').notNull()
|
2937
|
+
});
|
2938
|
+
|
2939
|
+
const ticket = pgTable('ticket', {
|
2940
|
+
staffId: integer('staff_id').notNull()
|
2941
|
+
});
|
2942
|
+
|
2943
|
+
await ctx.db2.execute(sql`drop table if exists ${internalStaff}`);
|
2944
|
+
await ctx.db2.execute(sql`drop table if exists ${customUser}`);
|
2945
|
+
await ctx.db2.execute(sql`drop table if exists ${ticket}`);
|
2946
|
+
|
2947
|
+
await ctx.db2.execute(sql`create table internal_staff (user_id integer not null)`);
|
2948
|
+
await ctx.db2.execute(sql`create table custom_user (id integer not null)`);
|
2949
|
+
await ctx.db2.execute(sql`create table ticket (staff_id integer not null)`);
|
2950
|
+
|
2951
|
+
await ctx.db2.insert(internalStaff).values({ userId: 1 });
|
2952
|
+
await ctx.db2.insert(customUser).values({ id: 1 });
|
2953
|
+
await ctx.db2.insert(ticket).values({ staffId: 1 });
|
2954
|
+
|
2955
|
+
const subq = ctx.db2
|
2956
|
+
.select()
|
2957
|
+
.from(internalStaff)
|
2958
|
+
.leftJoin(customUser, eq(internalStaff.userId, customUser.id))
|
2959
|
+
.as('internal_staff');
|
2960
|
+
|
2961
|
+
const mainQuery = await ctx.db2
|
2962
|
+
.select()
|
2963
|
+
.from(ticket)
|
2964
|
+
.leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId));
|
2965
|
+
|
2966
|
+
assert.deepEqual(mainQuery, [
|
2967
|
+
{
|
2968
|
+
ticket: { staffId: 1 },
|
2969
|
+
internal_staff: {
|
2970
|
+
internal_staff: { userId: 1 },
|
2971
|
+
custom_user: { id: 1 }
|
2972
|
+
}
|
2973
|
+
}
|
2974
|
+
]);
|
2975
|
+
|
2976
|
+
await ctx.db2.execute(sql`drop table ${internalStaff}`);
|
2977
|
+
await ctx.db2.execute(sql`drop table ${customUser}`);
|
2978
|
+
await ctx.db2.execute(sql`drop table ${ticket}`);
|
2979
|
+
});
|
2980
|
+
|
2981
|
+
notSupported('subquery with view', async (ctx) => {
|
2982
|
+
const users = pgTable('users_subquery_view', {
|
2983
|
+
id: serial('id').primaryKey(),
|
2984
|
+
name: text('name').notNull(),
|
2985
|
+
cityId: integer('city_id').notNull()
|
2986
|
+
});
|
2987
|
+
|
2988
|
+
const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1)));
|
2989
|
+
|
2990
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
2991
|
+
await ctx.db2.execute(sql`drop view if exists ${newYorkers}`);
|
2992
|
+
|
2993
|
+
await ctx.db2.execute(
|
2994
|
+
sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`
|
2995
|
+
);
|
2996
|
+
await ctx.db2.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`);
|
2997
|
+
|
2998
|
+
await ctx.db2.insert(users).values([
|
2999
|
+
{ name: 'John', cityId: 1 },
|
3000
|
+
{ name: 'Jane', cityId: 2 },
|
3001
|
+
{ name: 'Jack', cityId: 1 },
|
3002
|
+
{ name: 'Jill', cityId: 2 }
|
3003
|
+
]);
|
3004
|
+
|
3005
|
+
const sq = ctx.db2.$with('sq').as(ctx.db2.select().from(newYorkers));
|
3006
|
+
const result = await ctx.db2.with(sq).select().from(sq);
|
3007
|
+
|
3008
|
+
assert.deepEqual(result, [
|
3009
|
+
{ id: 1, name: 'John', cityId: 1 },
|
3010
|
+
{ id: 3, name: 'Jack', cityId: 1 }
|
3011
|
+
]);
|
3012
|
+
|
3013
|
+
await ctx.db2.execute(sql`drop view ${newYorkers}`);
|
3014
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
3015
|
+
});
|
3016
|
+
|
3017
|
+
notSupported('join view as subquery', async (ctx) => {
|
3018
|
+
const users = pgTable('users_join_view', {
|
3019
|
+
id: serial('id').primaryKey(),
|
3020
|
+
name: text('name').notNull(),
|
3021
|
+
cityId: integer('city_id').notNull()
|
3022
|
+
});
|
3023
|
+
|
3024
|
+
const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1)));
|
3025
|
+
|
3026
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
3027
|
+
await ctx.db2.execute(sql`drop view if exists ${newYorkers}`);
|
3028
|
+
|
3029
|
+
await ctx.db2.execute(
|
3030
|
+
sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`
|
3031
|
+
);
|
3032
|
+
await ctx.db2.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`);
|
3033
|
+
|
3034
|
+
await ctx.db2.insert(users).values([
|
3035
|
+
{ name: 'John', cityId: 1 },
|
3036
|
+
{ name: 'Jane', cityId: 2 },
|
3037
|
+
{ name: 'Jack', cityId: 1 },
|
3038
|
+
{ name: 'Jill', cityId: 2 }
|
3039
|
+
]);
|
3040
|
+
|
3041
|
+
const sq = ctx.db2.select().from(newYorkers).as('new_yorkers_sq');
|
3042
|
+
|
3043
|
+
const result = await ctx.db2.select().from(users).leftJoin(sq, eq(users.id, sq.id));
|
3044
|
+
|
3045
|
+
assert.deepEqual(result, [
|
3046
|
+
{
|
3047
|
+
users_join_view: { id: 1, name: 'John', cityId: 1 },
|
3048
|
+
new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }
|
3049
|
+
},
|
3050
|
+
{
|
3051
|
+
users_join_view: { id: 2, name: 'Jane', cityId: 2 },
|
3052
|
+
new_yorkers_sq: null
|
3053
|
+
},
|
3054
|
+
{
|
3055
|
+
users_join_view: { id: 3, name: 'Jack', cityId: 1 },
|
3056
|
+
new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }
|
3057
|
+
},
|
3058
|
+
{
|
3059
|
+
users_join_view: { id: 4, name: 'Jill', cityId: 2 },
|
3060
|
+
new_yorkers_sq: null
|
3061
|
+
}
|
3062
|
+
]);
|
3063
|
+
|
3064
|
+
await ctx.db2.execute(sql`drop view ${newYorkers}`);
|
3065
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
3066
|
+
});
|
3067
|
+
|
3068
|
+
test('table selection with single table', async (ctx) => {
|
3069
|
+
const users = pgTable('users', {
|
3070
|
+
id: serial('id').primaryKey(),
|
3071
|
+
name: text('name').notNull(),
|
3072
|
+
cityId: integer('city_id').notNull()
|
3073
|
+
});
|
3074
|
+
|
3075
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
3076
|
+
|
3077
|
+
await ctx.db2.execute(
|
3078
|
+
sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`
|
3079
|
+
);
|
3080
|
+
|
3081
|
+
await ctx.db2.insert(users).values({ name: 'John', cityId: 1 });
|
3082
|
+
|
3083
|
+
const result = await ctx.db2.select({ users }).from(users);
|
3084
|
+
|
3085
|
+
assert.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]);
|
3086
|
+
|
3087
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
3088
|
+
});
|
3089
|
+
|
3090
|
+
test('set null to jsonb field', async (ctx) => {
|
3091
|
+
const users = pgTable('users', {
|
3092
|
+
id: serial('id').primaryKey(),
|
3093
|
+
jsonb: jsonb('jsonb')
|
3094
|
+
});
|
3095
|
+
|
3096
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
3097
|
+
|
3098
|
+
await ctx.db2.execute(sql`create table ${users} (id serial not null primary key, jsonb jsonb)`);
|
3099
|
+
|
3100
|
+
const result = await ctx.db2.insert(users).values({ jsonb: null }).returning();
|
3101
|
+
|
3102
|
+
assert.deepEqual(result, [{ id: 1, jsonb: null }]);
|
3103
|
+
|
3104
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
3105
|
+
});
|
3106
|
+
|
3107
|
+
test('insert undefined', async (ctx) => {
|
3108
|
+
const users = pgTable('users', {
|
3109
|
+
id: serial('id').primaryKey(),
|
3110
|
+
name: text('name')
|
3111
|
+
});
|
3112
|
+
|
3113
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
3114
|
+
|
3115
|
+
await ctx.db2.execute(sql`create table ${users} (id serial not null primary key, name text)`);
|
3116
|
+
|
3117
|
+
ctx.expect(async () => await ctx.db2.insert(users).values({ name: undefined })).not.toThrowError();
|
3118
|
+
|
3119
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
3120
|
+
});
|
3121
|
+
|
3122
|
+
fixme('update undefined', async (ctx) => {
|
3123
|
+
const users = pgTable('users', {
|
3124
|
+
id: serial('id').primaryKey(),
|
3125
|
+
name: text('name')
|
3126
|
+
});
|
3127
|
+
|
3128
|
+
await ctx.db2.execute(sql`drop table if exists ${users}`);
|
3129
|
+
|
3130
|
+
await ctx.db2.execute(sql`create table ${users} (id serial not null primary key, name text)`);
|
3131
|
+
|
3132
|
+
ctx.expect(async () => await ctx.db2.update(users).set({ name: undefined })).toThrowError();
|
3133
|
+
ctx.expect(async () => await ctx.db2.update(users).set({ id: 1, name: undefined })).not.toThrowError();
|
3134
|
+
|
3135
|
+
await ctx.db2.execute(sql`drop table ${users}`);
|
3136
|
+
});
|
3137
|
+
|
3138
|
+
test('array operators', async (ctx) => {
|
3139
|
+
const posts = pgTable('posts', {
|
3140
|
+
id: serial('id').primaryKey(),
|
3141
|
+
tags: text('tags').array()
|
3142
|
+
});
|
3143
|
+
|
3144
|
+
await ctx.db2.execute(sql`drop table if exists ${posts}`);
|
3145
|
+
|
3146
|
+
await ctx.db2.execute(sql`create table ${posts} (id serial primary key, tags text[])`);
|
3147
|
+
|
3148
|
+
await ctx.db2.insert(posts).values([
|
3149
|
+
{
|
3150
|
+
tags: ['ORM']
|
3151
|
+
},
|
3152
|
+
{
|
3153
|
+
tags: ['Typescript']
|
3154
|
+
},
|
3155
|
+
{
|
3156
|
+
tags: ['Typescript', 'ORM']
|
3157
|
+
},
|
3158
|
+
{
|
3159
|
+
tags: ['Typescript', 'Frontend', 'React']
|
3160
|
+
},
|
3161
|
+
{
|
3162
|
+
tags: ['Typescript', 'ORM', 'Database', 'Postgres']
|
3163
|
+
},
|
3164
|
+
{
|
3165
|
+
tags: ['Java', 'Spring', 'OOP']
|
3166
|
+
}
|
3167
|
+
]);
|
3168
|
+
|
3169
|
+
const contains = await ctx.db2
|
3170
|
+
.select({ id: posts.id })
|
3171
|
+
.from(posts)
|
3172
|
+
.where(arrayContains(posts.tags, ['Typescript', 'ORM']));
|
3173
|
+
const contained = await ctx.db2
|
3174
|
+
.select({ id: posts.id })
|
3175
|
+
.from(posts)
|
3176
|
+
.where(arrayContained(posts.tags, ['Typescript', 'ORM']));
|
3177
|
+
const overlaps = await ctx.db2
|
3178
|
+
.select({ id: posts.id })
|
3179
|
+
.from(posts)
|
3180
|
+
.where(arrayOverlaps(posts.tags, ['Typescript', 'ORM']));
|
3181
|
+
const withSubQuery = await ctx.db2
|
3182
|
+
.select({ id: posts.id })
|
3183
|
+
.from(posts)
|
3184
|
+
.where(arrayContains(posts.tags, ctx.db2.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1))));
|
3185
|
+
|
3186
|
+
assert.deepEqual(contains, [{ id: 3 }, { id: 5 }]);
|
3187
|
+
assert.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]);
|
3188
|
+
assert.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]);
|
3189
|
+
assert.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]);
|
3190
|
+
});
|
3191
|
+
|
3192
|
+
test('set operations (union) from query builder with subquery', async (ctx) => {
|
3193
|
+
// Error: invalid SQL: unused parameters: used 0 of 2 parameters
|
3194
|
+
if (type === 'http') return ctx.skip();
|
3195
|
+
|
3196
|
+
await setupSetOperationSuite(ctx.db2);
|
3197
|
+
|
3198
|
+
const sq = ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).as('sq');
|
3199
|
+
|
3200
|
+
const result = await ctx.db2
|
3201
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3202
|
+
.from(cities2Table)
|
3203
|
+
.union(ctx.db2.select().from(sq))
|
3204
|
+
.orderBy(asc(sql`name`))
|
3205
|
+
.limit(2)
|
3206
|
+
.offset(1);
|
3207
|
+
|
3208
|
+
ctx.expect(result.length === 2);
|
3209
|
+
|
3210
|
+
assert.deepEqual(result, [
|
3211
|
+
{ id: 3, name: 'Jack' },
|
3212
|
+
{ id: 2, name: 'Jane' }
|
3213
|
+
]);
|
3214
|
+
|
3215
|
+
ctx
|
3216
|
+
.expect(() => {
|
3217
|
+
ctx.db2
|
3218
|
+
.select({ id: cities2Table.id, name: citiesTable.name, name2: users2Table.name })
|
3219
|
+
.from(cities2Table)
|
3220
|
+
.union(
|
3221
|
+
// @ts-expect-error
|
3222
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table)
|
3223
|
+
)
|
3224
|
+
.orderBy(asc(sql`name`));
|
3225
|
+
})
|
3226
|
+
.toThrowError();
|
3227
|
+
});
|
3228
|
+
|
3229
|
+
test('set operations (union) as function', async (ctx) => {
|
3230
|
+
// Error: invalid SQL: unused parameters: used 3 of 5 parameters
|
3231
|
+
if (type === 'http') return ctx.skip();
|
3232
|
+
|
3233
|
+
await setupSetOperationSuite(ctx.db2);
|
3234
|
+
|
3235
|
+
const result = await union(
|
3236
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).where(eq(citiesTable.id, 1)),
|
3237
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3238
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3239
|
+
)
|
3240
|
+
.orderBy(asc(sql`name`))
|
3241
|
+
.limit(1)
|
3242
|
+
.offset(1);
|
3243
|
+
|
3244
|
+
ctx.expect(result.length === 1);
|
3245
|
+
|
3246
|
+
assert.deepEqual(result, [{ id: 1, name: 'New York' }]);
|
3247
|
+
|
3248
|
+
ctx
|
3249
|
+
.expect(() => {
|
3250
|
+
union(
|
3251
|
+
ctx.db2
|
3252
|
+
.select({ name: citiesTable.name, id: cities2Table.id })
|
3253
|
+
.from(cities2Table)
|
3254
|
+
.where(eq(citiesTable.id, 1)),
|
3255
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3256
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3257
|
+
).orderBy(asc(sql`name`));
|
3258
|
+
})
|
3259
|
+
.toThrowError();
|
3260
|
+
});
|
3261
|
+
|
3262
|
+
test('set operations (union all) from query builder', async (ctx) => {
|
3263
|
+
await setupSetOperationSuite(ctx.db2);
|
3264
|
+
|
3265
|
+
const result = await ctx.db2
|
3266
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3267
|
+
.from(cities2Table)
|
3268
|
+
.limit(2)
|
3269
|
+
.unionAll(ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).limit(2))
|
3270
|
+
.orderBy(asc(sql`id`));
|
3271
|
+
|
3272
|
+
ctx.expect(result.length === 4);
|
3273
|
+
|
3274
|
+
assert.deepEqual(result, [
|
3275
|
+
{ id: 1, name: 'New York' },
|
3276
|
+
{ id: 1, name: 'New York' },
|
3277
|
+
{ id: 2, name: 'London' },
|
3278
|
+
{ id: 2, name: 'London' }
|
3279
|
+
]);
|
3280
|
+
|
3281
|
+
ctx
|
3282
|
+
.expect(() => {
|
3283
|
+
ctx.db2
|
3284
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3285
|
+
.from(cities2Table)
|
3286
|
+
.limit(2)
|
3287
|
+
.unionAll(ctx.db2.select({ name: citiesTable.name, id: cities2Table.id }).from(cities2Table).limit(2))
|
3288
|
+
.orderBy(asc(sql`id`));
|
3289
|
+
})
|
3290
|
+
.toThrowError();
|
3291
|
+
});
|
3292
|
+
|
3293
|
+
test('set operations (union all) as function', async (ctx) => {
|
3294
|
+
await setupSetOperationSuite(ctx.db2);
|
3295
|
+
|
3296
|
+
const result = await unionAll(
|
3297
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).where(eq(citiesTable.id, 1)),
|
3298
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3299
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3300
|
+
);
|
3301
|
+
|
3302
|
+
ctx.expect(result.length === 3);
|
3303
|
+
|
3304
|
+
assert.deepEqual(result, [
|
3305
|
+
{ id: 1, name: 'New York' },
|
3306
|
+
{ id: 1, name: 'John' },
|
3307
|
+
{ id: 1, name: 'John' }
|
3308
|
+
]);
|
3309
|
+
|
3310
|
+
ctx
|
3311
|
+
.expect(() => {
|
3312
|
+
unionAll(
|
3313
|
+
ctx.db2
|
3314
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3315
|
+
.from(cities2Table)
|
3316
|
+
.where(eq(citiesTable.id, 1)),
|
3317
|
+
ctx.db2.select({ name: users2Table.name, id: users2Table.id }).from(users2Table).where(eq(users2Table.id, 1)),
|
3318
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3319
|
+
);
|
3320
|
+
})
|
3321
|
+
.toThrowError();
|
3322
|
+
});
|
3323
|
+
|
3324
|
+
test('set operations (intersect) from query builder', async (ctx) => {
|
3325
|
+
await setupSetOperationSuite(ctx.db2);
|
3326
|
+
|
3327
|
+
const result = await ctx.db2
|
3328
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3329
|
+
.from(cities2Table)
|
3330
|
+
.intersect(
|
3331
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).where(gt(citiesTable.id, 1))
|
3332
|
+
)
|
3333
|
+
.orderBy(asc(sql`name`));
|
3334
|
+
|
3335
|
+
ctx.expect(result.length === 2);
|
3336
|
+
|
3337
|
+
assert.deepEqual(result, [
|
3338
|
+
{ id: 2, name: 'London' },
|
3339
|
+
{ id: 3, name: 'Tampa' }
|
3340
|
+
]);
|
3341
|
+
|
3342
|
+
ctx
|
3343
|
+
.expect(() => {
|
3344
|
+
ctx.db2
|
3345
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3346
|
+
.from(cities2Table)
|
3347
|
+
.intersect(
|
3348
|
+
// @ts-expect-error
|
3349
|
+
db
|
3350
|
+
.select({ id: cities2Table.id, name: citiesTable.name, id2: cities2Table.id })
|
3351
|
+
.from(cities2Table)
|
3352
|
+
.where(gt(citiesTable.id, 1))
|
3353
|
+
)
|
3354
|
+
.orderBy(asc(sql`name`));
|
3355
|
+
})
|
3356
|
+
.toThrowError();
|
3357
|
+
});
|
3358
|
+
|
3359
|
+
test('set operations (intersect) as function', async (ctx) => {
|
3360
|
+
await setupSetOperationSuite(ctx.db2);
|
3361
|
+
|
3362
|
+
const result = await intersect(
|
3363
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).where(eq(citiesTable.id, 1)),
|
3364
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3365
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3366
|
+
);
|
3367
|
+
|
3368
|
+
ctx.expect(result.length === 0);
|
3369
|
+
|
3370
|
+
assert.deepEqual(result, []);
|
3371
|
+
|
3372
|
+
ctx
|
3373
|
+
.expect(() => {
|
3374
|
+
intersect(
|
3375
|
+
ctx.db2
|
3376
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3377
|
+
.from(cities2Table)
|
3378
|
+
.where(eq(citiesTable.id, 1)),
|
3379
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3380
|
+
ctx.db2.select({ name: users2Table.name, id: users2Table.id }).from(users2Table).where(eq(users2Table.id, 1))
|
3381
|
+
);
|
3382
|
+
})
|
3383
|
+
.toThrowError();
|
3384
|
+
});
|
3385
|
+
|
3386
|
+
test('set operations (intersect all) from query builder', async (ctx) => {
|
3387
|
+
await setupSetOperationSuite(ctx.db2);
|
3388
|
+
|
3389
|
+
const result = await ctx.db2
|
3390
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3391
|
+
.from(cities2Table)
|
3392
|
+
.limit(2)
|
3393
|
+
.intersectAll(ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).limit(2))
|
3394
|
+
.orderBy(asc(sql`id`));
|
3395
|
+
|
3396
|
+
ctx.expect(result.length === 2);
|
3397
|
+
|
3398
|
+
assert.deepEqual(result, [
|
3399
|
+
{ id: 1, name: 'New York' },
|
3400
|
+
{ id: 2, name: 'London' }
|
3401
|
+
]);
|
3402
|
+
|
3403
|
+
ctx
|
3404
|
+
.expect(() => {
|
3405
|
+
ctx.db2
|
3406
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3407
|
+
.from(cities2Table)
|
3408
|
+
.limit(2)
|
3409
|
+
.intersectAll(ctx.db2.select({ name: users2Table.name, id: users2Table.id }).from(cities2Table).limit(2))
|
3410
|
+
.orderBy(asc(sql`id`));
|
3411
|
+
})
|
3412
|
+
.toThrowError();
|
3413
|
+
});
|
3414
|
+
|
3415
|
+
test('set operations (intersect all) as function', async (ctx) => {
|
3416
|
+
await setupSetOperationSuite(ctx.db2);
|
3417
|
+
|
3418
|
+
const result = await intersectAll(
|
3419
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3420
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3421
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3422
|
+
);
|
3423
|
+
|
3424
|
+
ctx.expect(result.length === 1);
|
3425
|
+
|
3426
|
+
assert.deepEqual(result, [{ id: 1, name: 'John' }]);
|
3427
|
+
|
3428
|
+
ctx
|
3429
|
+
.expect(() => {
|
3430
|
+
intersectAll(
|
3431
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3432
|
+
ctx.db2.select({ name: users2Table.name, id: users2Table.id }).from(users2Table).where(eq(users2Table.id, 1)),
|
3433
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3434
|
+
);
|
3435
|
+
})
|
3436
|
+
.toThrowError();
|
3437
|
+
});
|
3438
|
+
|
3439
|
+
test('set operations (except) from query builder', async (ctx) => {
|
3440
|
+
await setupSetOperationSuite(ctx.db2);
|
3441
|
+
|
3442
|
+
const result = await ctx.db2
|
3443
|
+
.select()
|
3444
|
+
.from(cities2Table)
|
3445
|
+
.except(ctx.db2.select().from(cities2Table).where(gt(citiesTable.id, 1)));
|
3446
|
+
|
3447
|
+
ctx.expect(result.length === 1);
|
3448
|
+
|
3449
|
+
assert.deepEqual(result, [{ id: 1, name: 'New York' }]);
|
3450
|
+
|
3451
|
+
ctx
|
3452
|
+
.expect(() => {
|
3453
|
+
ctx.db2
|
3454
|
+
.select()
|
3455
|
+
.from(cities2Table)
|
3456
|
+
.except(
|
3457
|
+
ctx.db2
|
3458
|
+
.select({ name: users2Table.name, id: users2Table.id })
|
3459
|
+
.from(cities2Table)
|
3460
|
+
.where(gt(citiesTable.id, 1))
|
3461
|
+
);
|
3462
|
+
})
|
3463
|
+
.toThrowError();
|
3464
|
+
});
|
3465
|
+
|
3466
|
+
test('set operations (except) as function', async (ctx) => {
|
3467
|
+
await setupSetOperationSuite(ctx.db2);
|
3468
|
+
|
3469
|
+
const result = await except(
|
3470
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table),
|
3471
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).where(eq(citiesTable.id, 1)),
|
3472
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3473
|
+
).orderBy(asc(sql`id`));
|
3474
|
+
|
3475
|
+
ctx.expect(result.length === 2);
|
3476
|
+
|
3477
|
+
assert.deepEqual(result, [
|
3478
|
+
{ id: 2, name: 'London' },
|
3479
|
+
{ id: 3, name: 'Tampa' }
|
3480
|
+
]);
|
3481
|
+
|
3482
|
+
ctx
|
3483
|
+
.expect(() => {
|
3484
|
+
except(
|
3485
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table),
|
3486
|
+
ctx.db2
|
3487
|
+
.select({ name: users2Table.name, id: users2Table.id })
|
3488
|
+
.from(cities2Table)
|
3489
|
+
.where(eq(citiesTable.id, 1)),
|
3490
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3491
|
+
).orderBy(asc(sql`id`));
|
3492
|
+
})
|
3493
|
+
.toThrowError();
|
3494
|
+
});
|
3495
|
+
|
3496
|
+
test('set operations (except all) from query builder', async (ctx) => {
|
3497
|
+
await setupSetOperationSuite(ctx.db2);
|
3498
|
+
|
3499
|
+
const result = await ctx.db2
|
3500
|
+
.select()
|
3501
|
+
.from(cities2Table)
|
3502
|
+
.exceptAll(
|
3503
|
+
ctx.db2.select({ id: cities2Table.id, name: citiesTable.name }).from(cities2Table).where(eq(citiesTable.id, 1))
|
3504
|
+
)
|
3505
|
+
.orderBy(asc(sql`id`));
|
3506
|
+
|
3507
|
+
ctx.expect(result.length === 2);
|
3508
|
+
|
3509
|
+
assert.deepEqual(result, [
|
3510
|
+
{ id: 2, name: 'London' },
|
3511
|
+
{ id: 3, name: 'Tampa' }
|
3512
|
+
]);
|
3513
|
+
|
3514
|
+
ctx
|
3515
|
+
.expect(() => {
|
3516
|
+
ctx.db2
|
3517
|
+
.select({ name: cities2Table.name, id: cities2Table.id })
|
3518
|
+
.from(cities2Table)
|
3519
|
+
.exceptAll(
|
3520
|
+
ctx.db2
|
3521
|
+
.select({ id: cities2Table.id, name: citiesTable.name })
|
3522
|
+
.from(cities2Table)
|
3523
|
+
.where(eq(citiesTable.id, 1))
|
3524
|
+
)
|
3525
|
+
.orderBy(asc(sql`id`));
|
3526
|
+
})
|
3527
|
+
.toThrowError();
|
3528
|
+
});
|
3529
|
+
|
3530
|
+
test('set operations (except all) as function', async (ctx) => {
|
3531
|
+
// Error: invalid SQL: unused parameters: used 2 of 4 parameters
|
3532
|
+
if (type === 'http') return ctx.skip();
|
3533
|
+
|
3534
|
+
await setupSetOperationSuite(ctx.db2);
|
3535
|
+
|
3536
|
+
const result = await exceptAll(
|
3537
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table),
|
3538
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(gt(users2Table.id, 7)),
|
3539
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3540
|
+
)
|
3541
|
+
.orderBy(asc(sql`id`))
|
3542
|
+
.limit(5)
|
3543
|
+
.offset(2);
|
3544
|
+
|
3545
|
+
ctx.expect(result.length === 4);
|
3546
|
+
|
3547
|
+
assert.deepEqual(result, [
|
3548
|
+
{ id: 4, name: 'Peter' },
|
3549
|
+
{ id: 5, name: 'Ben' },
|
3550
|
+
{ id: 6, name: 'Jill' },
|
3551
|
+
{ id: 7, name: 'Mary' }
|
3552
|
+
]);
|
3553
|
+
|
3554
|
+
ctx
|
3555
|
+
.expect(() => {
|
3556
|
+
exceptAll(
|
3557
|
+
ctx.db2.select({ name: users2Table.name, id: users2Table.id }).from(users2Table),
|
3558
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(gt(users2Table.id, 7)),
|
3559
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1))
|
3560
|
+
).orderBy(asc(sql`id`));
|
3561
|
+
})
|
3562
|
+
.toThrowError();
|
3563
|
+
});
|
3564
|
+
|
3565
|
+
test('set operations (mixed) from query builder with subquery', async (ctx) => {
|
3566
|
+
await setupSetOperationSuite(ctx.db2);
|
3567
|
+
const sq = ctx.db2.select().from(cities2Table).where(gt(citiesTable.id, 1)).as('sq');
|
3568
|
+
|
3569
|
+
const result = await ctx.db2
|
3570
|
+
.select()
|
3571
|
+
.from(cities2Table)
|
3572
|
+
.except(({ unionAll }) =>
|
3573
|
+
unionAll(ctx.db2.select().from(sq), ctx.db2.select().from(cities2Table).where(eq(citiesTable.id, 2)))
|
3574
|
+
);
|
3575
|
+
|
3576
|
+
ctx.expect(result.length === 1);
|
3577
|
+
|
3578
|
+
assert.deepEqual(result, [{ id: 1, name: 'New York' }]);
|
3579
|
+
|
3580
|
+
ctx
|
3581
|
+
.expect(() => {
|
3582
|
+
ctx.db2
|
3583
|
+
.select()
|
3584
|
+
.from(cities2Table)
|
3585
|
+
.except(({ unionAll }) =>
|
3586
|
+
unionAll(
|
3587
|
+
ctx.db2
|
3588
|
+
.select({ name: cities2Table.name, id: cities2Table.id })
|
3589
|
+
.from(cities2Table)
|
3590
|
+
.where(gt(citiesTable.id, 1)),
|
3591
|
+
ctx.db2.select().from(cities2Table).where(eq(citiesTable.id, 2))
|
3592
|
+
)
|
3593
|
+
);
|
3594
|
+
})
|
3595
|
+
.toThrowError();
|
3596
|
+
});
|
3597
|
+
|
3598
|
+
test('set operations (mixed all) as function', async (ctx) => {
|
3599
|
+
await setupSetOperationSuite(ctx.db2);
|
3600
|
+
|
3601
|
+
const result = await union(
|
3602
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3603
|
+
except(
|
3604
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(gte(users2Table.id, 5)),
|
3605
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 7))
|
3606
|
+
),
|
3607
|
+
ctx.db2.select().from(cities2Table).where(gt(citiesTable.id, 1))
|
3608
|
+
).orderBy(asc(sql`id`));
|
3609
|
+
|
3610
|
+
ctx.expect(result.length === 6);
|
3611
|
+
|
3612
|
+
assert.deepEqual(result, [
|
3613
|
+
{ id: 1, name: 'John' },
|
3614
|
+
{ id: 2, name: 'London' },
|
3615
|
+
{ id: 3, name: 'Tampa' },
|
3616
|
+
{ id: 5, name: 'Ben' },
|
3617
|
+
{ id: 6, name: 'Jill' },
|
3618
|
+
{ id: 8, name: 'Sally' }
|
3619
|
+
]);
|
3620
|
+
|
3621
|
+
ctx
|
3622
|
+
.expect(() => {
|
3623
|
+
union(
|
3624
|
+
ctx.db2.select({ id: users2Table.id, name: users2Table.name }).from(users2Table).where(eq(users2Table.id, 1)),
|
3625
|
+
except(
|
3626
|
+
ctx.db2
|
3627
|
+
.select({ id: users2Table.id, name: users2Table.name })
|
3628
|
+
.from(users2Table)
|
3629
|
+
.where(gte(users2Table.id, 5)),
|
3630
|
+
ctx.db2
|
3631
|
+
.select({ name: users2Table.name, id: users2Table.id })
|
3632
|
+
.from(users2Table)
|
3633
|
+
.where(eq(users2Table.id, 7))
|
3634
|
+
),
|
3635
|
+
ctx.db2.select().from(cities2Table).where(gt(citiesTable.id, 1))
|
3636
|
+
).orderBy(asc(sql`id`));
|
3637
|
+
})
|
3638
|
+
.toThrowError();
|
3639
|
+
});
|
3640
|
+
|
3641
|
+
test('aggregate function: count', async (ctx) => {
|
3642
|
+
const table = aggregateTable;
|
3643
|
+
await setupAggregateFunctionsSuite(ctx.db2);
|
3644
|
+
|
3645
|
+
const result1 = await ctx.db2.select({ value: count() }).from(table);
|
3646
|
+
const result2 = await ctx.db2.select({ value: count(table.a) }).from(table);
|
3647
|
+
const result3 = await ctx.db2.select({ value: countDistinct(table.name) }).from(table);
|
3648
|
+
|
3649
|
+
assert.deepEqual(result1[0]?.value, 7);
|
3650
|
+
assert.deepEqual(result2[0]?.value, 5);
|
3651
|
+
assert.deepEqual(result3[0]?.value, 6);
|
3652
|
+
});
|
3653
|
+
|
3654
|
+
test('aggregate function: avg', async (ctx) => {
|
3655
|
+
const table = aggregateTable;
|
3656
|
+
await setupAggregateFunctionsSuite(ctx.db2);
|
3657
|
+
|
3658
|
+
const result1 = await ctx.db2.select({ value: avg(table.b) }).from(table);
|
3659
|
+
const result2 = await ctx.db2.select({ value: avg(table.nullOnly) }).from(table);
|
3660
|
+
const result3 = await ctx.db2.select({ value: avgDistinct(table.b) }).from(table);
|
3661
|
+
|
3662
|
+
assert.deepEqual(result1[0]?.value, '33.3333333333333333');
|
3663
|
+
assert.deepEqual(result2[0]?.value, null);
|
3664
|
+
assert.deepEqual(result3[0]?.value, '42.5000000000000000');
|
3665
|
+
});
|
3666
|
+
|
3667
|
+
test('aggregate function: sum', async (ctx) => {
|
3668
|
+
const table = aggregateTable;
|
3669
|
+
await setupAggregateFunctionsSuite(ctx.db2);
|
3670
|
+
|
3671
|
+
const result1 = await ctx.db2.select({ value: sum(table.b) }).from(table);
|
3672
|
+
const result2 = await ctx.db2.select({ value: sum(table.nullOnly) }).from(table);
|
3673
|
+
const result3 = await ctx.db2.select({ value: sumDistinct(table.b) }).from(table);
|
3674
|
+
|
3675
|
+
assert.deepEqual(result1[0]?.value, '200');
|
3676
|
+
assert.deepEqual(result2[0]?.value, null);
|
3677
|
+
assert.deepEqual(result3[0]?.value, '170');
|
3678
|
+
});
|
3679
|
+
|
3680
|
+
test('aggregate function: max', async (ctx) => {
|
3681
|
+
const table = aggregateTable;
|
3682
|
+
await setupAggregateFunctionsSuite(ctx.db2);
|
3683
|
+
|
3684
|
+
const result1 = await ctx.db2.select({ value: max(table.b) }).from(table);
|
3685
|
+
const result2 = await ctx.db2.select({ value: max(table.nullOnly) }).from(table);
|
3686
|
+
|
3687
|
+
assert.deepEqual(result1[0]?.value, 90);
|
3688
|
+
assert.deepEqual(result2[0]?.value, null);
|
3689
|
+
});
|
3690
|
+
|
3691
|
+
test('aggregate function: min', async (ctx) => {
|
3692
|
+
const table = aggregateTable;
|
3693
|
+
await setupAggregateFunctionsSuite(ctx.db2);
|
3694
|
+
|
3695
|
+
const result1 = await ctx.db2.select({ value: min(table.b) }).from(table);
|
3696
|
+
const result2 = await ctx.db2.select({ value: min(table.nullOnly) }).from(table);
|
3697
|
+
|
3698
|
+
assert.deepEqual(result1[0]?.value, 10);
|
3699
|
+
assert.deepEqual(result2[0]?.value, null);
|
3700
|
+
});
|
3701
|
+
|
3702
|
+
test('array mapping and parsing', async (ctx) => {
|
3703
|
+
// Array support
|
3704
|
+
if (type === 'http') return ctx.skip();
|
3705
|
+
|
3706
|
+
const arrays = pgTable('arrays_tests', {
|
3707
|
+
id: serial('id').primaryKey(),
|
3708
|
+
tags: text('tags').array(),
|
3709
|
+
nested: text('nested').array().array(),
|
3710
|
+
numbers: integer('numbers').notNull().array()
|
3711
|
+
});
|
3712
|
+
|
3713
|
+
await ctx.db2.execute(sql`drop table if exists ${arrays}`);
|
3714
|
+
await ctx.db2.execute(sql`
|
3715
|
+
create table ${arrays} (
|
3716
|
+
id serial primary key,
|
3717
|
+
tags text[],
|
3718
|
+
nested text[][],
|
3719
|
+
numbers integer[]
|
3720
|
+
)
|
3721
|
+
`);
|
3722
|
+
|
3723
|
+
await ctx.db2.insert(arrays).values({
|
3724
|
+
tags: ['', 'b', 'c'],
|
3725
|
+
nested: [
|
3726
|
+
['1', ''],
|
3727
|
+
['3', '\\a']
|
3728
|
+
],
|
3729
|
+
numbers: [1, 2, 3]
|
3730
|
+
});
|
3731
|
+
|
3732
|
+
const result = await ctx.db2.select().from(arrays);
|
3733
|
+
|
3734
|
+
assert.deepEqual(result, [
|
3735
|
+
{
|
3736
|
+
id: 1,
|
3737
|
+
tags: ['', 'b', 'c'],
|
3738
|
+
nested: [
|
3739
|
+
['1', ''],
|
3740
|
+
['3', '\\a']
|
3741
|
+
],
|
3742
|
+
numbers: [1, 2, 3]
|
3743
|
+
}
|
3744
|
+
]);
|
3745
|
+
|
3746
|
+
await ctx.db2.execute(sql`drop table ${arrays}`);
|
3747
|
+
});
|
3748
|
+
});
|
3749
|
+
|
3750
|
+
const randomString = () => {
|
3751
|
+
return Array.from({ length: 10 }, () => 'abcdefghijklmnopqrstuvwxyz'[Math.floor(Math.random() * 26)]).join('');
|
3752
|
+
};
|
3753
|
+
|
3754
|
+
async function waitForReplication(): Promise<void> {
|
3755
|
+
try {
|
3756
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
3757
|
+
await api.branches.getBranchList({ workspace, database, region });
|
3758
|
+
} catch (error) {
|
3759
|
+
console.log(`Replication not ready yet, retrying...`);
|
3760
|
+
return await waitForReplication();
|
3761
|
+
}
|
3762
|
+
}
|
3763
|
+
|
3764
|
+
// shut up eslint you cannot possibly comprehend what's happening here
|
3765
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
|
3766
|
+
export function Expect<T extends true>() {}
|
3767
|
+
|
3768
|
+
export type Equal<X, Y extends X> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
|