@xata.io/drizzle 0.0.0-alpha.ve565538 → 0.0.0-alpha.ve69ec6a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +2 -2
- package/dist/index.cjs +12 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +12 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -4
- package/test/integration.ava.ts +2187 -0
- package/test/utils.ts +5 -0
- package/test/xata.codegen.ts +149 -0
@@ -0,0 +1,2187 @@
|
|
1
|
+
import { getHostUrl, parseProviderString, XataApiClient } from '@xata.io/client';
|
2
|
+
import type { TestFn } from 'ava';
|
3
|
+
import anyTest from 'ava';
|
4
|
+
import dotenv from 'dotenv';
|
5
|
+
import {
|
6
|
+
and,
|
7
|
+
asc,
|
8
|
+
eq,
|
9
|
+
gt,
|
10
|
+
gte,
|
11
|
+
inArray,
|
12
|
+
lt,
|
13
|
+
name,
|
14
|
+
placeholder,
|
15
|
+
sql,
|
16
|
+
TransactionRollbackError,
|
17
|
+
type SQL,
|
18
|
+
type SQLWrapper
|
19
|
+
} from 'drizzle-orm';
|
20
|
+
import {
|
21
|
+
alias,
|
22
|
+
boolean,
|
23
|
+
char,
|
24
|
+
cidr,
|
25
|
+
getMaterializedViewConfig,
|
26
|
+
getViewConfig,
|
27
|
+
inet,
|
28
|
+
integer,
|
29
|
+
jsonb,
|
30
|
+
macaddr,
|
31
|
+
macaddr8,
|
32
|
+
pgEnum,
|
33
|
+
pgMaterializedView,
|
34
|
+
pgTable,
|
35
|
+
pgTableCreator,
|
36
|
+
uuid as pgUuid,
|
37
|
+
pgView,
|
38
|
+
serial,
|
39
|
+
text,
|
40
|
+
timestamp,
|
41
|
+
varchar,
|
42
|
+
type PgColumn
|
43
|
+
} from 'drizzle-orm/pg-core';
|
44
|
+
import { migrate } from 'drizzle-orm/vercel-postgres/migrator';
|
45
|
+
import fetch from 'node-fetch';
|
46
|
+
import { join } from 'path';
|
47
|
+
import { v4 as uuid } from 'uuid';
|
48
|
+
import { drizzle, type XataDatabase } from '../src';
|
49
|
+
import { Expect, type Equal } from './utils';
|
50
|
+
import { tables, XataClient } from './xata.codegen';
|
51
|
+
|
52
|
+
const ENABLE_LOGGING = false;
|
53
|
+
|
54
|
+
// Get environment variables before reading them
|
55
|
+
dotenv.config({ path: join(process.cwd(), '.env') });
|
56
|
+
|
57
|
+
const apiKey = process.env.XATA_API_KEY ?? '';
|
58
|
+
if (apiKey === '') throw new Error('XATA_API_KEY environment variable is not set');
|
59
|
+
|
60
|
+
const workspace = process.env.XATA_WORKSPACE ?? '';
|
61
|
+
if (workspace === '') throw new Error('XATA_WORKSPACE environment variable is not set');
|
62
|
+
|
63
|
+
const region = process.env.XATA_REGION || 'eu-west-1';
|
64
|
+
|
65
|
+
const host = parseProviderString(process.env.XATA_API_PROVIDER);
|
66
|
+
if (host === null) {
|
67
|
+
throw new Error(
|
68
|
+
`Invalid XATA_API_PROVIDER environment variable, expected either "production", "staging" or "apiUrl,workspacesUrl"`
|
69
|
+
);
|
70
|
+
}
|
71
|
+
|
72
|
+
const usersTable = pgTable('users', {
|
73
|
+
id: serial('id').primaryKey(),
|
74
|
+
name: text('name').notNull(),
|
75
|
+
verified: boolean('verified').notNull().default(false),
|
76
|
+
jsonb: jsonb('jsonb').$type<string[]>(),
|
77
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow()
|
78
|
+
});
|
79
|
+
|
80
|
+
const citiesTable = pgTable('cities', {
|
81
|
+
id: serial('id').primaryKey(),
|
82
|
+
name: text('name').notNull(),
|
83
|
+
state: char('state', { length: 2 })
|
84
|
+
});
|
85
|
+
|
86
|
+
const users2Table = pgTable('users2', {
|
87
|
+
id: serial('id').primaryKey(),
|
88
|
+
name: text('name').notNull(),
|
89
|
+
cityId: integer('city_id').references(() => citiesTable.id)
|
90
|
+
});
|
91
|
+
|
92
|
+
const coursesTable = pgTable('courses', {
|
93
|
+
id: serial('id').primaryKey(),
|
94
|
+
name: text('name').notNull(),
|
95
|
+
categoryId: integer('category_id').references(() => courseCategoriesTable.id)
|
96
|
+
});
|
97
|
+
|
98
|
+
const courseCategoriesTable = pgTable('course_categories', {
|
99
|
+
id: serial('id').primaryKey(),
|
100
|
+
name: text('name').notNull()
|
101
|
+
});
|
102
|
+
|
103
|
+
const orders = pgTable('orders', {
|
104
|
+
id: serial('id').primaryKey(),
|
105
|
+
region: text('region').notNull(),
|
106
|
+
product: text('product').notNull(),
|
107
|
+
amount: integer('amount').notNull(),
|
108
|
+
quantity: integer('quantity').notNull()
|
109
|
+
});
|
110
|
+
|
111
|
+
const network = pgTable('network_table', {
|
112
|
+
inet: inet('inet').notNull(),
|
113
|
+
cidr: cidr('cidr').notNull(),
|
114
|
+
macaddr: macaddr('macaddr').notNull(),
|
115
|
+
macaddr8: macaddr8('macaddr8').notNull()
|
116
|
+
});
|
117
|
+
|
118
|
+
const salEmp = pgTable('sal_emp', {
|
119
|
+
name: text('name'),
|
120
|
+
payByQuarter: integer('pay_by_quarter').array(),
|
121
|
+
schedule: text('schedule').array().array()
|
122
|
+
});
|
123
|
+
|
124
|
+
const _tictactoe = pgTable('tictactoe', {
|
125
|
+
squares: integer('squares').array(3).array(3)
|
126
|
+
});
|
127
|
+
|
128
|
+
const usersMigratorTable = pgTable('users12', {
|
129
|
+
id: serial('id').primaryKey(),
|
130
|
+
name: text('name').notNull(),
|
131
|
+
email: text('email').notNull()
|
132
|
+
});
|
133
|
+
|
134
|
+
interface Context {
|
135
|
+
id: string;
|
136
|
+
db: XataDatabase;
|
137
|
+
client: XataClient;
|
138
|
+
api: XataApiClient;
|
139
|
+
}
|
140
|
+
|
141
|
+
const test = anyTest as TestFn<Context>;
|
142
|
+
|
143
|
+
test.before(async (t) => {
|
144
|
+
const ctx = t.context;
|
145
|
+
ctx.api = new XataApiClient({ apiKey, fetch, host, clientName: 'sdk-tests' });
|
146
|
+
});
|
147
|
+
|
148
|
+
test.beforeEach(async (t) => {
|
149
|
+
const ctx = t.context;
|
150
|
+
ctx.id = Date.now().toString(36);
|
151
|
+
|
152
|
+
const { databaseName: database } = await ctx.api.database.createDatabase({
|
153
|
+
workspace,
|
154
|
+
database: `sdk-integration-test-drizzle-${ctx.id}`,
|
155
|
+
data: { region },
|
156
|
+
headers: { 'X-Xata-Files': 'true' }
|
157
|
+
});
|
158
|
+
|
159
|
+
const { edits } = await ctx.api.migrations.compareBranchWithUserSchema({
|
160
|
+
workspace,
|
161
|
+
region,
|
162
|
+
database,
|
163
|
+
branch: 'main',
|
164
|
+
schema: { tables: tables as any }
|
165
|
+
});
|
166
|
+
|
167
|
+
await ctx.api.migrations.applyBranchSchemaEdit({ workspace, region, database, branch: 'main', edits });
|
168
|
+
|
169
|
+
const workspaceUrl = getHostUrl(host, 'workspaces').replace('{workspaceId}', workspace).replace('{region}', region);
|
170
|
+
|
171
|
+
ctx.client = new XataClient({
|
172
|
+
databaseURL: `${workspaceUrl}/db/${database}`,
|
173
|
+
branch: 'main',
|
174
|
+
apiKey,
|
175
|
+
fetch,
|
176
|
+
clientName: 'sdk-tests'
|
177
|
+
});
|
178
|
+
|
179
|
+
ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING });
|
180
|
+
});
|
181
|
+
|
182
|
+
test.afterEach.always(async (t) => {
|
183
|
+
const ctx = t.context;
|
184
|
+
|
185
|
+
await ctx.api.database.deleteDatabase({ workspace, database: `sdk-integration-test-drizzle-${ctx.id}` });
|
186
|
+
});
|
187
|
+
|
188
|
+
test.skip('select all fields', async (t) => {
|
189
|
+
const { db } = t.context;
|
190
|
+
|
191
|
+
const now = Date.now();
|
192
|
+
|
193
|
+
await db.insert(usersTable).values({ name: 'John' });
|
194
|
+
const result = await db.select().from(usersTable);
|
195
|
+
|
196
|
+
t.assert(result[0]!.createdAt instanceof Date);
|
197
|
+
t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100);
|
198
|
+
t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]);
|
199
|
+
});
|
200
|
+
|
201
|
+
test.skip('select sql', async (t) => {
|
202
|
+
const { db } = t.context;
|
203
|
+
|
204
|
+
await db.insert(usersTable).values({ name: 'John' });
|
205
|
+
const users = await db
|
206
|
+
.select({
|
207
|
+
name: sql`upper(${usersTable.name})`
|
208
|
+
})
|
209
|
+
.from(usersTable);
|
210
|
+
|
211
|
+
t.deepEqual(users, [{ name: 'JOHN' }]);
|
212
|
+
});
|
213
|
+
|
214
|
+
test.skip('select typed sql', async (t) => {
|
215
|
+
const { db } = t.context;
|
216
|
+
|
217
|
+
await db.insert(usersTable).values({ name: 'John' });
|
218
|
+
|
219
|
+
const users = await db
|
220
|
+
.select({
|
221
|
+
name: sql<string>`upper(${usersTable.name})`
|
222
|
+
})
|
223
|
+
.from(usersTable);
|
224
|
+
|
225
|
+
t.deepEqual(users, [{ name: 'JOHN' }]);
|
226
|
+
});
|
227
|
+
|
228
|
+
test.skip('select distinct', async (t) => {
|
229
|
+
const { db } = t.context;
|
230
|
+
|
231
|
+
const usersDistinctTable = pgTable('users_distinct', {
|
232
|
+
id: integer('id').notNull(),
|
233
|
+
name: text('name').notNull()
|
234
|
+
});
|
235
|
+
|
236
|
+
await db.execute(sql`drop table if exists ${usersDistinctTable}`);
|
237
|
+
await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`);
|
238
|
+
|
239
|
+
await db.insert(usersDistinctTable).values([
|
240
|
+
{ id: 1, name: 'John' },
|
241
|
+
{ id: 1, name: 'John' },
|
242
|
+
{ id: 2, name: 'John' },
|
243
|
+
{ id: 1, name: 'Jane' }
|
244
|
+
]);
|
245
|
+
const users1 = await db
|
246
|
+
.selectDistinct()
|
247
|
+
.from(usersDistinctTable)
|
248
|
+
.orderBy(usersDistinctTable.id, usersDistinctTable.name);
|
249
|
+
const users2 = await db
|
250
|
+
.selectDistinctOn([usersDistinctTable.id])
|
251
|
+
.from(usersDistinctTable)
|
252
|
+
.orderBy(usersDistinctTable.id);
|
253
|
+
const users3 = await db
|
254
|
+
.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name })
|
255
|
+
.from(usersDistinctTable)
|
256
|
+
.orderBy(usersDistinctTable.name);
|
257
|
+
|
258
|
+
await db.execute(sql`drop table ${usersDistinctTable}`);
|
259
|
+
|
260
|
+
t.deepEqual(users1, [
|
261
|
+
{ id: 1, name: 'Jane' },
|
262
|
+
{ id: 1, name: 'John' },
|
263
|
+
{ id: 2, name: 'John' }
|
264
|
+
]);
|
265
|
+
|
266
|
+
t.deepEqual(users2.length, 2);
|
267
|
+
t.deepEqual(users2[0]?.id, 1);
|
268
|
+
t.deepEqual(users2[1]?.id, 2);
|
269
|
+
|
270
|
+
t.deepEqual(users3.length, 2);
|
271
|
+
t.deepEqual(users3[0]?.name, 'Jane');
|
272
|
+
t.deepEqual(users3[1]?.name, 'John');
|
273
|
+
});
|
274
|
+
|
275
|
+
test.skip('insert returning sql', async (t) => {
|
276
|
+
const { db } = t.context;
|
277
|
+
|
278
|
+
const users = await db
|
279
|
+
.insert(usersTable)
|
280
|
+
.values({ name: 'John' })
|
281
|
+
.returning({
|
282
|
+
name: sql`upper(${usersTable.name})`
|
283
|
+
});
|
284
|
+
|
285
|
+
t.deepEqual(users, [{ name: 'JOHN' }]);
|
286
|
+
});
|
287
|
+
|
288
|
+
test.skip('delete returning sql', async (t) => {
|
289
|
+
const { db } = t.context;
|
290
|
+
|
291
|
+
await db.insert(usersTable).values({ name: 'John' });
|
292
|
+
const users = await db
|
293
|
+
.delete(usersTable)
|
294
|
+
.where(eq(usersTable.name, 'John'))
|
295
|
+
.returning({
|
296
|
+
name: sql`upper(${usersTable.name})`
|
297
|
+
});
|
298
|
+
|
299
|
+
t.deepEqual(users, [{ name: 'JOHN' }]);
|
300
|
+
});
|
301
|
+
|
302
|
+
test.skip('update returning sql', async (t) => {
|
303
|
+
const { db } = t.context;
|
304
|
+
|
305
|
+
await db.insert(usersTable).values({ name: 'John' });
|
306
|
+
const users = await db
|
307
|
+
.update(usersTable)
|
308
|
+
.set({ name: 'Jane' })
|
309
|
+
.where(eq(usersTable.name, 'John'))
|
310
|
+
.returning({
|
311
|
+
name: sql`upper(${usersTable.name})`
|
312
|
+
});
|
313
|
+
|
314
|
+
t.deepEqual(users, [{ name: 'JANE' }]);
|
315
|
+
});
|
316
|
+
|
317
|
+
test.skip('update with returning all fields', async (t) => {
|
318
|
+
const { db } = t.context;
|
319
|
+
|
320
|
+
const now = Date.now();
|
321
|
+
|
322
|
+
await db.insert(usersTable).values({ name: 'John' });
|
323
|
+
const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning();
|
324
|
+
|
325
|
+
t.assert(users[0]!.createdAt instanceof Date);
|
326
|
+
t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100);
|
327
|
+
t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]);
|
328
|
+
});
|
329
|
+
|
330
|
+
test.skip('update with returning partial', async (t) => {
|
331
|
+
const { db } = t.context;
|
332
|
+
|
333
|
+
await db.insert(usersTable).values({ name: 'John' });
|
334
|
+
const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({
|
335
|
+
id: usersTable.id,
|
336
|
+
name: usersTable.name
|
337
|
+
});
|
338
|
+
|
339
|
+
t.deepEqual(users, [{ id: 1, name: 'Jane' }]);
|
340
|
+
});
|
341
|
+
|
342
|
+
test.skip('delete with returning all fields', async (t) => {
|
343
|
+
const { db } = t.context;
|
344
|
+
|
345
|
+
const now = Date.now();
|
346
|
+
|
347
|
+
await db.insert(usersTable).values({ name: 'John' });
|
348
|
+
const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning();
|
349
|
+
|
350
|
+
t.assert(users[0]!.createdAt instanceof Date);
|
351
|
+
t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100);
|
352
|
+
t.deepEqual(users, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]);
|
353
|
+
});
|
354
|
+
|
355
|
+
test.skip('delete with returning partial', async (t) => {
|
356
|
+
const { db } = t.context;
|
357
|
+
|
358
|
+
await db.insert(usersTable).values({ name: 'John' });
|
359
|
+
const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({
|
360
|
+
id: usersTable.id,
|
361
|
+
name: usersTable.name
|
362
|
+
});
|
363
|
+
|
364
|
+
t.deepEqual(users, [{ id: 1, name: 'John' }]);
|
365
|
+
});
|
366
|
+
|
367
|
+
test.skip('insert + select', async (t) => {
|
368
|
+
const { db } = t.context;
|
369
|
+
|
370
|
+
await db.insert(usersTable).values({ name: 'John' });
|
371
|
+
const result = await db.select().from(usersTable);
|
372
|
+
t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]);
|
373
|
+
|
374
|
+
await db.insert(usersTable).values({ name: 'Jane' });
|
375
|
+
const result2 = await db.select().from(usersTable);
|
376
|
+
t.deepEqual(result2, [
|
377
|
+
{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt },
|
378
|
+
{ id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }
|
379
|
+
]);
|
380
|
+
});
|
381
|
+
|
382
|
+
test.skip('json insert', async (t) => {
|
383
|
+
const { db } = t.context;
|
384
|
+
|
385
|
+
await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] });
|
386
|
+
const result = await db
|
387
|
+
.select({
|
388
|
+
id: usersTable.id,
|
389
|
+
name: usersTable.name,
|
390
|
+
jsonb: usersTable.jsonb
|
391
|
+
})
|
392
|
+
.from(usersTable);
|
393
|
+
|
394
|
+
t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]);
|
395
|
+
});
|
396
|
+
|
397
|
+
test.skip('char insert', async (t) => {
|
398
|
+
const { db } = t.context;
|
399
|
+
|
400
|
+
await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' });
|
401
|
+
const result = await db
|
402
|
+
.select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state })
|
403
|
+
.from(citiesTable);
|
404
|
+
|
405
|
+
t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]);
|
406
|
+
});
|
407
|
+
|
408
|
+
test.skip('char update', async (t) => {
|
409
|
+
const { db } = t.context;
|
410
|
+
|
411
|
+
await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' });
|
412
|
+
await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1));
|
413
|
+
const result = await db
|
414
|
+
.select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state })
|
415
|
+
.from(citiesTable);
|
416
|
+
|
417
|
+
t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]);
|
418
|
+
});
|
419
|
+
|
420
|
+
test.skip('char delete', async (t) => {
|
421
|
+
const { db } = t.context;
|
422
|
+
|
423
|
+
await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' });
|
424
|
+
await db.delete(citiesTable).where(eq(citiesTable.state, 'TX'));
|
425
|
+
const result = await db
|
426
|
+
.select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state })
|
427
|
+
.from(citiesTable);
|
428
|
+
|
429
|
+
t.deepEqual(result, []);
|
430
|
+
});
|
431
|
+
|
432
|
+
test.skip('insert with overridden default values', async (t) => {
|
433
|
+
const { db } = t.context;
|
434
|
+
|
435
|
+
await db.insert(usersTable).values({ name: 'John', verified: true });
|
436
|
+
const result = await db.select().from(usersTable);
|
437
|
+
|
438
|
+
t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]);
|
439
|
+
});
|
440
|
+
|
441
|
+
test.skip('insert many', async (t) => {
|
442
|
+
const { db } = t.context;
|
443
|
+
|
444
|
+
await db
|
445
|
+
.insert(usersTable)
|
446
|
+
.values([
|
447
|
+
{ name: 'John' },
|
448
|
+
{ name: 'Bruce', jsonb: ['foo', 'bar'] },
|
449
|
+
{ name: 'Jane' },
|
450
|
+
{ name: 'Austin', verified: true }
|
451
|
+
]);
|
452
|
+
const result = await db
|
453
|
+
.select({
|
454
|
+
id: usersTable.id,
|
455
|
+
name: usersTable.name,
|
456
|
+
jsonb: usersTable.jsonb,
|
457
|
+
verified: usersTable.verified
|
458
|
+
})
|
459
|
+
.from(usersTable);
|
460
|
+
|
461
|
+
t.deepEqual(result, [
|
462
|
+
{ id: 1, name: 'John', jsonb: null, verified: false },
|
463
|
+
{ id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false },
|
464
|
+
{ id: 3, name: 'Jane', jsonb: null, verified: false },
|
465
|
+
{ id: 4, name: 'Austin', jsonb: null, verified: true }
|
466
|
+
]);
|
467
|
+
});
|
468
|
+
|
469
|
+
test.skip('insert many with returning', async (t) => {
|
470
|
+
const { db } = t.context;
|
471
|
+
|
472
|
+
const result = await db
|
473
|
+
.insert(usersTable)
|
474
|
+
.values([
|
475
|
+
{ name: 'John' },
|
476
|
+
{ name: 'Bruce', jsonb: ['foo', 'bar'] },
|
477
|
+
{ name: 'Jane' },
|
478
|
+
{ name: 'Austin', verified: true }
|
479
|
+
])
|
480
|
+
.returning({
|
481
|
+
id: usersTable.id,
|
482
|
+
name: usersTable.name,
|
483
|
+
jsonb: usersTable.jsonb,
|
484
|
+
verified: usersTable.verified
|
485
|
+
});
|
486
|
+
|
487
|
+
t.deepEqual(result, [
|
488
|
+
{ id: 1, name: 'John', jsonb: null, verified: false },
|
489
|
+
{ id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false },
|
490
|
+
{ id: 3, name: 'Jane', jsonb: null, verified: false },
|
491
|
+
{ id: 4, name: 'Austin', jsonb: null, verified: true }
|
492
|
+
]);
|
493
|
+
});
|
494
|
+
|
495
|
+
test.skip('select with group by as field', async (t) => {
|
496
|
+
const { db } = t.context;
|
497
|
+
|
498
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
499
|
+
|
500
|
+
const result = await db.select({ name: usersTable.name }).from(usersTable).groupBy(usersTable.name);
|
501
|
+
|
502
|
+
t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]);
|
503
|
+
});
|
504
|
+
|
505
|
+
test.skip('select with group by as sql', async (t) => {
|
506
|
+
const { db } = t.context;
|
507
|
+
|
508
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
509
|
+
|
510
|
+
const result = await db
|
511
|
+
.select({ name: usersTable.name })
|
512
|
+
.from(usersTable)
|
513
|
+
.groupBy(sql`${usersTable.name}`);
|
514
|
+
|
515
|
+
t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]);
|
516
|
+
});
|
517
|
+
|
518
|
+
test.skip('select with group by as sql + column', async (t) => {
|
519
|
+
const { db } = t.context;
|
520
|
+
|
521
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
522
|
+
|
523
|
+
const result = await db
|
524
|
+
.select({ name: usersTable.name })
|
525
|
+
.from(usersTable)
|
526
|
+
.groupBy(sql`${usersTable.name}`, usersTable.id);
|
527
|
+
|
528
|
+
t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]);
|
529
|
+
});
|
530
|
+
|
531
|
+
test.skip('select with group by as column + sql', async (t) => {
|
532
|
+
const { db } = t.context;
|
533
|
+
|
534
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
535
|
+
|
536
|
+
const result = await db
|
537
|
+
.select({ name: usersTable.name })
|
538
|
+
.from(usersTable)
|
539
|
+
.groupBy(usersTable.id, sql`${usersTable.name}`);
|
540
|
+
|
541
|
+
t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]);
|
542
|
+
});
|
543
|
+
|
544
|
+
test.skip('select with group by complex query', async (t) => {
|
545
|
+
const { db } = t.context;
|
546
|
+
|
547
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]);
|
548
|
+
|
549
|
+
const result = await db
|
550
|
+
.select({ name: usersTable.name })
|
551
|
+
.from(usersTable)
|
552
|
+
.groupBy(usersTable.id, sql`${usersTable.name}`)
|
553
|
+
.orderBy(asc(usersTable.name))
|
554
|
+
.limit(1);
|
555
|
+
|
556
|
+
t.deepEqual(result, [{ name: 'Jane' }]);
|
557
|
+
});
|
558
|
+
|
559
|
+
test('build query', async (t) => {
|
560
|
+
const { db } = t.context;
|
561
|
+
|
562
|
+
const query = db
|
563
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
564
|
+
.from(usersTable)
|
565
|
+
.groupBy(usersTable.id, usersTable.name)
|
566
|
+
.toSQL();
|
567
|
+
|
568
|
+
t.deepEqual(query, {
|
569
|
+
sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"',
|
570
|
+
params: []
|
571
|
+
});
|
572
|
+
});
|
573
|
+
|
574
|
+
test.skip('insert sql', async (t) => {
|
575
|
+
const { db } = t.context;
|
576
|
+
|
577
|
+
await db.insert(usersTable).values({ name: sql`${'John'}` });
|
578
|
+
const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable);
|
579
|
+
t.deepEqual(result, [{ id: 1, name: 'John' }]);
|
580
|
+
});
|
581
|
+
|
582
|
+
test.skip('partial join with alias', async (t) => {
|
583
|
+
const { db } = t.context;
|
584
|
+
const customerAlias = alias(usersTable, 'customer');
|
585
|
+
|
586
|
+
await db.insert(usersTable).values([
|
587
|
+
{ id: 10, name: 'Ivan' },
|
588
|
+
{ id: 11, name: 'Hans' }
|
589
|
+
]);
|
590
|
+
const result = await db
|
591
|
+
.select({
|
592
|
+
user: {
|
593
|
+
id: usersTable.id,
|
594
|
+
name: usersTable.name
|
595
|
+
},
|
596
|
+
customer: {
|
597
|
+
id: customerAlias.id,
|
598
|
+
name: customerAlias.name
|
599
|
+
}
|
600
|
+
})
|
601
|
+
.from(usersTable)
|
602
|
+
.leftJoin(customerAlias, eq(customerAlias.id, 11))
|
603
|
+
.where(eq(usersTable.id, 10));
|
604
|
+
|
605
|
+
t.deepEqual(result, [
|
606
|
+
{
|
607
|
+
user: { id: 10, name: 'Ivan' },
|
608
|
+
customer: { id: 11, name: 'Hans' }
|
609
|
+
}
|
610
|
+
]);
|
611
|
+
});
|
612
|
+
|
613
|
+
test.skip('full join with alias', async (t) => {
|
614
|
+
const { db } = t.context;
|
615
|
+
|
616
|
+
const pgTable = pgTableCreator((name) => `prefixed_${name}`);
|
617
|
+
|
618
|
+
const users = pgTable('users', {
|
619
|
+
id: serial('id').primaryKey(),
|
620
|
+
name: text('name').notNull()
|
621
|
+
});
|
622
|
+
|
623
|
+
await db.execute(sql`drop table if exists ${users}`);
|
624
|
+
await db.execute(sql`create table ${users} (id serial primary key, name text not null)`);
|
625
|
+
|
626
|
+
const customers = alias(users, 'customer');
|
627
|
+
|
628
|
+
await db.insert(users).values([
|
629
|
+
{ id: 10, name: 'Ivan' },
|
630
|
+
{ id: 11, name: 'Hans' }
|
631
|
+
]);
|
632
|
+
const result = await db.select().from(users).leftJoin(customers, eq(customers.id, 11)).where(eq(users.id, 10));
|
633
|
+
|
634
|
+
t.deepEqual(result, [
|
635
|
+
{
|
636
|
+
users: {
|
637
|
+
id: 10,
|
638
|
+
name: 'Ivan'
|
639
|
+
},
|
640
|
+
customer: {
|
641
|
+
id: 11,
|
642
|
+
name: 'Hans'
|
643
|
+
}
|
644
|
+
}
|
645
|
+
]);
|
646
|
+
|
647
|
+
await db.execute(sql`drop table ${users}`);
|
648
|
+
});
|
649
|
+
|
650
|
+
test.skip('select from alias', async (t) => {
|
651
|
+
const { db } = t.context;
|
652
|
+
|
653
|
+
const pgTable = pgTableCreator((name) => `prefixed_${name}`);
|
654
|
+
|
655
|
+
const users = pgTable('users', {
|
656
|
+
id: serial('id').primaryKey(),
|
657
|
+
name: text('name').notNull()
|
658
|
+
});
|
659
|
+
|
660
|
+
await db.execute(sql`drop table if exists ${users}`);
|
661
|
+
await db.execute(sql`create table ${users} (id serial primary key, name text not null)`);
|
662
|
+
|
663
|
+
const user = alias(users, 'user');
|
664
|
+
const customers = alias(users, 'customer');
|
665
|
+
|
666
|
+
await db.insert(users).values([
|
667
|
+
{ id: 10, name: 'Ivan' },
|
668
|
+
{ id: 11, name: 'Hans' }
|
669
|
+
]);
|
670
|
+
const result = await db.select().from(user).leftJoin(customers, eq(customers.id, 11)).where(eq(user.id, 10));
|
671
|
+
|
672
|
+
t.deepEqual(result, [
|
673
|
+
{
|
674
|
+
user: {
|
675
|
+
id: 10,
|
676
|
+
name: 'Ivan'
|
677
|
+
},
|
678
|
+
customer: {
|
679
|
+
id: 11,
|
680
|
+
name: 'Hans'
|
681
|
+
}
|
682
|
+
}
|
683
|
+
]);
|
684
|
+
|
685
|
+
await db.execute(sql`drop table ${users}`);
|
686
|
+
});
|
687
|
+
|
688
|
+
test.skip('insert with spaces', async (t) => {
|
689
|
+
const { db } = t.context;
|
690
|
+
|
691
|
+
await db.insert(usersTable).values({ name: sql`'Jo h n'` });
|
692
|
+
const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable);
|
693
|
+
|
694
|
+
t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]);
|
695
|
+
});
|
696
|
+
|
697
|
+
test.skip('prepared statement', async (t) => {
|
698
|
+
const { db } = t.context;
|
699
|
+
|
700
|
+
await db.insert(usersTable).values({ name: 'John' });
|
701
|
+
const statement = db
|
702
|
+
.select({
|
703
|
+
id: usersTable.id,
|
704
|
+
name: usersTable.name
|
705
|
+
})
|
706
|
+
.from(usersTable)
|
707
|
+
.prepare('statement1');
|
708
|
+
const result = await statement.execute();
|
709
|
+
|
710
|
+
t.deepEqual(result, [{ id: 1, name: 'John' }]);
|
711
|
+
});
|
712
|
+
|
713
|
+
test.skip('prepared statement reuse', async (t) => {
|
714
|
+
const { db } = t.context;
|
715
|
+
|
716
|
+
const stmt = db
|
717
|
+
.insert(usersTable)
|
718
|
+
.values({
|
719
|
+
verified: true,
|
720
|
+
name: placeholder('name')
|
721
|
+
})
|
722
|
+
.prepare('stmt2');
|
723
|
+
|
724
|
+
for (let i = 0; i < 10; i++) {
|
725
|
+
await stmt.execute({ name: `John ${i}` });
|
726
|
+
}
|
727
|
+
|
728
|
+
const result = await db
|
729
|
+
.select({
|
730
|
+
id: usersTable.id,
|
731
|
+
name: usersTable.name,
|
732
|
+
verified: usersTable.verified
|
733
|
+
})
|
734
|
+
.from(usersTable);
|
735
|
+
|
736
|
+
t.deepEqual(result, [
|
737
|
+
{ id: 1, name: 'John 0', verified: true },
|
738
|
+
{ id: 2, name: 'John 1', verified: true },
|
739
|
+
{ id: 3, name: 'John 2', verified: true },
|
740
|
+
{ id: 4, name: 'John 3', verified: true },
|
741
|
+
{ id: 5, name: 'John 4', verified: true },
|
742
|
+
{ id: 6, name: 'John 5', verified: true },
|
743
|
+
{ id: 7, name: 'John 6', verified: true },
|
744
|
+
{ id: 8, name: 'John 7', verified: true },
|
745
|
+
{ id: 9, name: 'John 8', verified: true },
|
746
|
+
{ id: 10, name: 'John 9', verified: true }
|
747
|
+
]);
|
748
|
+
});
|
749
|
+
|
750
|
+
test.skip('prepared statement with placeholder in .where', async (t) => {
|
751
|
+
const { db } = t.context;
|
752
|
+
|
753
|
+
await db.insert(usersTable).values({ name: 'John' });
|
754
|
+
const stmt = db
|
755
|
+
.select({
|
756
|
+
id: usersTable.id,
|
757
|
+
name: usersTable.name
|
758
|
+
})
|
759
|
+
.from(usersTable)
|
760
|
+
.where(eq(usersTable.id, placeholder('id')))
|
761
|
+
.prepare('stmt3');
|
762
|
+
const result = await stmt.execute({ id: 1 });
|
763
|
+
|
764
|
+
t.deepEqual(result, [{ id: 1, name: 'John' }]);
|
765
|
+
});
|
766
|
+
|
767
|
+
test.skip('prepared statement with placeholder in .limit', async (t) => {
|
768
|
+
const { db } = t.context;
|
769
|
+
|
770
|
+
await db.insert(usersTable).values({ name: 'John' });
|
771
|
+
const stmt = db
|
772
|
+
.select({
|
773
|
+
id: usersTable.id,
|
774
|
+
name: usersTable.name
|
775
|
+
})
|
776
|
+
.from(usersTable)
|
777
|
+
.where(eq(usersTable.id, placeholder('id')))
|
778
|
+
.limit(placeholder('limit'))
|
779
|
+
.prepare('stmt_limit');
|
780
|
+
|
781
|
+
const result = await stmt.execute({ id: 1, limit: 1 });
|
782
|
+
|
783
|
+
t.deepEqual(result, [{ id: 1, name: 'John' }]);
|
784
|
+
t.is(result.length, 1);
|
785
|
+
});
|
786
|
+
|
787
|
+
test.skip('prepared statement with placeholder in .offset', async (t) => {
|
788
|
+
const { db } = t.context;
|
789
|
+
|
790
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]);
|
791
|
+
const stmt = db
|
792
|
+
.select({
|
793
|
+
id: usersTable.id,
|
794
|
+
name: usersTable.name
|
795
|
+
})
|
796
|
+
.from(usersTable)
|
797
|
+
.offset(placeholder('offset'))
|
798
|
+
.prepare('stmt_offset');
|
799
|
+
|
800
|
+
const result = await stmt.execute({ offset: 1 });
|
801
|
+
|
802
|
+
t.deepEqual(result, [{ id: 2, name: 'John1' }]);
|
803
|
+
});
|
804
|
+
|
805
|
+
// TODO change tests to new structure
|
806
|
+
test.skip('migrator', async (t) => {
|
807
|
+
const { db } = t.context;
|
808
|
+
|
809
|
+
await db.execute(sql`drop table if exists all_columns`);
|
810
|
+
await db.execute(sql`drop table if exists users12`);
|
811
|
+
await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`);
|
812
|
+
|
813
|
+
await migrate(db, { migrationsFolder: './drizzle2/pg' });
|
814
|
+
|
815
|
+
await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' });
|
816
|
+
|
817
|
+
const result = await db.select().from(usersMigratorTable);
|
818
|
+
|
819
|
+
t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]);
|
820
|
+
|
821
|
+
await db.execute(sql`drop table all_columns`);
|
822
|
+
await db.execute(sql`drop table users12`);
|
823
|
+
await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`);
|
824
|
+
});
|
825
|
+
|
826
|
+
test.skip('insert via db.execute + select via db.execute', async (t) => {
|
827
|
+
const { db } = t.context;
|
828
|
+
|
829
|
+
await db.execute(sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`);
|
830
|
+
|
831
|
+
const result = await db.execute<{ id: number; name: string }>(sql`select id, name from "users"`);
|
832
|
+
t.deepEqual(result.rows, [{ id: 1, name: 'John' }]);
|
833
|
+
});
|
834
|
+
|
835
|
+
test.skip('insert via db.execute + returning', async (t) => {
|
836
|
+
const { db } = t.context;
|
837
|
+
|
838
|
+
const inserted = await db.execute<{ id: number; name: string }>(
|
839
|
+
sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'}) returning ${usersTable.id}, ${
|
840
|
+
usersTable.name
|
841
|
+
}`
|
842
|
+
);
|
843
|
+
t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]);
|
844
|
+
});
|
845
|
+
|
846
|
+
test.skip('insert via db.execute w/ query builder', async (t) => {
|
847
|
+
const { db } = t.context;
|
848
|
+
|
849
|
+
const inserted = await db.execute<Pick<typeof usersTable.$inferSelect, 'id' | 'name'>>(
|
850
|
+
db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name })
|
851
|
+
);
|
852
|
+
t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]);
|
853
|
+
});
|
854
|
+
|
855
|
+
test.skip('build query insert with onConflict do update', async (t) => {
|
856
|
+
const { db } = t.context;
|
857
|
+
|
858
|
+
const query = db
|
859
|
+
.insert(usersTable)
|
860
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
861
|
+
.onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } })
|
862
|
+
.toSQL();
|
863
|
+
|
864
|
+
t.deepEqual(query, {
|
865
|
+
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do update set "name" = $3',
|
866
|
+
params: ['John', '["foo","bar"]', 'John1']
|
867
|
+
});
|
868
|
+
});
|
869
|
+
|
870
|
+
test.skip('build query insert with onConflict do update / multiple columns', async (t) => {
|
871
|
+
const { db } = t.context;
|
872
|
+
|
873
|
+
const query = db
|
874
|
+
.insert(usersTable)
|
875
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
876
|
+
.onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } })
|
877
|
+
.toSQL();
|
878
|
+
|
879
|
+
t.deepEqual(query, {
|
880
|
+
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id","name") do update set "name" = $3',
|
881
|
+
params: ['John', '["foo","bar"]', 'John1']
|
882
|
+
});
|
883
|
+
});
|
884
|
+
|
885
|
+
test.skip('build query insert with onConflict do nothing', async (t) => {
|
886
|
+
const { db } = t.context;
|
887
|
+
|
888
|
+
const query = db
|
889
|
+
.insert(usersTable)
|
890
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
891
|
+
.onConflictDoNothing()
|
892
|
+
.toSQL();
|
893
|
+
|
894
|
+
t.deepEqual(query, {
|
895
|
+
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict do nothing',
|
896
|
+
params: ['John', '["foo","bar"]']
|
897
|
+
});
|
898
|
+
});
|
899
|
+
|
900
|
+
test.skip('build query insert with onConflict do nothing + target', async (t) => {
|
901
|
+
const { db } = t.context;
|
902
|
+
|
903
|
+
const query = db
|
904
|
+
.insert(usersTable)
|
905
|
+
.values({ name: 'John', jsonb: ['foo', 'bar'] })
|
906
|
+
.onConflictDoNothing({ target: usersTable.id })
|
907
|
+
.toSQL();
|
908
|
+
|
909
|
+
t.deepEqual(query, {
|
910
|
+
sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do nothing',
|
911
|
+
params: ['John', '["foo","bar"]']
|
912
|
+
});
|
913
|
+
});
|
914
|
+
|
915
|
+
test.skip('insert with onConflict do update', async (t) => {
|
916
|
+
const { db } = t.context;
|
917
|
+
|
918
|
+
await db.insert(usersTable).values({ name: 'John' });
|
919
|
+
|
920
|
+
await db
|
921
|
+
.insert(usersTable)
|
922
|
+
.values({ id: 1, name: 'John' })
|
923
|
+
.onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } });
|
924
|
+
|
925
|
+
const res = await db
|
926
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
927
|
+
.from(usersTable)
|
928
|
+
.where(eq(usersTable.id, 1));
|
929
|
+
|
930
|
+
t.deepEqual(res, [{ id: 1, name: 'John1' }]);
|
931
|
+
});
|
932
|
+
|
933
|
+
test.skip('insert with onConflict do nothing', async (t) => {
|
934
|
+
const { db } = t.context;
|
935
|
+
|
936
|
+
await db.insert(usersTable).values({ name: 'John' });
|
937
|
+
|
938
|
+
await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing();
|
939
|
+
|
940
|
+
const res = await db
|
941
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
942
|
+
.from(usersTable)
|
943
|
+
.where(eq(usersTable.id, 1));
|
944
|
+
|
945
|
+
t.deepEqual(res, [{ id: 1, name: 'John' }]);
|
946
|
+
});
|
947
|
+
|
948
|
+
test.skip('insert with onConflict do nothing + target', async (t) => {
|
949
|
+
const { db } = t.context;
|
950
|
+
|
951
|
+
await db.insert(usersTable).values({ name: 'John' });
|
952
|
+
|
953
|
+
await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing({ target: usersTable.id });
|
954
|
+
|
955
|
+
const res = await db
|
956
|
+
.select({ id: usersTable.id, name: usersTable.name })
|
957
|
+
.from(usersTable)
|
958
|
+
.where(eq(usersTable.id, 1));
|
959
|
+
|
960
|
+
t.deepEqual(res, [{ id: 1, name: 'John' }]);
|
961
|
+
});
|
962
|
+
|
963
|
+
test.skip('left join (flat object fields)', async (t) => {
|
964
|
+
const { db } = t.context;
|
965
|
+
|
966
|
+
const { id: cityId } = await db
|
967
|
+
.insert(citiesTable)
|
968
|
+
.values([{ name: 'Paris' }, { name: 'London' }])
|
969
|
+
.returning({ id: citiesTable.id })
|
970
|
+
.then((rows) => rows[0]!);
|
971
|
+
|
972
|
+
await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]);
|
973
|
+
|
974
|
+
const res = await db
|
975
|
+
.select({
|
976
|
+
userId: users2Table.id,
|
977
|
+
userName: users2Table.name,
|
978
|
+
cityId: citiesTable.id,
|
979
|
+
cityName: citiesTable.name
|
980
|
+
})
|
981
|
+
.from(users2Table)
|
982
|
+
.leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id));
|
983
|
+
|
984
|
+
t.deepEqual(res, [
|
985
|
+
{ userId: 1, userName: 'John', cityId, cityName: 'Paris' },
|
986
|
+
{ userId: 2, userName: 'Jane', cityId: null, cityName: null }
|
987
|
+
]);
|
988
|
+
});
|
989
|
+
|
990
|
+
test.skip('left join (grouped fields)', async (t) => {
|
991
|
+
const { db } = t.context;
|
992
|
+
|
993
|
+
const { id: cityId } = await db
|
994
|
+
.insert(citiesTable)
|
995
|
+
.values([{ name: 'Paris' }, { name: 'London' }])
|
996
|
+
.returning({ id: citiesTable.id })
|
997
|
+
.then((rows) => rows[0]!);
|
998
|
+
|
999
|
+
await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]);
|
1000
|
+
|
1001
|
+
const res = await db
|
1002
|
+
.select({
|
1003
|
+
id: users2Table.id,
|
1004
|
+
user: {
|
1005
|
+
name: users2Table.name,
|
1006
|
+
nameUpper: sql<string>`upper(${users2Table.name})`
|
1007
|
+
},
|
1008
|
+
city: {
|
1009
|
+
id: citiesTable.id,
|
1010
|
+
name: citiesTable.name,
|
1011
|
+
nameUpper: sql<string>`upper(${citiesTable.name})`
|
1012
|
+
}
|
1013
|
+
})
|
1014
|
+
.from(users2Table)
|
1015
|
+
.leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id));
|
1016
|
+
|
1017
|
+
t.deepEqual(res, [
|
1018
|
+
{
|
1019
|
+
id: 1,
|
1020
|
+
user: { name: 'John', nameUpper: 'JOHN' },
|
1021
|
+
city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }
|
1022
|
+
},
|
1023
|
+
{
|
1024
|
+
id: 2,
|
1025
|
+
user: { name: 'Jane', nameUpper: 'JANE' },
|
1026
|
+
city: null
|
1027
|
+
}
|
1028
|
+
]);
|
1029
|
+
});
|
1030
|
+
|
1031
|
+
test.skip('left join (all fields)', async (t) => {
|
1032
|
+
const { db } = t.context;
|
1033
|
+
|
1034
|
+
const { id: cityId } = await db
|
1035
|
+
.insert(citiesTable)
|
1036
|
+
.values([{ name: 'Paris' }, { name: 'London' }])
|
1037
|
+
.returning({ id: citiesTable.id })
|
1038
|
+
.then((rows) => rows[0]!);
|
1039
|
+
|
1040
|
+
await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]);
|
1041
|
+
|
1042
|
+
const res = await db.select().from(users2Table).leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id));
|
1043
|
+
|
1044
|
+
t.deepEqual(res, [
|
1045
|
+
{
|
1046
|
+
users2: {
|
1047
|
+
id: 1,
|
1048
|
+
name: 'John',
|
1049
|
+
cityId
|
1050
|
+
},
|
1051
|
+
cities: {
|
1052
|
+
id: cityId,
|
1053
|
+
name: 'Paris',
|
1054
|
+
state: null
|
1055
|
+
}
|
1056
|
+
},
|
1057
|
+
{
|
1058
|
+
users2: {
|
1059
|
+
id: 2,
|
1060
|
+
name: 'Jane',
|
1061
|
+
cityId: null
|
1062
|
+
},
|
1063
|
+
cities: null
|
1064
|
+
}
|
1065
|
+
]);
|
1066
|
+
});
|
1067
|
+
|
1068
|
+
test.skip('join subquery', async (t) => {
|
1069
|
+
const { db } = t.context;
|
1070
|
+
|
1071
|
+
await db
|
1072
|
+
.insert(courseCategoriesTable)
|
1073
|
+
.values([{ name: 'Category 1' }, { name: 'Category 2' }, { name: 'Category 3' }, { name: 'Category 4' }]);
|
1074
|
+
|
1075
|
+
await db.insert(coursesTable).values([
|
1076
|
+
{ name: 'Development', categoryId: 2 },
|
1077
|
+
{ name: 'IT & Software', categoryId: 3 },
|
1078
|
+
{ name: 'Marketing', categoryId: 4 },
|
1079
|
+
{ name: 'Design', categoryId: 1 }
|
1080
|
+
]);
|
1081
|
+
|
1082
|
+
const sq2 = db
|
1083
|
+
.select({
|
1084
|
+
categoryId: courseCategoriesTable.id,
|
1085
|
+
category: courseCategoriesTable.name,
|
1086
|
+
total: sql<number>`count(${courseCategoriesTable.id})`
|
1087
|
+
})
|
1088
|
+
.from(courseCategoriesTable)
|
1089
|
+
.groupBy(courseCategoriesTable.id, courseCategoriesTable.name)
|
1090
|
+
.as('sq2');
|
1091
|
+
|
1092
|
+
const res = await db
|
1093
|
+
.select({
|
1094
|
+
courseName: coursesTable.name,
|
1095
|
+
categoryId: sq2.categoryId
|
1096
|
+
})
|
1097
|
+
.from(coursesTable)
|
1098
|
+
.leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId))
|
1099
|
+
.orderBy(coursesTable.name);
|
1100
|
+
|
1101
|
+
t.deepEqual(res, [
|
1102
|
+
{ courseName: 'Design', categoryId: 1 },
|
1103
|
+
{ courseName: 'Development', categoryId: 2 },
|
1104
|
+
{ courseName: 'IT & Software', categoryId: 3 },
|
1105
|
+
{ courseName: 'Marketing', categoryId: 4 }
|
1106
|
+
]);
|
1107
|
+
});
|
1108
|
+
|
1109
|
+
test.skip('with ... select', async (t) => {
|
1110
|
+
const { db } = t.context;
|
1111
|
+
|
1112
|
+
await db.insert(orders).values([
|
1113
|
+
{ region: 'Europe', product: 'A', amount: 10, quantity: 1 },
|
1114
|
+
{ region: 'Europe', product: 'A', amount: 20, quantity: 2 },
|
1115
|
+
{ region: 'Europe', product: 'B', amount: 20, quantity: 2 },
|
1116
|
+
{ region: 'Europe', product: 'B', amount: 30, quantity: 3 },
|
1117
|
+
{ region: 'US', product: 'A', amount: 30, quantity: 3 },
|
1118
|
+
{ region: 'US', product: 'A', amount: 40, quantity: 4 },
|
1119
|
+
{ region: 'US', product: 'B', amount: 40, quantity: 4 },
|
1120
|
+
{ region: 'US', product: 'B', amount: 50, quantity: 5 }
|
1121
|
+
]);
|
1122
|
+
|
1123
|
+
const regionalSales = db.$with('regional_sales').as(
|
1124
|
+
db
|
1125
|
+
.select({
|
1126
|
+
region: orders.region,
|
1127
|
+
totalSales: sql<number>`sum(${orders.amount})`.as('total_sales')
|
1128
|
+
})
|
1129
|
+
.from(orders)
|
1130
|
+
.groupBy(orders.region)
|
1131
|
+
);
|
1132
|
+
|
1133
|
+
const topRegions = db.$with('top_regions').as(
|
1134
|
+
db
|
1135
|
+
.select({
|
1136
|
+
region: regionalSales.region
|
1137
|
+
})
|
1138
|
+
.from(regionalSales)
|
1139
|
+
.where(
|
1140
|
+
gt(regionalSales.totalSales, db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales))
|
1141
|
+
)
|
1142
|
+
);
|
1143
|
+
|
1144
|
+
const result = await db
|
1145
|
+
.with(regionalSales, topRegions)
|
1146
|
+
.select({
|
1147
|
+
region: orders.region,
|
1148
|
+
product: orders.product,
|
1149
|
+
productUnits: sql<number>`sum(${orders.quantity})::int`,
|
1150
|
+
productSales: sql<number>`sum(${orders.amount})::int`
|
1151
|
+
})
|
1152
|
+
.from(orders)
|
1153
|
+
.where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions)))
|
1154
|
+
.groupBy(orders.region, orders.product)
|
1155
|
+
.orderBy(orders.region, orders.product);
|
1156
|
+
|
1157
|
+
t.deepEqual(result, [
|
1158
|
+
{
|
1159
|
+
region: 'Europe',
|
1160
|
+
product: 'A',
|
1161
|
+
productUnits: 3,
|
1162
|
+
productSales: 30
|
1163
|
+
},
|
1164
|
+
{
|
1165
|
+
region: 'Europe',
|
1166
|
+
product: 'B',
|
1167
|
+
productUnits: 5,
|
1168
|
+
productSales: 50
|
1169
|
+
},
|
1170
|
+
{
|
1171
|
+
region: 'US',
|
1172
|
+
product: 'A',
|
1173
|
+
productUnits: 7,
|
1174
|
+
productSales: 70
|
1175
|
+
},
|
1176
|
+
{
|
1177
|
+
region: 'US',
|
1178
|
+
product: 'B',
|
1179
|
+
productUnits: 9,
|
1180
|
+
productSales: 90
|
1181
|
+
}
|
1182
|
+
]);
|
1183
|
+
});
|
1184
|
+
|
1185
|
+
test.skip('select from subquery sql', async (t) => {
|
1186
|
+
const { db } = t.context;
|
1187
|
+
|
1188
|
+
await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]);
|
1189
|
+
|
1190
|
+
const sq = db
|
1191
|
+
.select({ name: sql<string>`${users2Table.name} || ' modified'`.as('name') })
|
1192
|
+
.from(users2Table)
|
1193
|
+
.as('sq');
|
1194
|
+
|
1195
|
+
const res = await db.select({ name: sq.name }).from(sq);
|
1196
|
+
|
1197
|
+
t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]);
|
1198
|
+
});
|
1199
|
+
|
1200
|
+
test('select a field without joining its table', (t) => {
|
1201
|
+
const { db } = t.context;
|
1202
|
+
|
1203
|
+
t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query'));
|
1204
|
+
});
|
1205
|
+
|
1206
|
+
test('select all fields from subquery without alias', (t) => {
|
1207
|
+
const { db } = t.context;
|
1208
|
+
|
1209
|
+
const sq = db.$with('sq').as(db.select({ name: sql<string>`upper(${users2Table.name})` }).from(users2Table));
|
1210
|
+
|
1211
|
+
t.throws(() => db.select().from(sq).prepare('query'));
|
1212
|
+
});
|
1213
|
+
|
1214
|
+
test.skip('select count()', async (t) => {
|
1215
|
+
const { db } = t.context;
|
1216
|
+
|
1217
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]);
|
1218
|
+
|
1219
|
+
const res = await db.select({ count: sql`count(*)` }).from(usersTable);
|
1220
|
+
|
1221
|
+
t.deepEqual(res, [{ count: '2' }]);
|
1222
|
+
});
|
1223
|
+
|
1224
|
+
test.skip('select count w/ custom mapper', async (t) => {
|
1225
|
+
const { db } = t.context;
|
1226
|
+
|
1227
|
+
function count(value: PgColumn | SQLWrapper): SQL<number>;
|
1228
|
+
function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased<number>;
|
1229
|
+
function count(value: PgColumn | SQLWrapper, alias?: string): SQL<number> | SQL.Aliased<number> {
|
1230
|
+
const result = sql`count(${value})`.mapWith(Number);
|
1231
|
+
if (!alias) {
|
1232
|
+
return result;
|
1233
|
+
}
|
1234
|
+
return result.as(alias);
|
1235
|
+
}
|
1236
|
+
|
1237
|
+
await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]);
|
1238
|
+
|
1239
|
+
const res = await db.select({ count: count(sql`*`) }).from(usersTable);
|
1240
|
+
|
1241
|
+
t.deepEqual(res, [{ count: 2 }]);
|
1242
|
+
});
|
1243
|
+
|
1244
|
+
test.skip('network types', async (t) => {
|
1245
|
+
const { db } = t.context;
|
1246
|
+
|
1247
|
+
const value: typeof network.$inferSelect = {
|
1248
|
+
inet: '127.0.0.1',
|
1249
|
+
cidr: '192.168.100.128/25',
|
1250
|
+
macaddr: '08:00:2b:01:02:03',
|
1251
|
+
macaddr8: '08:00:2b:01:02:03:04:05'
|
1252
|
+
};
|
1253
|
+
|
1254
|
+
await db.insert(network).values(value);
|
1255
|
+
|
1256
|
+
const res = await db.select().from(network);
|
1257
|
+
|
1258
|
+
t.deepEqual(res, [value]);
|
1259
|
+
});
|
1260
|
+
|
1261
|
+
test.skip('array types', async (t) => {
|
1262
|
+
const { db } = t.context;
|
1263
|
+
|
1264
|
+
const values: (typeof salEmp.$inferSelect)[] = [
|
1265
|
+
{
|
1266
|
+
name: 'John',
|
1267
|
+
payByQuarter: [10000, 10000, 10000, 10000],
|
1268
|
+
schedule: [
|
1269
|
+
['meeting', 'lunch'],
|
1270
|
+
['training', 'presentation']
|
1271
|
+
]
|
1272
|
+
},
|
1273
|
+
{
|
1274
|
+
name: 'Carol',
|
1275
|
+
payByQuarter: [20000, 25000, 25000, 25000],
|
1276
|
+
schedule: [
|
1277
|
+
['breakfast', 'consulting'],
|
1278
|
+
['meeting', 'lunch']
|
1279
|
+
]
|
1280
|
+
}
|
1281
|
+
];
|
1282
|
+
|
1283
|
+
await db.insert(salEmp).values(values);
|
1284
|
+
|
1285
|
+
const res = await db.select().from(salEmp);
|
1286
|
+
|
1287
|
+
t.deepEqual(res, values);
|
1288
|
+
});
|
1289
|
+
|
1290
|
+
test('select for ...', (t) => {
|
1291
|
+
const { db } = t.context;
|
1292
|
+
|
1293
|
+
const query = db
|
1294
|
+
.select()
|
1295
|
+
.from(users2Table)
|
1296
|
+
.for('update')
|
1297
|
+
.for('no key update', { of: users2Table })
|
1298
|
+
.for('no key update', { of: users2Table, skipLocked: true })
|
1299
|
+
.for('share', { of: users2Table, noWait: true })
|
1300
|
+
.toSQL();
|
1301
|
+
|
1302
|
+
t.regex(
|
1303
|
+
query.sql,
|
1304
|
+
/ for update for no key update of "users2" for no key update of "users2" skip locked for share of "users2" no wait$/
|
1305
|
+
);
|
1306
|
+
});
|
1307
|
+
|
1308
|
+
test.skip('having', async (t) => {
|
1309
|
+
const { db } = t.context;
|
1310
|
+
|
1311
|
+
await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]);
|
1312
|
+
|
1313
|
+
await db.insert(users2Table).values([
|
1314
|
+
{ name: 'John', cityId: 1 },
|
1315
|
+
{ name: 'Jane', cityId: 1 },
|
1316
|
+
{
|
1317
|
+
name: 'Jack',
|
1318
|
+
cityId: 2
|
1319
|
+
}
|
1320
|
+
]);
|
1321
|
+
|
1322
|
+
const result = await db
|
1323
|
+
.select({
|
1324
|
+
id: citiesTable.id,
|
1325
|
+
name: sql<string>`upper(${citiesTable.name})`.as('upper_name'),
|
1326
|
+
usersCount: sql<number>`count(${users2Table.id})::int`.as('users_count')
|
1327
|
+
})
|
1328
|
+
.from(citiesTable)
|
1329
|
+
.leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id))
|
1330
|
+
.where(({ name }) => sql`length(${name}) >= 3`)
|
1331
|
+
.groupBy(citiesTable.id)
|
1332
|
+
.having(({ usersCount }) => sql`${usersCount} > 0`)
|
1333
|
+
.orderBy(({ name }) => name);
|
1334
|
+
|
1335
|
+
t.deepEqual(result, [
|
1336
|
+
{
|
1337
|
+
id: 1,
|
1338
|
+
name: 'LONDON',
|
1339
|
+
usersCount: 2
|
1340
|
+
},
|
1341
|
+
{
|
1342
|
+
id: 2,
|
1343
|
+
name: 'PARIS',
|
1344
|
+
usersCount: 1
|
1345
|
+
}
|
1346
|
+
]);
|
1347
|
+
});
|
1348
|
+
|
1349
|
+
test.skip('view', async (t) => {
|
1350
|
+
const { db } = t.context;
|
1351
|
+
|
1352
|
+
const newYorkers1 = pgView('new_yorkers').as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1)));
|
1353
|
+
|
1354
|
+
const newYorkers2 = pgView('new_yorkers', {
|
1355
|
+
id: serial('id').primaryKey(),
|
1356
|
+
name: text('name').notNull(),
|
1357
|
+
cityId: integer('city_id').notNull()
|
1358
|
+
}).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`);
|
1359
|
+
|
1360
|
+
const newYorkers3 = pgView('new_yorkers', {
|
1361
|
+
id: serial('id').primaryKey(),
|
1362
|
+
name: text('name').notNull(),
|
1363
|
+
cityId: integer('city_id').notNull()
|
1364
|
+
}).existing();
|
1365
|
+
|
1366
|
+
await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`);
|
1367
|
+
|
1368
|
+
await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]);
|
1369
|
+
|
1370
|
+
await db.insert(users2Table).values([
|
1371
|
+
{ name: 'John', cityId: 1 },
|
1372
|
+
{ name: 'Jane', cityId: 1 },
|
1373
|
+
{ name: 'Jack', cityId: 2 }
|
1374
|
+
]);
|
1375
|
+
|
1376
|
+
{
|
1377
|
+
const result = await db.select().from(newYorkers1);
|
1378
|
+
t.deepEqual(result, [
|
1379
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1380
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1381
|
+
]);
|
1382
|
+
}
|
1383
|
+
|
1384
|
+
{
|
1385
|
+
const result = await db.select().from(newYorkers2);
|
1386
|
+
t.deepEqual(result, [
|
1387
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1388
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1389
|
+
]);
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
{
|
1393
|
+
const result = await db.select().from(newYorkers3);
|
1394
|
+
t.deepEqual(result, [
|
1395
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1396
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1397
|
+
]);
|
1398
|
+
}
|
1399
|
+
|
1400
|
+
{
|
1401
|
+
const result = await db.select({ name: newYorkers1.name }).from(newYorkers1);
|
1402
|
+
t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]);
|
1403
|
+
}
|
1404
|
+
|
1405
|
+
await db.execute(sql`drop view ${newYorkers1}`);
|
1406
|
+
});
|
1407
|
+
|
1408
|
+
test.skip('materialized view', async (t) => {
|
1409
|
+
const { db } = t.context;
|
1410
|
+
|
1411
|
+
const newYorkers1 = pgMaterializedView('new_yorkers').as((qb) =>
|
1412
|
+
qb.select().from(users2Table).where(eq(users2Table.cityId, 1))
|
1413
|
+
);
|
1414
|
+
|
1415
|
+
const newYorkers2 = pgMaterializedView('new_yorkers', {
|
1416
|
+
id: serial('id').primaryKey(),
|
1417
|
+
name: text('name').notNull(),
|
1418
|
+
cityId: integer('city_id').notNull()
|
1419
|
+
}).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`);
|
1420
|
+
|
1421
|
+
const newYorkers3 = pgMaterializedView('new_yorkers', {
|
1422
|
+
id: serial('id').primaryKey(),
|
1423
|
+
name: text('name').notNull(),
|
1424
|
+
cityId: integer('city_id').notNull()
|
1425
|
+
}).existing();
|
1426
|
+
|
1427
|
+
await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`);
|
1428
|
+
|
1429
|
+
await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]);
|
1430
|
+
|
1431
|
+
await db.insert(users2Table).values([
|
1432
|
+
{ name: 'John', cityId: 1 },
|
1433
|
+
{ name: 'Jane', cityId: 1 },
|
1434
|
+
{ name: 'Jack', cityId: 2 }
|
1435
|
+
]);
|
1436
|
+
|
1437
|
+
{
|
1438
|
+
const result = await db.select().from(newYorkers1);
|
1439
|
+
t.deepEqual(result, []);
|
1440
|
+
}
|
1441
|
+
|
1442
|
+
await db.refreshMaterializedView(newYorkers1);
|
1443
|
+
|
1444
|
+
{
|
1445
|
+
const result = await db.select().from(newYorkers1);
|
1446
|
+
t.deepEqual(result, [
|
1447
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1448
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1449
|
+
]);
|
1450
|
+
}
|
1451
|
+
|
1452
|
+
{
|
1453
|
+
const result = await db.select().from(newYorkers2);
|
1454
|
+
t.deepEqual(result, [
|
1455
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1456
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1457
|
+
]);
|
1458
|
+
}
|
1459
|
+
|
1460
|
+
{
|
1461
|
+
const result = await db.select().from(newYorkers3);
|
1462
|
+
t.deepEqual(result, [
|
1463
|
+
{ id: 1, name: 'John', cityId: 1 },
|
1464
|
+
{ id: 2, name: 'Jane', cityId: 1 }
|
1465
|
+
]);
|
1466
|
+
}
|
1467
|
+
|
1468
|
+
{
|
1469
|
+
const result = await db.select({ name: newYorkers1.name }).from(newYorkers1);
|
1470
|
+
t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]);
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
await db.execute(sql`drop materialized view ${newYorkers1}`);
|
1474
|
+
});
|
1475
|
+
|
1476
|
+
// TODO: copy to SQLite and MySQL, add to docs
|
1477
|
+
test('select from raw sql', async (t) => {
|
1478
|
+
const { db } = t.context;
|
1479
|
+
|
1480
|
+
const result = await db
|
1481
|
+
.select({
|
1482
|
+
id: sql<number>`id`,
|
1483
|
+
name: sql<string>`name`
|
1484
|
+
})
|
1485
|
+
.from(sql`(select 1 as id, 'John' as name) as users`);
|
1486
|
+
|
1487
|
+
Expect<Equal<{ id: number; name: string }[], typeof result>>;
|
1488
|
+
|
1489
|
+
t.deepEqual(result, [{ id: 1, name: 'John' }]);
|
1490
|
+
});
|
1491
|
+
|
1492
|
+
test.skip('select from raw sql with joins', async (t) => {
|
1493
|
+
const { db } = t.context;
|
1494
|
+
|
1495
|
+
const result = await db
|
1496
|
+
.select({
|
1497
|
+
id: sql<number>`users.id`,
|
1498
|
+
name: sql<string>`users.name`,
|
1499
|
+
userCity: sql<string>`users.city`,
|
1500
|
+
cityName: sql<string>`cities.name`
|
1501
|
+
})
|
1502
|
+
.from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`)
|
1503
|
+
.leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`);
|
1504
|
+
|
1505
|
+
Expect<Equal<{ id: number; name: string; userCity: string; cityName: string }[], typeof result>>;
|
1506
|
+
|
1507
|
+
t.deepEqual(result, [{ id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }]);
|
1508
|
+
});
|
1509
|
+
|
1510
|
+
test.skip('join on aliased sql from select', async (t) => {
|
1511
|
+
const { db } = t.context;
|
1512
|
+
|
1513
|
+
const result = await db
|
1514
|
+
.select({
|
1515
|
+
userId: sql<number>`users.id`.as('userId'),
|
1516
|
+
name: sql<string>`users.name`,
|
1517
|
+
userCity: sql<string>`users.city`,
|
1518
|
+
cityId: sql<number>`cities.id`.as('cityId'),
|
1519
|
+
cityName: sql<string>`cities.name`
|
1520
|
+
})
|
1521
|
+
.from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`)
|
1522
|
+
.leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId));
|
1523
|
+
|
1524
|
+
Expect<Equal<{ userId: number; name: string; userCity: string; cityId: number; cityName: string }[], typeof result>>;
|
1525
|
+
|
1526
|
+
t.deepEqual(result, [{ userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }]);
|
1527
|
+
});
|
1528
|
+
|
1529
|
+
test.skip('join on aliased sql from with clause', async (t) => {
|
1530
|
+
const { db } = t.context;
|
1531
|
+
|
1532
|
+
const users = db.$with('users').as(
|
1533
|
+
db
|
1534
|
+
.select({
|
1535
|
+
id: sql<number>`id`.as('userId'),
|
1536
|
+
name: sql<string>`name`.as('userName'),
|
1537
|
+
city: sql<string>`city`.as('city')
|
1538
|
+
})
|
1539
|
+
.from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`)
|
1540
|
+
);
|
1541
|
+
|
1542
|
+
const cities = db.$with('cities').as(
|
1543
|
+
db
|
1544
|
+
.select({
|
1545
|
+
id: sql<number>`id`.as('cityId'),
|
1546
|
+
name: sql<string>`name`.as('cityName')
|
1547
|
+
})
|
1548
|
+
.from(sql`(select 1 as id, 'Paris' as name) as cities`)
|
1549
|
+
);
|
1550
|
+
|
1551
|
+
const result = await db
|
1552
|
+
.with(users, cities)
|
1553
|
+
.select({
|
1554
|
+
userId: users.id,
|
1555
|
+
name: users.name,
|
1556
|
+
userCity: users.city,
|
1557
|
+
cityId: cities.id,
|
1558
|
+
cityName: cities.name
|
1559
|
+
})
|
1560
|
+
.from(users)
|
1561
|
+
.leftJoin(cities, (cols) => eq(cols.cityId, cols.userId));
|
1562
|
+
|
1563
|
+
Expect<Equal<{ userId: number; name: string; userCity: string; cityId: number; cityName: string }[], typeof result>>;
|
1564
|
+
|
1565
|
+
t.deepEqual(result, [{ userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }]);
|
1566
|
+
});
|
1567
|
+
|
1568
|
+
test.skip('prefixed table', async (t) => {
|
1569
|
+
const { db } = t.context;
|
1570
|
+
|
1571
|
+
const pgTable = pgTableCreator((name) => `myprefix_${name}`);
|
1572
|
+
|
1573
|
+
const users = pgTable('test_prefixed_table_with_unique_name', {
|
1574
|
+
id: integer('id').primaryKey(),
|
1575
|
+
name: text('name').notNull()
|
1576
|
+
});
|
1577
|
+
|
1578
|
+
await db.execute(sql`drop table if exists ${users}`);
|
1579
|
+
|
1580
|
+
await db.execute(
|
1581
|
+
sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`
|
1582
|
+
);
|
1583
|
+
|
1584
|
+
await db.insert(users).values({ id: 1, name: 'John' });
|
1585
|
+
|
1586
|
+
const result = await db.select().from(users);
|
1587
|
+
|
1588
|
+
t.deepEqual(result, [{ id: 1, name: 'John' }]);
|
1589
|
+
|
1590
|
+
await db.execute(sql`drop table ${users}`);
|
1591
|
+
});
|
1592
|
+
|
1593
|
+
test.skip('select from enum', async (t) => {
|
1594
|
+
const { db } = t.context;
|
1595
|
+
|
1596
|
+
const muscleEnum = pgEnum('muscle', [
|
1597
|
+
'abdominals',
|
1598
|
+
'hamstrings',
|
1599
|
+
'adductors',
|
1600
|
+
'quadriceps',
|
1601
|
+
'biceps',
|
1602
|
+
'shoulders',
|
1603
|
+
'chest',
|
1604
|
+
'middle_back',
|
1605
|
+
'calves',
|
1606
|
+
'glutes',
|
1607
|
+
'lower_back',
|
1608
|
+
'lats',
|
1609
|
+
'triceps',
|
1610
|
+
'traps',
|
1611
|
+
'forearms',
|
1612
|
+
'neck',
|
1613
|
+
'abductors'
|
1614
|
+
]);
|
1615
|
+
|
1616
|
+
const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']);
|
1617
|
+
|
1618
|
+
const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']);
|
1619
|
+
|
1620
|
+
const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']);
|
1621
|
+
|
1622
|
+
const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']);
|
1623
|
+
|
1624
|
+
const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']);
|
1625
|
+
|
1626
|
+
const exercises = pgTable('exercises', {
|
1627
|
+
id: serial('id').primaryKey(),
|
1628
|
+
name: varchar('name').notNull(),
|
1629
|
+
force: forceEnum('force'),
|
1630
|
+
level: levelEnum('level'),
|
1631
|
+
mechanic: mechanicEnum('mechanic'),
|
1632
|
+
equipment: equipmentEnum('equipment'),
|
1633
|
+
instructions: text('instructions'),
|
1634
|
+
category: categoryEnum('category'),
|
1635
|
+
primaryMuscles: muscleEnum('primary_muscles').array(),
|
1636
|
+
secondaryMuscles: muscleEnum('secondary_muscles').array(),
|
1637
|
+
createdAt: timestamp('created_at')
|
1638
|
+
.notNull()
|
1639
|
+
.default(sql`now()`),
|
1640
|
+
updatedAt: timestamp('updated_at')
|
1641
|
+
.notNull()
|
1642
|
+
.default(sql`now()`)
|
1643
|
+
});
|
1644
|
+
|
1645
|
+
await db.execute(sql`drop table if exists ${exercises}`);
|
1646
|
+
await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`);
|
1647
|
+
await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`);
|
1648
|
+
await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`);
|
1649
|
+
await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`);
|
1650
|
+
await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`);
|
1651
|
+
await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`);
|
1652
|
+
|
1653
|
+
await db.execute(
|
1654
|
+
sql`create type ${name(
|
1655
|
+
muscleEnum.enumName
|
1656
|
+
)} as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`
|
1657
|
+
);
|
1658
|
+
await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`);
|
1659
|
+
await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`);
|
1660
|
+
await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`);
|
1661
|
+
await db.execute(
|
1662
|
+
sql`create type ${name(
|
1663
|
+
equipmentEnum.enumName
|
1664
|
+
)} as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`
|
1665
|
+
);
|
1666
|
+
await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`);
|
1667
|
+
await db.execute(sql`
|
1668
|
+
create table ${exercises} (
|
1669
|
+
id serial primary key,
|
1670
|
+
name varchar not null,
|
1671
|
+
force force,
|
1672
|
+
level level,
|
1673
|
+
mechanic mechanic,
|
1674
|
+
equipment equipment,
|
1675
|
+
instructions text,
|
1676
|
+
category category,
|
1677
|
+
primary_muscles muscle[],
|
1678
|
+
secondary_muscles muscle[],
|
1679
|
+
created_at timestamp not null default now(),
|
1680
|
+
updated_at timestamp not null default now()
|
1681
|
+
)
|
1682
|
+
`);
|
1683
|
+
|
1684
|
+
await db.insert(exercises).values({
|
1685
|
+
name: 'Bench Press',
|
1686
|
+
force: 'isotonic',
|
1687
|
+
level: 'beginner',
|
1688
|
+
mechanic: 'compound',
|
1689
|
+
equipment: 'barbell',
|
1690
|
+
instructions:
|
1691
|
+
'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.',
|
1692
|
+
category: 'upper_body',
|
1693
|
+
primaryMuscles: ['chest', 'triceps'],
|
1694
|
+
secondaryMuscles: ['shoulders', 'traps']
|
1695
|
+
});
|
1696
|
+
|
1697
|
+
const result = await db.select().from(exercises);
|
1698
|
+
|
1699
|
+
t.deepEqual(result, [
|
1700
|
+
{
|
1701
|
+
id: 1,
|
1702
|
+
name: 'Bench Press',
|
1703
|
+
force: 'isotonic',
|
1704
|
+
level: 'beginner',
|
1705
|
+
mechanic: 'compound',
|
1706
|
+
equipment: 'barbell',
|
1707
|
+
instructions:
|
1708
|
+
'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.',
|
1709
|
+
category: 'upper_body',
|
1710
|
+
primaryMuscles: ['chest', 'triceps'],
|
1711
|
+
secondaryMuscles: ['shoulders', 'traps'],
|
1712
|
+
createdAt: result[0]!.createdAt,
|
1713
|
+
updatedAt: result[0]!.updatedAt
|
1714
|
+
}
|
1715
|
+
]);
|
1716
|
+
|
1717
|
+
await db.execute(sql`drop table ${exercises}`);
|
1718
|
+
await db.execute(sql`drop type ${name(muscleEnum.enumName)}`);
|
1719
|
+
await db.execute(sql`drop type ${name(forceEnum.enumName)}`);
|
1720
|
+
await db.execute(sql`drop type ${name(levelEnum.enumName)}`);
|
1721
|
+
await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`);
|
1722
|
+
await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`);
|
1723
|
+
await db.execute(sql`drop type ${name(categoryEnum.enumName)}`);
|
1724
|
+
});
|
1725
|
+
|
1726
|
+
test('orderBy with aliased column', (t) => {
|
1727
|
+
const { db } = t.context;
|
1728
|
+
|
1729
|
+
const query = db
|
1730
|
+
.select({
|
1731
|
+
test: sql`something`.as('test')
|
1732
|
+
})
|
1733
|
+
.from(users2Table)
|
1734
|
+
.orderBy((fields) => fields.test)
|
1735
|
+
.toSQL();
|
1736
|
+
|
1737
|
+
t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"');
|
1738
|
+
});
|
1739
|
+
|
1740
|
+
test.skip('select from sql', async (t) => {
|
1741
|
+
const { db } = t.context;
|
1742
|
+
|
1743
|
+
const metricEntry = pgTable('metric_entry', {
|
1744
|
+
id: pgUuid('id').notNull(),
|
1745
|
+
createdAt: timestamp('created_at').notNull()
|
1746
|
+
});
|
1747
|
+
|
1748
|
+
await db.execute(sql`drop table if exists ${metricEntry}`);
|
1749
|
+
await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`);
|
1750
|
+
|
1751
|
+
const metricId = uuid();
|
1752
|
+
|
1753
|
+
const intervals = db.$with('intervals').as(
|
1754
|
+
db
|
1755
|
+
.select({
|
1756
|
+
startTime: sql<string>`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'),
|
1757
|
+
endTime: sql<string>`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time')
|
1758
|
+
})
|
1759
|
+
.from(sql`generate_series(0, 29, 1) as t(x)`)
|
1760
|
+
);
|
1761
|
+
|
1762
|
+
await t.notThrowsAsync(() =>
|
1763
|
+
db
|
1764
|
+
.with(intervals)
|
1765
|
+
.select({
|
1766
|
+
startTime: intervals.startTime,
|
1767
|
+
endTime: intervals.endTime,
|
1768
|
+
count: sql<number>`count(${metricEntry})`
|
1769
|
+
})
|
1770
|
+
.from(metricEntry)
|
1771
|
+
.rightJoin(
|
1772
|
+
intervals,
|
1773
|
+
and(
|
1774
|
+
eq(metricEntry.id, metricId),
|
1775
|
+
gte(metricEntry.createdAt, intervals.startTime),
|
1776
|
+
lt(metricEntry.createdAt, intervals.endTime)
|
1777
|
+
)
|
1778
|
+
)
|
1779
|
+
.groupBy(intervals.startTime, intervals.endTime)
|
1780
|
+
.orderBy(asc(intervals.startTime))
|
1781
|
+
);
|
1782
|
+
});
|
1783
|
+
|
1784
|
+
test.skip('timestamp timezone', async (t) => {
|
1785
|
+
const { db } = t.context;
|
1786
|
+
|
1787
|
+
const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', {
|
1788
|
+
id: serial('id').primaryKey(),
|
1789
|
+
name: text('name').notNull(),
|
1790
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
1791
|
+
updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow()
|
1792
|
+
});
|
1793
|
+
|
1794
|
+
await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`);
|
1795
|
+
|
1796
|
+
await db.execute(
|
1797
|
+
sql`
|
1798
|
+
create table users_test_with_and_without_timezone (
|
1799
|
+
id serial not null primary key,
|
1800
|
+
name text not null,
|
1801
|
+
created_at timestamptz not null default now(),
|
1802
|
+
updated_at timestamp not null default now()
|
1803
|
+
)
|
1804
|
+
`
|
1805
|
+
);
|
1806
|
+
|
1807
|
+
const date = new Date(Date.parse('2020-01-01T00:00:00+04:00'));
|
1808
|
+
|
1809
|
+
await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' });
|
1810
|
+
await db.insert(usersTableWithAndWithoutTimezone).values({
|
1811
|
+
name: 'Without default times',
|
1812
|
+
createdAt: date,
|
1813
|
+
updatedAt: date
|
1814
|
+
});
|
1815
|
+
const users = await db.select().from(usersTableWithAndWithoutTimezone);
|
1816
|
+
|
1817
|
+
// check that the timestamps are set correctly for default times
|
1818
|
+
t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000);
|
1819
|
+
t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000);
|
1820
|
+
|
1821
|
+
// check that the timestamps are set correctly for non default times
|
1822
|
+
t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000);
|
1823
|
+
t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000);
|
1824
|
+
});
|
1825
|
+
|
1826
|
+
test.skip('transaction', async (t) => {
|
1827
|
+
const { db } = t.context;
|
1828
|
+
|
1829
|
+
const users = pgTable('users_transactions', {
|
1830
|
+
id: serial('id').primaryKey(),
|
1831
|
+
balance: integer('balance').notNull()
|
1832
|
+
});
|
1833
|
+
const products = pgTable('products_transactions', {
|
1834
|
+
id: serial('id').primaryKey(),
|
1835
|
+
price: integer('price').notNull(),
|
1836
|
+
stock: integer('stock').notNull()
|
1837
|
+
});
|
1838
|
+
|
1839
|
+
await db.execute(sql`drop table if exists ${users}`);
|
1840
|
+
await db.execute(sql`drop table if exists ${products}`);
|
1841
|
+
|
1842
|
+
await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`);
|
1843
|
+
await db.execute(
|
1844
|
+
sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`
|
1845
|
+
);
|
1846
|
+
|
1847
|
+
const user = await db
|
1848
|
+
.insert(users)
|
1849
|
+
.values({ balance: 100 })
|
1850
|
+
.returning()
|
1851
|
+
.then((rows) => rows[0]!);
|
1852
|
+
const product = await db
|
1853
|
+
.insert(products)
|
1854
|
+
.values({ price: 10, stock: 10 })
|
1855
|
+
.returning()
|
1856
|
+
.then((rows) => rows[0]!);
|
1857
|
+
|
1858
|
+
await db.transaction(async (tx) => {
|
1859
|
+
await tx
|
1860
|
+
.update(users)
|
1861
|
+
.set({ balance: user.balance - product.price })
|
1862
|
+
.where(eq(users.id, user.id));
|
1863
|
+
await tx
|
1864
|
+
.update(products)
|
1865
|
+
.set({ stock: product.stock - 1 })
|
1866
|
+
.where(eq(products.id, product.id));
|
1867
|
+
});
|
1868
|
+
|
1869
|
+
const result = await db.select().from(users);
|
1870
|
+
|
1871
|
+
t.deepEqual(result, [{ id: 1, balance: 90 }]);
|
1872
|
+
|
1873
|
+
await db.execute(sql`drop table ${users}`);
|
1874
|
+
await db.execute(sql`drop table ${products}`);
|
1875
|
+
});
|
1876
|
+
|
1877
|
+
test.skip('transaction rollback', async (t) => {
|
1878
|
+
const { db } = t.context;
|
1879
|
+
|
1880
|
+
const users = pgTable('users_transactions_rollback', {
|
1881
|
+
id: serial('id').primaryKey(),
|
1882
|
+
balance: integer('balance').notNull()
|
1883
|
+
});
|
1884
|
+
|
1885
|
+
await db.execute(sql`drop table if exists ${users}`);
|
1886
|
+
|
1887
|
+
await db.execute(
|
1888
|
+
sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`
|
1889
|
+
);
|
1890
|
+
|
1891
|
+
await t.throwsAsync(
|
1892
|
+
async () =>
|
1893
|
+
await db.transaction(async (tx) => {
|
1894
|
+
await tx.insert(users).values({ balance: 100 });
|
1895
|
+
tx.rollback();
|
1896
|
+
}),
|
1897
|
+
new TransactionRollbackError()
|
1898
|
+
);
|
1899
|
+
|
1900
|
+
const result = await db.select().from(users);
|
1901
|
+
|
1902
|
+
t.deepEqual(result, []);
|
1903
|
+
|
1904
|
+
await db.execute(sql`drop table ${users}`);
|
1905
|
+
});
|
1906
|
+
|
1907
|
+
test.skip('nested transaction', async (t) => {
|
1908
|
+
const { db } = t.context;
|
1909
|
+
|
1910
|
+
const users = pgTable('users_nested_transactions', {
|
1911
|
+
id: serial('id').primaryKey(),
|
1912
|
+
balance: integer('balance').notNull()
|
1913
|
+
});
|
1914
|
+
|
1915
|
+
await db.execute(sql`drop table if exists ${users}`);
|
1916
|
+
|
1917
|
+
await db.execute(
|
1918
|
+
sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`
|
1919
|
+
);
|
1920
|
+
|
1921
|
+
await db.transaction(async (tx) => {
|
1922
|
+
await tx.insert(users).values({ balance: 100 });
|
1923
|
+
|
1924
|
+
await tx.transaction(async (tx) => {
|
1925
|
+
await tx.update(users).set({ balance: 200 });
|
1926
|
+
});
|
1927
|
+
});
|
1928
|
+
|
1929
|
+
const result = await db.select().from(users);
|
1930
|
+
|
1931
|
+
t.deepEqual(result, [{ id: 1, balance: 200 }]);
|
1932
|
+
|
1933
|
+
await db.execute(sql`drop table ${users}`);
|
1934
|
+
});
|
1935
|
+
|
1936
|
+
test.skip('nested transaction rollback', async (t) => {
|
1937
|
+
const { db } = t.context;
|
1938
|
+
|
1939
|
+
const users = pgTable('users_nested_transactions_rollback', {
|
1940
|
+
id: serial('id').primaryKey(),
|
1941
|
+
balance: integer('balance').notNull()
|
1942
|
+
});
|
1943
|
+
|
1944
|
+
await db.execute(sql`drop table if exists ${users}`);
|
1945
|
+
|
1946
|
+
await db.execute(
|
1947
|
+
sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`
|
1948
|
+
);
|
1949
|
+
|
1950
|
+
await db.transaction(async (tx) => {
|
1951
|
+
await tx.insert(users).values({ balance: 100 });
|
1952
|
+
|
1953
|
+
await t.throwsAsync(
|
1954
|
+
async () =>
|
1955
|
+
await tx.transaction(async (tx) => {
|
1956
|
+
await tx.update(users).set({ balance: 200 });
|
1957
|
+
tx.rollback();
|
1958
|
+
}),
|
1959
|
+
new TransactionRollbackError()
|
1960
|
+
);
|
1961
|
+
});
|
1962
|
+
|
1963
|
+
const result = await db.select().from(users);
|
1964
|
+
|
1965
|
+
t.deepEqual(result, [{ id: 1, balance: 100 }]);
|
1966
|
+
|
1967
|
+
await db.execute(sql`drop table ${users}`);
|
1968
|
+
});
|
1969
|
+
|
1970
|
+
test.skip('join subquery with join', async (t) => {
|
1971
|
+
const { db } = t.context;
|
1972
|
+
|
1973
|
+
const internalStaff = pgTable('internal_staff', {
|
1974
|
+
userId: integer('user_id').notNull()
|
1975
|
+
});
|
1976
|
+
|
1977
|
+
const customUser = pgTable('custom_user', {
|
1978
|
+
id: integer('id').notNull()
|
1979
|
+
});
|
1980
|
+
|
1981
|
+
const ticket = pgTable('ticket', {
|
1982
|
+
staffId: integer('staff_id').notNull()
|
1983
|
+
});
|
1984
|
+
|
1985
|
+
await db.execute(sql`drop table if exists ${internalStaff}`);
|
1986
|
+
await db.execute(sql`drop table if exists ${customUser}`);
|
1987
|
+
await db.execute(sql`drop table if exists ${ticket}`);
|
1988
|
+
|
1989
|
+
await db.execute(sql`create table internal_staff (user_id integer not null)`);
|
1990
|
+
await db.execute(sql`create table custom_user (id integer not null)`);
|
1991
|
+
await db.execute(sql`create table ticket (staff_id integer not null)`);
|
1992
|
+
|
1993
|
+
await db.insert(internalStaff).values({ userId: 1 });
|
1994
|
+
await db.insert(customUser).values({ id: 1 });
|
1995
|
+
await db.insert(ticket).values({ staffId: 1 });
|
1996
|
+
|
1997
|
+
const subq = db
|
1998
|
+
.select()
|
1999
|
+
.from(internalStaff)
|
2000
|
+
.leftJoin(customUser, eq(internalStaff.userId, customUser.id))
|
2001
|
+
.as('internal_staff');
|
2002
|
+
|
2003
|
+
const mainQuery = await db.select().from(ticket).leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId));
|
2004
|
+
|
2005
|
+
t.deepEqual(mainQuery, [
|
2006
|
+
{
|
2007
|
+
ticket: { staffId: 1 },
|
2008
|
+
internal_staff: {
|
2009
|
+
internal_staff: { userId: 1 },
|
2010
|
+
custom_user: { id: 1 }
|
2011
|
+
}
|
2012
|
+
}
|
2013
|
+
]);
|
2014
|
+
|
2015
|
+
await db.execute(sql`drop table ${internalStaff}`);
|
2016
|
+
await db.execute(sql`drop table ${customUser}`);
|
2017
|
+
await db.execute(sql`drop table ${ticket}`);
|
2018
|
+
});
|
2019
|
+
|
2020
|
+
test.skip('subquery with view', async (t) => {
|
2021
|
+
const { db } = t.context;
|
2022
|
+
|
2023
|
+
const users = pgTable('users_subquery_view', {
|
2024
|
+
id: serial('id').primaryKey(),
|
2025
|
+
name: text('name').notNull(),
|
2026
|
+
cityId: integer('city_id').notNull()
|
2027
|
+
});
|
2028
|
+
|
2029
|
+
const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1)));
|
2030
|
+
|
2031
|
+
await db.execute(sql`drop table if exists ${users}`);
|
2032
|
+
await db.execute(sql`drop view if exists ${newYorkers}`);
|
2033
|
+
|
2034
|
+
await db.execute(
|
2035
|
+
sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`
|
2036
|
+
);
|
2037
|
+
await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`);
|
2038
|
+
|
2039
|
+
await db.insert(users).values([
|
2040
|
+
{ name: 'John', cityId: 1 },
|
2041
|
+
{ name: 'Jane', cityId: 2 },
|
2042
|
+
{ name: 'Jack', cityId: 1 },
|
2043
|
+
{ name: 'Jill', cityId: 2 }
|
2044
|
+
]);
|
2045
|
+
|
2046
|
+
const sq = db.$with('sq').as(db.select().from(newYorkers));
|
2047
|
+
const result = await db.with(sq).select().from(sq);
|
2048
|
+
|
2049
|
+
t.deepEqual(result, [
|
2050
|
+
{ id: 1, name: 'John', cityId: 1 },
|
2051
|
+
{ id: 3, name: 'Jack', cityId: 1 }
|
2052
|
+
]);
|
2053
|
+
|
2054
|
+
await db.execute(sql`drop view ${newYorkers}`);
|
2055
|
+
await db.execute(sql`drop table ${users}`);
|
2056
|
+
});
|
2057
|
+
|
2058
|
+
test.skip('join view as subquery', async (t) => {
|
2059
|
+
const { db } = t.context;
|
2060
|
+
|
2061
|
+
const users = pgTable('users_join_view', {
|
2062
|
+
id: serial('id').primaryKey(),
|
2063
|
+
name: text('name').notNull(),
|
2064
|
+
cityId: integer('city_id').notNull()
|
2065
|
+
});
|
2066
|
+
|
2067
|
+
const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1)));
|
2068
|
+
|
2069
|
+
await db.execute(sql`drop table if exists ${users}`);
|
2070
|
+
await db.execute(sql`drop view if exists ${newYorkers}`);
|
2071
|
+
|
2072
|
+
await db.execute(
|
2073
|
+
sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`
|
2074
|
+
);
|
2075
|
+
await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`);
|
2076
|
+
|
2077
|
+
await db.insert(users).values([
|
2078
|
+
{ name: 'John', cityId: 1 },
|
2079
|
+
{ name: 'Jane', cityId: 2 },
|
2080
|
+
{ name: 'Jack', cityId: 1 },
|
2081
|
+
{ name: 'Jill', cityId: 2 }
|
2082
|
+
]);
|
2083
|
+
|
2084
|
+
const sq = db.select().from(newYorkers).as('new_yorkers_sq');
|
2085
|
+
|
2086
|
+
const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id));
|
2087
|
+
|
2088
|
+
t.deepEqual(result, [
|
2089
|
+
{
|
2090
|
+
users_join_view: { id: 1, name: 'John', cityId: 1 },
|
2091
|
+
new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }
|
2092
|
+
},
|
2093
|
+
{
|
2094
|
+
users_join_view: { id: 2, name: 'Jane', cityId: 2 },
|
2095
|
+
new_yorkers_sq: null
|
2096
|
+
},
|
2097
|
+
{
|
2098
|
+
users_join_view: { id: 3, name: 'Jack', cityId: 1 },
|
2099
|
+
new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }
|
2100
|
+
},
|
2101
|
+
{
|
2102
|
+
users_join_view: { id: 4, name: 'Jill', cityId: 2 },
|
2103
|
+
new_yorkers_sq: null
|
2104
|
+
}
|
2105
|
+
]);
|
2106
|
+
|
2107
|
+
await db.execute(sql`drop view ${newYorkers}`);
|
2108
|
+
await db.execute(sql`drop table ${users}`);
|
2109
|
+
});
|
2110
|
+
|
2111
|
+
test.skip('table selection with single table', async (t) => {
|
2112
|
+
const { db } = t.context;
|
2113
|
+
|
2114
|
+
const users = pgTable('users', {
|
2115
|
+
id: serial('id').primaryKey(),
|
2116
|
+
name: text('name').notNull(),
|
2117
|
+
cityId: integer('city_id').notNull()
|
2118
|
+
});
|
2119
|
+
|
2120
|
+
await db.execute(sql`drop table if exists ${users}`);
|
2121
|
+
|
2122
|
+
await db.execute(
|
2123
|
+
sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`
|
2124
|
+
);
|
2125
|
+
|
2126
|
+
await db.insert(users).values({ name: 'John', cityId: 1 });
|
2127
|
+
|
2128
|
+
const result = await db.select({ users }).from(users);
|
2129
|
+
|
2130
|
+
t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]);
|
2131
|
+
|
2132
|
+
await db.execute(sql`drop table ${users}`);
|
2133
|
+
});
|
2134
|
+
|
2135
|
+
test.skip('set null to jsonb field', async (t) => {
|
2136
|
+
const { db } = t.context;
|
2137
|
+
|
2138
|
+
const users = pgTable('users', {
|
2139
|
+
id: serial('id').primaryKey(),
|
2140
|
+
jsonb: jsonb('jsonb')
|
2141
|
+
});
|
2142
|
+
|
2143
|
+
await db.execute(sql`drop table if exists ${users}`);
|
2144
|
+
|
2145
|
+
await db.execute(sql`create table ${users} (id serial not null primary key, jsonb jsonb)`);
|
2146
|
+
|
2147
|
+
const result = await db.insert(users).values({ jsonb: null }).returning();
|
2148
|
+
|
2149
|
+
t.deepEqual(result, [{ id: 1, jsonb: null }]);
|
2150
|
+
|
2151
|
+
await db.execute(sql`drop table ${users}`);
|
2152
|
+
});
|
2153
|
+
|
2154
|
+
test.skip('insert undefined', async (t) => {
|
2155
|
+
const { db } = t.context;
|
2156
|
+
|
2157
|
+
const users = pgTable('users', {
|
2158
|
+
id: serial('id').primaryKey(),
|
2159
|
+
name: text('name')
|
2160
|
+
});
|
2161
|
+
|
2162
|
+
await db.execute(sql`drop table if exists ${users}`);
|
2163
|
+
|
2164
|
+
await db.execute(sql`create table ${users} (id serial not null primary key, name text)`);
|
2165
|
+
|
2166
|
+
await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined }));
|
2167
|
+
|
2168
|
+
await db.execute(sql`drop table ${users}`);
|
2169
|
+
});
|
2170
|
+
|
2171
|
+
test.skip('update undefined', async (t) => {
|
2172
|
+
const { db } = t.context;
|
2173
|
+
|
2174
|
+
const users = pgTable('users', {
|
2175
|
+
id: serial('id').primaryKey(),
|
2176
|
+
name: text('name')
|
2177
|
+
});
|
2178
|
+
|
2179
|
+
await db.execute(sql`drop table if exists ${users}`);
|
2180
|
+
|
2181
|
+
await db.execute(sql`create table ${users} (id serial not null primary key, name text)`);
|
2182
|
+
|
2183
|
+
await t.throwsAsync(async () => await db.update(users).set({ name: undefined }));
|
2184
|
+
await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined }));
|
2185
|
+
|
2186
|
+
await db.execute(sql`drop table ${users}`);
|
2187
|
+
});
|