@xata.io/drizzle 0.0.0-alpha.vbaa467c → 0.0.0-alpha.vbae8d27060d2a4d1064936766420bd236bfa7e73

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