@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.
@@ -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;