From 1a896ea96666e0d232792c684340112735c4300e Mon Sep 17 00:00:00 2001 From: AndriiSherman Date: Sat, 6 Jul 2024 17:52:11 +0300 Subject: [PATCH] Update all tests --- drizzle-orm/src/relations.ts | 4 +- integration-tests/package.json | 32 +- .../tests/__old/better-sqlite.test.ts | 2110 -------- .../tests/__old/d1-batch.test.ts | 550 --- integration-tests/tests/__old/d1.test.ts | 1840 ------- .../tests/__old/libsql-batch.test.ts | 569 --- integration-tests/tests/__old/libsql.test.ts | 2800 ----------- .../tests/__old/mysql-proxy.test.ts | 2122 -------- .../tests/__old/mysql-schema.test.ts | 900 ---- .../tests/__old/mysql.custom.test.ts | 853 ---- .../tests/__old/mysql.prefixed.test.ts | 1780 ------- integration-tests/tests/__old/mysql.test.ts | 3001 ----------- .../tests/__old/neon-http-batch.test.ts | 556 --- .../tests/__old/neon-http.test.ts | 2708 ---------- .../tests/__old/pg-proxy.test.ts | 2937 ----------- .../tests/__old/pg-schema.test.ts | 994 ---- .../tests/__old/pg.custom.test.ts | 842 ---- integration-tests/tests/__old/pg.test.ts | 4368 ----------------- integration-tests/tests/__old/pglite.test.ts | 4072 --------------- .../tests/__old/postgres.js.test.ts | 2791 ----------- integration-tests/tests/__old/sql.js.test.ts | 1838 ------- .../tests/__old/sqlite-proxy-batch.test.ts | 702 --- .../tests/__old/sqlite-proxy.test.ts | 1114 ----- .../tests/__old/vercel-pg.test.ts | 2470 ---------- .../tests/__old/xata-http.test.ts | 2391 --------- .../{__old => }/awsdatapi.alltypes.test.ts | 1 + integration-tests/tests/imports.test.cjs | 55 - integration-tests/tests/imports.test.mjs | 47 - integration-tests/tests/mysql/mysql-common.ts | 4 +- .../tests/mysql/mysql-custom.test.ts | 2 +- .../tests/mysql/mysql-prefixed.test.ts | 2 +- integration-tests/tests/pg/neon-http.test.ts | 2 +- .../tests/pg/node-postgres.test.ts | 2 +- integration-tests/tests/pg/pg-custom.test.ts | 2 +- .../tests/pg/postgres-js.test.ts | 2 +- integration-tests/tests/pg/vercel-pg.test.ts | 2 +- integration-tests/tests/pg/xata-http.test.ts | 2 +- .../tests/sqlite/better-sqlite.test.ts | 1 - integration-tests/tests/sqlite/d1.test.ts | 6 +- .../tests/sqlite/libsql-batch.test.ts | 8 +- integration-tests/tests/sqlite/libsql.test.ts | 6 +- integration-tests/tests/sqlite/sql-js.test.ts | 1 - .../tests/sqlite/sqlite-common.ts | 7 +- .../tests/sqlite/sqlite-proxy-batch.test.ts | 4 +- .../tests/sqlite/sqlite-proxy.test.ts | 28 +- integration-tests/tests/version.test.ts | 2 +- integration-tests/vitest.config.ts | 27 +- 47 files changed, 57 insertions(+), 44500 deletions(-) delete mode 100644 integration-tests/tests/__old/better-sqlite.test.ts delete mode 100644 integration-tests/tests/__old/d1-batch.test.ts delete mode 100644 integration-tests/tests/__old/d1.test.ts delete mode 100644 integration-tests/tests/__old/libsql-batch.test.ts delete mode 100644 integration-tests/tests/__old/libsql.test.ts delete mode 100644 integration-tests/tests/__old/mysql-proxy.test.ts delete mode 100644 integration-tests/tests/__old/mysql-schema.test.ts delete mode 100644 integration-tests/tests/__old/mysql.custom.test.ts delete mode 100644 integration-tests/tests/__old/mysql.prefixed.test.ts delete mode 100644 integration-tests/tests/__old/mysql.test.ts delete mode 100644 integration-tests/tests/__old/neon-http-batch.test.ts delete mode 100644 integration-tests/tests/__old/neon-http.test.ts delete mode 100644 integration-tests/tests/__old/pg-proxy.test.ts delete mode 100644 integration-tests/tests/__old/pg-schema.test.ts delete mode 100644 integration-tests/tests/__old/pg.custom.test.ts delete mode 100644 integration-tests/tests/__old/pg.test.ts delete mode 100644 integration-tests/tests/__old/pglite.test.ts delete mode 100644 integration-tests/tests/__old/postgres.js.test.ts delete mode 100644 integration-tests/tests/__old/sql.js.test.ts delete mode 100644 integration-tests/tests/__old/sqlite-proxy-batch.test.ts delete mode 100644 integration-tests/tests/__old/sqlite-proxy.test.ts delete mode 100644 integration-tests/tests/__old/vercel-pg.test.ts delete mode 100644 integration-tests/tests/__old/xata-http.test.ts rename integration-tests/tests/{__old => }/awsdatapi.alltypes.test.ts (99%) delete mode 100644 integration-tests/tests/imports.test.cjs delete mode 100644 integration-tests/tests/imports.test.mjs diff --git a/drizzle-orm/src/relations.ts b/drizzle-orm/src/relations.ts index c0d45d7e3..99780897e 100644 --- a/drizzle-orm/src/relations.ts +++ b/drizzle-orm/src/relations.ts @@ -1,4 +1,4 @@ -import { type AnyTable, getTableUniqueName, type InferModelFromColumns, isTable, Table } from '~/table.ts'; +import { type AnyTable, getTableUniqueName, type InferModelFromColumns, Table } from '~/table.ts'; import { type AnyColumn, Column } from './column.ts'; import { entityKind, is } from './entity.ts'; import { PrimaryKeyBuilder } from './pg-core/primary-keys.ts'; @@ -429,7 +429,7 @@ export function extractTablesRelationalConfig< > = {}; const tablesConfig: TablesRelationalConfig = {}; for (const [key, value] of Object.entries(schema)) { - if (isTable(value)) { + if (is(value, Table)) { const dbName = getTableUniqueName(value); const bufferedRelations = relationsBuffer[dbName]; tableNamesMap[dbName] = key; diff --git a/integration-tests/package.json b/integration-tests/package.json index c58b461c1..43371ef53 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -5,39 +5,11 @@ "type": "module", "scripts": { "test:types": "tsc", - "test": "pnpm test:ava && pnpm test:esm && pnpm test:rqb", - "test:ava": "cross-env NODE_OPTIONS='--loader=ts-node/esm --no-warnings' ava tests --timeout=60s --serial", - "test:rqb": "vitest run", + "test": "pnpm test:esm && pnpm test:vitest", + "test:vitest": "vitest run", "test:esm": "node tests/imports.test.mjs && node tests/imports.test.cjs", "test:data-api": "sst shell vitest run tests/awsdatapi.test.ts" }, - "ava": { - "files": [ - "tests/**/*.test.{ts,cts,mts,js,cjs,mjs}", - "!tests/imports.test.mjs", - "!tests/imports.test.cjs", - "!tests/awsdatapi.alltypes.test.ts", - "!tests/awsdatapi.test.ts", - "!tests/planetscale-serverless/**/*.ts", - "!tests/bun/**/*", - "!tests/vercel-pg.test.ts", - "!tests/relational/**/*", - "!tests/libsql-batch.test.ts", - "!tests/xata-http.test.ts", - "!tests/d1-batch.test.ts", - "!tests/sqlite-proxy-batch.test.ts", - "!tests/neon-http-batch.test.ts", - "!tests/neon-http.test.ts", - "!tests/tidb-serverless.test.ts", - "!tests/replicas/**/*", - "!tests/imports/**/*", - "!tests/extensions/**/*", - "!tests/prisma/**/*" - ], - "extensions": { - "ts": "module" - } - }, "keywords": [], "author": "Drizzle Team", "license": "Apache-2.0", diff --git a/integration-tests/tests/__old/better-sqlite.test.ts b/integration-tests/tests/__old/better-sqlite.test.ts deleted file mode 100644 index 50214a470..000000000 --- a/integration-tests/tests/__old/better-sqlite.test.ts +++ /dev/null @@ -1,2110 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Database from 'better-sqlite3'; -import { - and, - asc, - eq, - type Equal, - exists, - gt, - inArray, - name, - placeholder, - sql, - TransactionRollbackError, -} from 'drizzle-orm'; -import { type BetterSQLite3Database, drizzle } from 'drizzle-orm/better-sqlite3'; -import { migrate } from 'drizzle-orm/better-sqlite3/migrator'; -import { - alias, - blob, - getTableConfig, - getViewConfig, - int, - integer, - primaryKey, - sqliteTable, - sqliteTableCreator, - sqliteView, - text, - unique, - uniqueKeyName, -} from 'drizzle-orm/sqlite-core'; -import { Expect, randomString } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - verified: integer('verified', { mode: 'boolean' }).notNull().default(false), - json: blob('json', { mode: 'json' }).$type(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`strftime('%s', 'now')`), -}); - -const users2Table = sqliteTable('users2', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const citiesTable = sqliteTable('cities', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const coursesTable = sqliteTable('courses', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = sqliteTable('course_categories', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = sqliteTable('orders', { - id: integer('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const usersMigratorTable = sqliteTable('users12', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const anotherUsersMigratorTable = sqliteTable('another_users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const pkExampleTable = sqliteTable('pk_example', { - id: integer('id').notNull(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => ({ - compositePk: primaryKey(table.id, table.name), -})); - -const bigIntExample = sqliteTable('big_int_example', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - bigInt: blob('big_int', { mode: 'bigint' }).notNull(), -}); - -interface Context { - db: BetterSQLite3Database; - client: Database.Database; -} - -const test = anyTest as TestFn; - -test.before((t) => { - const ctx = t.context; - const dbPath = process.env['SQLITE_DB_PATH'] ?? ':memory:'; - - ctx.client = new Database(dbPath); - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always((t) => { - const ctx = t.context; - ctx.client?.close(); -}); - -test.after.always((t) => { - const ctx = t.context; - ctx.client?.close(); -}); - -test.beforeEach((t) => { - const ctx = t.context; - - ctx.db.run(sql`drop table if exists ${usersTable}`); - ctx.db.run(sql`drop table if exists ${users2Table}`); - ctx.db.run(sql`drop table if exists ${citiesTable}`); - ctx.db.run(sql`drop table if exists ${coursesTable}`); - ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`); - ctx.db.run(sql`drop table if exists ${orders}`); - ctx.db.run(sql`drop table if exists ${bigIntExample}`); - ctx.db.run(sql`drop table if exists ${pkExampleTable}`); - - ctx.db.run(sql` - create table ${usersTable} ( - id integer primary key, - name text not null, - verified integer not null default 0, - json blob, - created_at integer not null default (strftime('%s', 'now')) - ) - `); - ctx.db.run(sql` - create table ${users2Table} ( - id integer primary key, - name text not null, - city_id integer references ${citiesTable}(${name(citiesTable.id.name)}) - ) - `); - ctx.db.run(sql` - create table ${citiesTable} ( - id integer primary key, - name text not null - ) - `); - ctx.db.run(sql` - create table ${courseCategoriesTable} ( - id integer primary key, - name text not null - ) - `); - ctx.db.run(sql` - create table ${coursesTable} ( - id integer primary key, - name text not null, - category_id integer references ${courseCategoriesTable}(${name(courseCategoriesTable.id.name)}) - ) - `); - ctx.db.run(sql` - create table ${orders} ( - id integer primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `); - ctx.db.run(sql` - create table ${pkExampleTable} ( - id integer not null, - name text not null, - email text not null, - primary key (id, name) - ) - `); - ctx.db.run(sql` - create table ${bigIntExample} ( - id integer primary key, - name text not null, - big_int blob not null - ) - `); -}); - -test.serial('table configs: unique third param', (t) => { - const cities1Table = sqliteTable('cities1', { - id: int('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: unique().on(t.name, t.state), - f1: unique('custom').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - t.assert(tableConfig.uniqueConstraints.length === 2); - - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); - t.assert( - tableConfig.uniqueConstraints[0]?.name - === uniqueKeyName(cities1Table, tableConfig.uniqueConstraints[0]?.columns?.map((column) => column.name) ?? []), - ); - - t.deepEqual(tableConfig.uniqueConstraints[1]?.columns.map((t) => t.name), ['name', 'state']); - t.assert(tableConfig.uniqueConstraints[1]?.name === 'custom'); -}); - -test.serial('table configs: unique in column', (t) => { - const cities1Table = sqliteTable('cities1', { - id: int('id').primaryKey(), - name: text('name').notNull().unique(), - state: text('state').unique('custom'), - field: text('field').unique(), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - t.assert(columnName?.isUnique); - t.assert(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name])); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - t.assert(columnState?.isUnique); - t.assert(columnState?.uniqueName === 'custom'); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - t.assert(columnField?.isUnique); - t.assert(columnField?.uniqueName === uniqueKeyName(cities1Table, [columnField!.name])); -}); - -test.serial('insert bigint values', (t) => { - const { db } = t.context; - - db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run(); - db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run(); - db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run(); - db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run(); - db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run(); - - const result = db.select().from(bigIntExample).all(); - t.deepEqual(result, [ - { id: 1, name: 'one', bigInt: BigInt('0') }, - { id: 2, name: 'two', bigInt: BigInt('127') }, - { id: 3, name: 'three', bigInt: BigInt('32767') }, - { id: 4, name: 'four', bigInt: BigInt('1234567890') }, - { id: 5, name: 'five', bigInt: BigInt('12345678900987654321') }, - ]); -}); - -test.serial('select all fields', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select().from(usersTable).all(); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select partial', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select({ name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', (t) => { - const { db } = t.context; - - const usersDistinctTable = sqliteTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${usersDistinctTable}`); - db.run(sql`create table ${usersDistinctTable} (id integer, name text)`); - - db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]).run(); - const users = db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ).all(); - - db.run(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', (t) => { - const { db } = t.context; - - const users = db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('insert returning sql + get()', (t) => { - const { db } = t.context; - - const users = db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).get(); - - t.deepEqual(users, { name: 'JOHN' }); -}); - -test.serial('delete returning sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update returning sql + get()', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).get(); - - t.deepEqual(users, { name: 'JANE' }); -}); - -test.serial('insert with auto increment', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'George' }, - { name: 'Austin' }, - ]).run(); - const result = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'George' }, - { id: 4, name: 'Austin' }, - ]); -}); - -test.serial('insert with default values', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert with overridden default values', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John', verified: true }).run(); - const result = db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('update with returning all fields', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning all fields + get()', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().get(); - - t.assert(users.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users.createdAt.getTime() - now) < 5000); - t.deepEqual(users, { id: 1, name: 'Jane', verified: false, json: null, createdAt: users.createdAt }); -}); - -test.serial('update with returning partial', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning all fields + get()', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().get(); - - t.assert(users!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, { id: 1, name: 'John', verified: false, json: null, createdAt: users!.createdAt }); -}); - -test.serial('delete with returning partial', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('delete with returning partial + get()', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).get(); - - t.deepEqual(users, { id: 1, name: 'John' }); -}); - -test.serial('insert + select', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - db.insert(usersTable).values({ name: 'Jane' }).run(); - const result2 = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result2, [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]); -}); - -test.serial('json insert', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John', json: ['foo', 'bar'] }).run(); - const result = db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', json: ['foo', 'bar'] }]); -}); - -test.serial('insert many', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]).run(); - const result = db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', (t) => { - const { db } = t.context; - - const result = db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }) - .all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('partial join with alias', (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)) - .all(); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('select from alias', (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: sql`'Jo h n'` }).run(); - const result = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const statement = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).prepare(); - const result = statement.all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - stmt.run({ name: `John ${i}` }); - } - - const result = db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = stmt.all({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('select with group by as field', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with exists', async (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const user = alias(usersTable, 'user'); - const result = db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select with group by as sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('migrator', (t) => { - const { db } = t.context; - - db.run(sql`drop table if exists another_users`); - db.run(sql`drop table if exists users12`); - db.run(sql`drop table if exists __drizzle_migrations`); - - migrate(db, { migrationsFolder: './drizzle2/sqlite' }); - - db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result = db.select().from(usersMigratorTable).all(); - - db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result2 = db.select().from(usersMigratorTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - t.deepEqual(result2, [{ id: 1, name: 'John', email: 'email' }]); - - db.run(sql`drop table another_users`); - db.run(sql`drop table users12`); - db.run(sql`drop table __drizzle_migrations`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - db.run(sql`drop table if exists another_users`); - db.run(sql`drop table if exists users12`); - db.run(sql`drop table if exists ${sql.identifier(customTable)}`); - - migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); - - // test if the custom migrations table was created - const res = db.all(sql`select * from ${sql.identifier(customTable)};`); - t.true(res.length > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - db.run(sql`drop table another_users`); - db.run(sql`drop table users12`); - db.run(sql`drop table ${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.run + select via db.all', (t) => { - const { db } = t.context; - - db.run(sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`); - - const result = db.all<{ id: number; name: string }>(sql`select id, name from "users"`); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.get', (t) => { - const { db } = t.context; - - const inserted = db.get<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name(usersTable.name.name) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.run + select via db.get', (t) => { - const { db } = t.context; - - db.run(sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`); - - const result = db.get<{ id: number; name: string }>( - sql`select ${usersTable.id}, ${usersTable.name} from ${usersTable}`, - ); - t.deepEqual(result, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.get w/ query builder', (t) => { - const { db } = t.context; - - const inserted = db.get( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('left join (flat object fields)', (t) => { - const { db } = t.context; - - const { id: cityId } = db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all()[0]!; - - db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', (t) => { - const { db } = t.context; - - const { id: cityId } = db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all()[0]!; - - db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', (t) => { - const { db } = t.context; - - const { id: cityId } = db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all()[0]!; - - db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)).all(); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', (t) => { - const { db } = t.context; - - db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]).run(); - - db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]).run(); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name) - .all(); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', (t) => { - const { db } = t.context; - - db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]).run(); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as int)`, - productSales: sql`cast(sum(${orders.amount}) as int)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product) - .all(); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('query check: insert single empty row', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (null, ?, null)', - params: ['Dan'], - }); -}); - -test.serial('query check: insert multiple empty rows', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (null, ?, null), (null, ?, null)', - params: ['Dan', 'Dan'], - }); -}); - -test.serial('Insert all defaults in 1 row', (t) => { - const { db } = t.context; - - const users = sqliteTable('empty_insert_single', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text default 'Dan', state text)`, - ); - - db.insert(users).values({}).run(); - - const res = db.select().from(users).all(); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', (t) => { - const { db } = t.context; - - const users = sqliteTable('empty_insert_multiple', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text default 'Dan', state text)`, - ); - - db.insert(users).values([{}, {}]).run(); - - const res = db.select().from(users).all(); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('select from subquery sql', (t) => { - const { db } = t.context; - - db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = db.select({ name: sq.name }).from(sq).all(); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const res = db.select({ count: sql`count(*)` }).from(usersTable).all(); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('having', (t) => { - const { db } = t.context; - - db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]).run(); - - db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - const result = db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name) - .all(); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', (t) => { - const { db } = t.context; - - const newYorkers1 = sqliteView('new_yorkers1') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = sqliteView('new_yorkers2', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = sqliteView('new_yorkers1', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - db.run(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - db.run(sql`create view ${newYorkers2} as ${getViewConfig(newYorkers2).query}`); - - db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]).run(); - - db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - { - const result = db.select().from(newYorkers1).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = db.select().from(newYorkers2).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = db.select().from(newYorkers3).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = db.select({ name: newYorkers1.name }).from(newYorkers1).all(); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - db.run(sql`drop view ${newYorkers1}`); - db.run(sql`drop view ${newYorkers2}`); -}); - -test.serial('insert null timestamp', (t) => { - const { db } = t.context; - - const test = sqliteTable('test', { - t: integer('t', { mode: 'timestamp' }), - }); - - db.run(sql`create table ${test} (t timestamp)`); - - db.insert(test).values({ t: null }).run(); - const res = db.select().from(test).all(); - t.deepEqual(res, [{ t: null }]); - - db.run(sql`drop table ${test}`); -}); - -test.serial('select from raw sql', (t) => { - const { db } = t.context; - - const result = db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`).all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', (t) => { - const { db } = t.context; - - const result = db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`) - .all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', (t) => { - const { db } = t.context; - - const result = db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', (t) => { - const { db } = t.context; - - const table = sqliteTableCreator((name) => `myprefix_${name}`); - - const users = table('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - db.insert(users).values({ id: 1, name: 'John' }).run(); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('transaction', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = sqliteTable('products_transactions', { - id: integer('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`drop table if exists ${products}`); - - db.run(sql`create table users_transactions (id integer not null primary key, balance integer not null)`); - db.run( - sql`create table products_transactions (id integer not null primary key, price integer not null, stock integer not null)`, - ); - - const user = db.insert(users).values({ balance: 100 }).returning().get(); - const product = db.insert(products).values({ price: 10, stock: 10 }).returning().get(); - - db.transaction((tx) => { - tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)).run(); - tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)).run(); - }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - db.run(sql`drop table ${users}`); - db.run(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table users_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - t.throws(() => - db.transaction((tx) => { - tx.insert(users).values({ balance: 100 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table users_nested_transactions (id integer not null primary key, balance integer not null)`, - ); - - db.transaction((tx) => { - tx.insert(users).values({ balance: 100 }).run(); - - tx.transaction((tx) => { - tx.update(users).set({ balance: 200 }).run(); - }); - }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table users_nested_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - db.transaction((tx) => { - tx.insert(users).values({ balance: 100 }).run(); - - t.throws(() => - tx.transaction((tx) => { - tx.update(users).set({ balance: 200 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', (t) => { - const { db } = t.context; - - const internalStaff = sqliteTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = sqliteTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = sqliteTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - db.run(sql`drop table if exists ${internalStaff}`); - db.run(sql`drop table if exists ${customUser}`); - db.run(sql`drop table if exists ${ticket}`); - - db.run(sql`create table internal_staff (user_id integer not null)`); - db.run(sql`create table custom_user (id integer not null)`); - db.run(sql`create table ticket (staff_id integer not null)`); - - db.insert(internalStaff).values({ userId: 1 }).run(); - db.insert(customUser).values({ id: 1 }).run(); - db.insert(ticket).values({ staffId: 1 }).run(); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)) - .all(); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - db.run(sql`drop table ${internalStaff}`); - db.run(sql`drop table ${customUser}`); - db.run(sql`drop table ${ticket}`); -}); - -test.serial('join view as subquery', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_join_view', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = sqliteView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`drop view if exists ${newYorkers}`); - - db.run( - sql`create table ${users} (id integer not null primary key, name text not null, city_id integer not null)`, - ); - db.run(sql`create view ${newYorkers} as ${getViewConfig(newYorkers).query}`); - - db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]).run(); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = db.select().from(users).leftJoin(sq, eq(users.id, sq.id)).all(); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - db.run(sql`drop view ${newYorkers}`); - db.run(sql`drop table ${users}`); -}); - -test.serial('insert with onConflict do nothing', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing() - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk', (t) => { - const { db } = t.context; - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing() - .run(); - - const res = db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do nothing using target', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }) - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk as target', (t) => { - const { db } = t.context; - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing({ target: [pkExampleTable.id, pkExampleTable.name] }) - .run(); - - const res = db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do update', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do update using composite pk', (t) => { - const { db } = t.context; - - db.insert(pkExampleTable).values({ id: 1, name: 'John', email: 'john@example.com' }).run(); - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .onConflictDoUpdate({ target: [pkExampleTable.id, pkExampleTable.name], set: { email: 'john1@example.com' } }) - .run(); - - const res = db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john1@example.com' }]); -}); - -test.serial('insert with onConflict do update where', (t) => { - const { db } = t.context; - - db - .insert(usersTable) - .values([{ id: 1, name: 'John', verified: false }]) - .run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John1', verified: true }) - .onConflictDoUpdate({ - target: usersTable.id, - set: { name: 'John1', verified: true }, - where: eq(usersTable.verified, false), - }) - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name, verified: usersTable.verified }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1', verified: true }]); -}); - -test.serial('insert undefined', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - t.notThrows(() => db.insert(users).values({ name: undefined }).run()); - - db.run(sql`drop table ${users}`); -}); - -test.serial('update undefined', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - t.throws(() => db.update(users).set({ name: undefined }).run()); - t.notThrows(() => db.update(users).set({ id: 1, name: undefined }).run()); - - db.run(sql`drop table ${users}`); -}); - -test.serial('async api - CRUD', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - await db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)); - - const res1 = await db.select().from(users); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - await db.delete(users).where(eq(users.id, 1)); - - const res2 = await db.select().from(users); - - t.deepEqual(res2, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + async execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + sync execute', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - insertStmt.execute().sync(); - - const selectStmt = db.select().from(users).prepare(); - const res = selectStmt.execute().sync(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - updateStmt.execute().sync(); - - const res1 = selectStmt.execute().sync(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - deleteStmt.execute().sync(); - - const res2 = selectStmt.execute().sync(); - - t.deepEqual(res2, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('select + .get() for empty result', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const res = db.select().from(users).where(eq(users.id, 1)).get(); - - t.is(res, undefined); - - db.run(sql`drop table ${users}`); -}); - -test.serial('text w/ json mode', (t) => { - const { db } = t.context; - - const test = sqliteTable('test', { - data: text('data', { mode: 'json' }).notNull(), - dataTyped: text('data_typed', { mode: 'json' }).$type<{ a: 1 }>().notNull(), - }); - - db.run(sql`drop table if exists ${test}`); - db.run(sql`create table ${test} (data text not null, data_typed text not null)`); - - db.insert(test).values({ data: { foo: 'bar' }, dataTyped: { a: 1 } }).run(); - - const res = db.select().from(test).get(); - - t.deepEqual(res, { data: { foo: 'bar' }, dataTyped: { a: 1 } }); - - db.run(sql`drop table ${test}`); -}); diff --git a/integration-tests/tests/__old/d1-batch.test.ts b/integration-tests/tests/__old/d1-batch.test.ts deleted file mode 100644 index 7abebbb51..000000000 --- a/integration-tests/tests/__old/d1-batch.test.ts +++ /dev/null @@ -1,550 +0,0 @@ -import 'dotenv/config'; -import { D1Database, D1DatabaseAPI } from '@miniflare/d1'; -import { createSQLiteDB } from '@miniflare/shared'; -import { eq, relations, sql } from 'drizzle-orm'; -import type { DrizzleD1Database } from 'drizzle-orm/d1'; -import { drizzle } from 'drizzle-orm/d1'; -import { type AnySQLiteColumn, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; -import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; - -const ENABLE_LOGGING = false; - -export const usersTable = sqliteTable('users', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - verified: integer('verified').notNull().default(0), - invitedBy: integer('invited_by').references((): AnySQLiteColumn => usersTable.id), -}); -export const usersConfig = relations(usersTable, ({ one, many }) => ({ - invitee: one(usersTable, { - fields: [usersTable.invitedBy], - references: [usersTable.id], - }), - usersToGroups: many(usersToGroupsTable), - posts: many(postsTable), -})); - -export const groupsTable = sqliteTable('groups', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - description: text('description'), -}); -export const groupsConfig = relations(groupsTable, ({ many }) => ({ - usersToGroups: many(usersToGroupsTable), -})); - -export const usersToGroupsTable = sqliteTable( - 'users_to_groups', - { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id', { mode: 'number' }).notNull().references( - () => usersTable.id, - ), - groupId: integer('group_id', { mode: 'number' }).notNull().references( - () => groupsTable.id, - ), - }, - (t) => ({ - pk: primaryKey(t.userId, t.groupId), - }), -); -export const usersToGroupsConfig = relations(usersToGroupsTable, ({ one }) => ({ - group: one(groupsTable, { - fields: [usersToGroupsTable.groupId], - references: [groupsTable.id], - }), - user: one(usersTable, { - fields: [usersToGroupsTable.userId], - references: [usersTable.id], - }), -})); - -export const postsTable = sqliteTable('posts', { - id: integer('id').primaryKey({ autoIncrement: true }), - content: text('content').notNull(), - ownerId: integer('owner_id', { mode: 'number' }).references( - () => usersTable.id, - ), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const postsConfig = relations(postsTable, ({ one, many }) => ({ - author: one(usersTable, { - fields: [postsTable.ownerId], - references: [usersTable.id], - }), - comments: many(commentsTable), -})); - -export const commentsTable = sqliteTable('comments', { - id: integer('id').primaryKey({ autoIncrement: true }), - content: text('content').notNull(), - creator: integer('creator', { mode: 'number' }).references( - () => usersTable.id, - ), - postId: integer('post_id', { mode: 'number' }).references(() => postsTable.id), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const commentsConfig = relations(commentsTable, ({ one, many }) => ({ - post: one(postsTable, { - fields: [commentsTable.postId], - references: [postsTable.id], - }), - author: one(usersTable, { - fields: [commentsTable.creator], - references: [usersTable.id], - }), - likes: many(commentLikesTable), -})); - -export const commentLikesTable = sqliteTable('comment_likes', { - id: integer('id').primaryKey({ autoIncrement: true }), - creator: integer('creator', { mode: 'number' }).references( - () => usersTable.id, - ), - commentId: integer('comment_id', { mode: 'number' }).references( - () => commentsTable.id, - ), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const commentLikesConfig = relations(commentLikesTable, ({ one }) => ({ - comment: one(commentsTable, { - fields: [commentLikesTable.commentId], - references: [commentsTable.id], - }), - author: one(usersTable, { - fields: [commentLikesTable.creator], - references: [usersTable.id], - }), -})); - -const schema = { - usersTable, - postsTable, - commentsTable, - usersToGroupsTable, - groupsTable, - commentLikesConfig, - commentsConfig, - postsConfig, - usersToGroupsConfig, - groupsConfig, - usersConfig, -}; - -let db: DrizzleD1Database; - -beforeAll(async () => { - const sqliteDb = await createSQLiteDB(':memory:'); - db = drizzle(new D1Database(new D1DatabaseAPI(sqliteDb)) as any, { schema, logger: ENABLE_LOGGING }); -}); - -beforeEach(async () => { - await db.run(sql`drop table if exists \`groups\``); - await db.run(sql`drop table if exists \`users\``); - await db.run(sql`drop table if exists \`users_to_groups\``); - await db.run(sql`drop table if exists \`posts\``); - await db.run(sql`drop table if exists \`comments\``); - await db.run(sql`drop table if exists \`comment_likes\``); - - await db.run( - sql` - CREATE TABLE \`users\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text NOT NULL, - \`verified\` integer DEFAULT 0 NOT NULL, - \`invited_by\` integer - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`groups\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text NOT NULL, - \`description\` text - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`users_to_groups\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`user_id\` integer NOT NULL, - \`group_id\` integer NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`posts\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`content\` text NOT NULL, - \`owner_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`comments\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`content\` text NOT NULL, - \`creator\` integer, - \`post_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`comment_likes\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`creator\` integer, - \`comment_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); -}); - -afterAll(async () => { - await db.run(sql`drop table if exists \`groups\``); - await db.run(sql`drop table if exists \`users\``); - await db.run(sql`drop table if exists \`users_to_groups\``); - await db.run(sql`drop table if exists \`posts\``); - await db.run(sql`drop table if exists \`comments\``); - await db.run(sql`drop table if exists \`comment_likes\``); -}); - -test('batch api example', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ - id: usersTable.id, - invitedBy: usersTable.invitedBy, - }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.select().from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - invitedBy: number | null; - }[], - D1Result, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - invitedBy: null, - }]); - - // expect(batchResponse[1]).toEqual({ - // results: [], - // success: true, - // meta: { - // duration: 0.027083873748779297, - // last_row_id: 2, - // changes: 1, - // served_by: 'miniflare.db', - // internal_stats: null, - // }, - // }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api only relational many -test('insert + findMany', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - D1Result, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - // expect(batchResponse[1]).toEqual({ columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api relational many + one -test('insert + findMany + findFirst', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.query.usersTable.findFirst({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - D1Result, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - // expect(batchResponse[1]).toEqual({ columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 1, name: 'John', verified: 0, invitedBy: null }, - ); -}); - -test('insert + db.all + db.get + db.values + db.run', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.run(sql`insert into users (id, name) values (2, 'Dan')`), - db.all(sql`select * from users`), - db.values(sql`select * from users`), - db.get(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - D1Result, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - unknown[][], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }, - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0], 'insert').toEqual([{ - id: 1, - }]); - - // expect(batchResponse[1]).toEqual({ columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2], 'all').toEqual([ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ]); - - expect(batchResponse[3], 'values').toEqual([[1, 'John', 0, null], [2, 'Dan', 0, null]]); - - expect(batchResponse[4], 'get').toEqual( - { id: 1, name: 'John', verified: 0, invited_by: null }, - ); -}); - -// batch api combined rqb + raw call -test('insert + findManyWith + db.all', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.all(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - D1Result, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - // expect(batchResponse[1]).toEqual({ columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ]); -}); - -// batch api for insert + update + select -test('insert + update + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)), - db.query.usersTable.findMany({}), - db.select().from(usersTable).where(eq(usersTable.id, 1)), - db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - D1Result, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - // expect(batchResponse[1]).toEqual({ columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 1n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[4]).toEqual([ - { id: 1, invitedBy: null }, - ]); -}); - -// batch api for insert + delete + select -test('insert + delete + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.delete(usersTable).where(eq(usersTable.id, 1)).returning({ id: usersTable.id, invitedBy: usersTable.invitedBy }), - db.query.usersTable.findFirst({ - columns: { - id: true, - invitedBy: true, - }, - }), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - D1Result, - { - id: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - // expect(batchResponse[1]).toEqual({ columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 2, invitedBy: null }, - ); -}); - -// * additionally -// batch for all libsql cases, just replace simple calls with batch calls -// batch for all rqb cases, just replace simple calls with batch calls diff --git a/integration-tests/tests/__old/d1.test.ts b/integration-tests/tests/__old/d1.test.ts deleted file mode 100644 index 6830e923e..000000000 --- a/integration-tests/tests/__old/d1.test.ts +++ /dev/null @@ -1,1840 +0,0 @@ -import { D1Database, D1DatabaseAPI } from '@miniflare/d1'; -import { createSQLiteDB } from '@miniflare/shared'; -import anyTest from 'ava'; -import type { TestFn } from 'ava'; -import { asc, eq, type Equal, gt, inArray, placeholder, sql, TransactionRollbackError } from 'drizzle-orm'; -import type { DrizzleD1Database } from 'drizzle-orm/d1'; -import { drizzle } from 'drizzle-orm/d1'; -import { migrate } from 'drizzle-orm/d1/migrator'; -import { - alias, - blob, - getViewConfig, - integer, - sqliteTable, - sqliteTableCreator, - sqliteView, - text, -} from 'drizzle-orm/sqlite-core'; -import { Expect } from '../utils.ts'; - -const usersTable = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - verified: integer('verified').notNull().default(0), - json: blob('json', { mode: 'json' }).$type(), - createdAt: integer('created_at', { mode: 'timestamp' }) - .notNull() - .default(sql`strftime('%s', 'now')`), -}); - -const users2Table = sqliteTable('users2', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const citiesTable = sqliteTable('cities', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const coursesTable = sqliteTable('courses', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = sqliteTable('course_categories', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = sqliteTable('orders', { - id: integer('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const usersMigratorTable = sqliteTable('users12', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const anotherUsersMigratorTable = sqliteTable('another_users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - d1: D1Database; - db: DrizzleD1Database; -} - -const test = anyTest as TestFn; - -test.before(async (t) => { - const ctx = t.context; - const sqliteDb = await createSQLiteDB(':memory:'); - const db = new D1Database(new D1DatabaseAPI(sqliteDb)); - ctx.d1 = db; - /** - * Casting the type to any due to the following type error - * - * Argument of type 'import("drizzle-orm/node_modules/.pnpm/@miniflare+d1@2.14.0/node_modules/@miniflare/d1/dist/src/index").D1Database' is not assignable to parameter of type 'D1Database'. - * The types returned by 'prepare(...).first(...)' are incompatible between these types. - * Type 'Promise' is not assignable to type 'Promise'. - * Type 'T | null' is not assignable to type 'T'. - * 'T' could be instantiated with an arbitrary type which could be unrelated to 'T | null' - */ - ctx.db = drizzle(db as any); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - - await ctx.db.run(sql`drop table if exists ${usersTable}`); - await ctx.db.run(sql`drop table if exists ${users2Table}`); - await ctx.db.run(sql`drop table if exists ${citiesTable}`); - await ctx.db.run(sql`drop table if exists ${coursesTable}`); - await ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`); - await ctx.db.run(sql`drop table if exists ${orders}`); - - await ctx.db.run(sql` - create table ${usersTable} ( - id integer primary key, - name text not null, - verified integer not null default 0, - json blob, - created_at integer not null default (strftime('%s', 'now')) - ) - `); - await ctx.db.run(sql` - create table ${users2Table} ( - id integer primary key, - name text not null, - city_id integer references ${citiesTable}(${sql.identifier(citiesTable.id.name)}) - ) - `); - await ctx.db.run(sql` - create table ${citiesTable} ( - id integer primary key, - name text not null - ) - `); - await ctx.db.run(sql` - create table ${courseCategoriesTable} ( - id integer primary key, - name text not null - ) - `); - await ctx.db.run(sql` - create table ${coursesTable} ( - id integer primary key, - name text not null, - category_id integer references ${courseCategoriesTable}(${ - sql.identifier( - courseCategoriesTable.id.name, - ) - }) - ) - `); - await ctx.db.run(sql` - create table ${orders} ( - id integer primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select().from(usersTable).all(); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(result, [ - { - id: 1, - name: 'John', - verified: 0, - json: null, - createdAt: result[0]!.createdAt, - }, - ]); -}); - -test.serial('select partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select({ name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable) - .all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('insert returning sql + get()', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).get(); - - t.deepEqual(users, { name: 'JOHN' }); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update returning sql + get()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).get(); - - t.deepEqual(users, { name: 'JANE' }); -}); - -test.serial('insert with auto increment', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'George' }, - { name: 'Austin' }, - ]).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'George' }, - { id: 4, name: 'Austin' }, - ]); -}); - -test.serial('insert with default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: 0, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: 1 }).run(); - const result = await db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: 1, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: 0, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning all fields + get()', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().get(); - - t.assert(users.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users.createdAt.getTime() - now) < 5000); - t.deepEqual(users, { id: 1, name: 'Jane', verified: 0, json: null, createdAt: users.createdAt }); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'John', verified: 0, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning all fields + get()', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().get(); - - t.assert(users!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, { id: 1, name: 'John', verified: 0, json: null, createdAt: users!.createdAt }); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('delete with returning partial + get()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).get(); - - t.deepEqual(users, { id: 1, name: 'John' }); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.insert(usersTable).values({ name: 'Jane' }).run(); - const result2 = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result2, [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', json: ['foo', 'bar'] }).run(); - /** - * TODO: Fix bug! - * The select below fails with - * SyntaxError { - * message: 'Unexpected non-whitespace character after JSON at position 2', - * } - */ - await t.throwsAsync( - db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - }).from(usersTable).all(), - ); - - // Uncomment when the above bug is fixed - // t.deepEqual(result, [{ id: 1, name: 'John', json: ['foo', 'bar'] }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: 1 }, - ]).run(); - - /** - * TODO: Fix bug! - * The select below fails with - * SyntaxError { - * message: 'Unexpected non-whitespace character after JSON at position 2', - * } - */ - await t.throwsAsync( - db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }).from(usersTable).all(), - ); - - // Uncomment when the above bug is fixed - // t.deepEqual(result, [ - // { id: 1, name: 'John', json: null, verified: 0 }, - // { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: 0 }, - // { id: 3, name: 'Jane', json: null, verified: 0 }, - // { id: 4, name: 'Austin', json: null, verified: 1 }, - // ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - /** - * TODO: Fix bug! - * The select below fails with - * SyntaxError { - * message: 'Unexpected non-whitespace character after JSON at position 2', - * } - */ - await t.throwsAsync( - db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: 1 }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }) - .all(), - ); - - // Uncomment when the above bug is fixed - // t.deepEqual(result, [ - // { id: 1, name: 'John', json: null, verified: 0 }, - // { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: 0 }, - // { id: 3, name: 'Jane', json: null, verified: 0 }, - // { id: 4, name: 'Austin', json: null, verified: 1 }, - // ]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)) - .all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)) - .all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)) - .all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const statement = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).prepare(); - const result = await statement.all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: 1, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.run({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: 1 }, - { id: 2, name: 'John 1', verified: 1 }, - { id: 3, name: 'John 2', verified: 1 }, - { id: 4, name: 'John 3', verified: 1 }, - { id: 5, name: 'John 4', verified: 1 }, - { id: 6, name: 'John 5', verified: 1 }, - { id: 7, name: 'John 6', verified: 1 }, - { id: 8, name: 'John 7', verified: 1 }, - { id: 9, name: 'John 8', verified: 1 }, - { id: 10, name: 'John 9', verified: 1 }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.all({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('migrator', async (t) => { - const { db } = t.context; - - await db.run(sql`drop table if exists another_users`); - await db.run(sql`drop table if exists users12`); - await db.run(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result = await db.select().from(usersMigratorTable).all(); - - await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result2 = await db.select().from(usersMigratorTable).all(); - - await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - t.deepEqual(result2, [{ id: 1, name: 'John', email: 'email' }]); - - await db.run(sql`drop table another_users`); - await db.run(sql`drop table users12`); - await db.run(sql`drop table __drizzle_migrations`); -}); - -test.serial('insert via db.run + select via db.all', async (t) => { - const { db } = t.context; - - await db.run(sql`insert into ${usersTable} (${sql.identifier(usersTable.name.name)}) values (${'John'})`); - - const result = await db.all<{ id: number; name: string }>(sql`select id, name from "users"`); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.get', async (t) => { - const { db } = t.context; - - const inserted = await db.get<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - sql.identifier(usersTable.name.name) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.run + select via db.get', async (t) => { - const { db } = t.context; - - await db.run(sql`insert into ${usersTable} (${sql.identifier(usersTable.name.name)}) values (${'John'})`); - - const result = await db.get<{ id: number; name: string }>( - sql`select ${usersTable.id}, ${usersTable.name} from ${usersTable}`, - ); - t.deepEqual(result, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.get w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.get( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const allCities = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all(); - const { id: cityId } = allCities[0]!; - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const allCities = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all(); - const { id: cityId } = allCities[0]!; - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const allCities = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all(); - const { id: cityId } = allCities[0]!; - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)).all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]).run(); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]).run(); - - const sq2 = await db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name) - .all(); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]).run(); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as int)`, - productSales: sql`cast(sum(${orders.amount}) as int)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product) - .all(); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq).all(); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', async (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', async (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable).all(); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]).run(); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name) - .all(); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = sqliteView('new_yorkers1') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = sqliteView('new_yorkers2', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = sqliteView('new_yorkers1', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.run(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - await db.run(sql`create view ${newYorkers2} as ${getViewConfig(newYorkers2).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]).run(); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - { - const result = await db.select().from(newYorkers1).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1).all(); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.run(sql`drop view ${newYorkers1}`); - await db.run(sql`drop view ${newYorkers2}`); -}); - -test.serial('insert null timestamp', async (t) => { - const { db } = t.context; - - const test = sqliteTable('test', { - t: integer('t', { mode: 'timestamp' }), - }); - - await db.run(sql`create table ${test} (t timestamp)`); - - await db.insert(test).values({ t: null }).run(); - const res = await db.select().from(test).all(); - t.deepEqual(res, [{ t: null }]); - - await db.run(sql`drop table ${test}`); -}); - -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`).all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`) - .all(); - - Expect>; - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const table = sqliteTableCreator((name) => `myprefix_${name}`); - - const users = table('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }).run(); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', async (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = sqliteTable('products_transactions', { - id: integer('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`drop table if exists ${products}`); - - await db.run(sql`create table users_transactions (id integer not null primary key, balance integer not null)`); - await db.run( - sql`create table products_transactions (id integer not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().get(); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().get(); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)).run(); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)).run(); - }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.run(sql`drop table ${users}`); - await db.run(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table users_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table users_nested_transactions (id integer not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }).run(); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }).run(); - }); - }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table users_nested_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }).run(); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = sqliteTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = sqliteTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = sqliteTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.run(sql`drop table if exists ${internalStaff}`); - await db.run(sql`drop table if exists ${customUser}`); - await db.run(sql`drop table if exists ${ticket}`); - - await db.run(sql`create table internal_staff (user_id integer not null)`); - await db.run(sql`create table custom_user (id integer not null)`); - await db.run(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }).run(); - await db.insert(customUser).values({ id: 1 }).run(); - await db.insert(ticket).values({ staffId: 1 }).run(); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)) - .all(); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.run(sql`drop table ${internalStaff}`); - await db.run(sql`drop table ${customUser}`); - await db.run(sql`drop table ${ticket}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_join_view', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = sqliteView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`drop view if exists ${newYorkers}`); - - await db.run( - sql`create table ${users} (id integer not null primary key, name text not null, city_id integer not null)`, - ); - await db.run(sql`create view ${newYorkers} as ${getViewConfig(newYorkers).query}`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]).run(); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)).all(); - - /** - * TODO: Fix Bug! The objects should be equal - * - * See #528 for more details. - * Tldr the D1 driver does not execute joins successfully - */ - t.notDeepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.run(sql`drop view ${newYorkers}`); - await db.run(sql`drop table ${users}`); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing() - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do update where', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([{ id: 1, name: 'John', verified: 0 }]) - .run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John1', verified: 0 }) - .onConflictDoUpdate({ - target: usersTable.id, - set: { name: 'John1', verified: 1 }, - where: eq(usersTable.verified, 0), - }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name, verified: usersTable.verified }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1', verified: 1 }]); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined }).run()); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await t.throwsAsync(async () => db.update(users).set({ name: undefined }).run()); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined }).run()); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - CRUD', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - await db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)); - - const res1 = await db.select().from(users); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - await db.delete(users).where(eq(users.id, 1)); - - const res2 = await db.select().from(users); - - t.deepEqual(res2, []); -}); - -test.serial('async api - insert + select w/ prepare + async execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); -}); - -test.serial('async api - insert + select w/ prepare + sync execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); -}); diff --git a/integration-tests/tests/__old/libsql-batch.test.ts b/integration-tests/tests/__old/libsql-batch.test.ts deleted file mode 100644 index 4b1883804..000000000 --- a/integration-tests/tests/__old/libsql-batch.test.ts +++ /dev/null @@ -1,569 +0,0 @@ -import 'dotenv/config'; -import type { Client, ResultSet } from '@libsql/client'; -import { createClient } from '@libsql/client'; -import { eq, relations, sql } from 'drizzle-orm'; -import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'; -import { type AnySQLiteColumn, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; -import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; - -const ENABLE_LOGGING = false; - -export const usersTable = sqliteTable('users', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - verified: integer('verified').notNull().default(0), - invitedBy: integer('invited_by').references((): AnySQLiteColumn => usersTable.id), -}); -export const usersConfig = relations(usersTable, ({ one, many }) => ({ - invitee: one(usersTable, { - fields: [usersTable.invitedBy], - references: [usersTable.id], - }), - usersToGroups: many(usersToGroupsTable), - posts: many(postsTable), -})); - -export const groupsTable = sqliteTable('groups', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - description: text('description'), -}); -export const groupsConfig = relations(groupsTable, ({ many }) => ({ - usersToGroups: many(usersToGroupsTable), -})); - -export const usersToGroupsTable = sqliteTable( - 'users_to_groups', - { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id', { mode: 'number' }).notNull().references( - () => usersTable.id, - ), - groupId: integer('group_id', { mode: 'number' }).notNull().references( - () => groupsTable.id, - ), - }, - (t) => ({ - pk: primaryKey(t.userId, t.groupId), - }), -); -export const usersToGroupsConfig = relations(usersToGroupsTable, ({ one }) => ({ - group: one(groupsTable, { - fields: [usersToGroupsTable.groupId], - references: [groupsTable.id], - }), - user: one(usersTable, { - fields: [usersToGroupsTable.userId], - references: [usersTable.id], - }), -})); - -export const postsTable = sqliteTable('posts', { - id: integer('id').primaryKey({ autoIncrement: true }), - content: text('content').notNull(), - ownerId: integer('owner_id', { mode: 'number' }).references( - () => usersTable.id, - ), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const postsConfig = relations(postsTable, ({ one, many }) => ({ - author: one(usersTable, { - fields: [postsTable.ownerId], - references: [usersTable.id], - }), - comments: many(commentsTable), -})); - -export const commentsTable = sqliteTable('comments', { - id: integer('id').primaryKey({ autoIncrement: true }), - content: text('content').notNull(), - creator: integer('creator', { mode: 'number' }).references( - () => usersTable.id, - ), - postId: integer('post_id', { mode: 'number' }).references(() => postsTable.id), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const commentsConfig = relations(commentsTable, ({ one, many }) => ({ - post: one(postsTable, { - fields: [commentsTable.postId], - references: [postsTable.id], - }), - author: one(usersTable, { - fields: [commentsTable.creator], - references: [usersTable.id], - }), - likes: many(commentLikesTable), -})); - -export const commentLikesTable = sqliteTable('comment_likes', { - id: integer('id').primaryKey({ autoIncrement: true }), - creator: integer('creator', { mode: 'number' }).references( - () => usersTable.id, - ), - commentId: integer('comment_id', { mode: 'number' }).references( - () => commentsTable.id, - ), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const commentLikesConfig = relations(commentLikesTable, ({ one }) => ({ - comment: one(commentsTable, { - fields: [commentLikesTable.commentId], - references: [commentsTable.id], - }), - author: one(usersTable, { - fields: [commentLikesTable.creator], - references: [usersTable.id], - }), -})); - -const schema = { - usersTable, - postsTable, - commentsTable, - usersToGroupsTable, - groupsTable, - commentLikesConfig, - commentsConfig, - postsConfig, - usersToGroupsConfig, - groupsConfig, - usersConfig, -}; - -let db: LibSQLDatabase; -let client: Client; - -beforeAll(async () => { - const url = process.env['LIBSQL_URL']; - const authToken = process.env['LIBSQL_AUTH_TOKEN']; - if (!url) { - throw new Error('LIBSQL_URL is not set'); - } - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - client = createClient({ url, authToken }); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to libsql'); - throw lastError; - } - - db = drizzle(client, { schema, logger: ENABLE_LOGGING }); -}); - -beforeEach(async () => { - await db.run(sql`drop table if exists \`groups\``); - await db.run(sql`drop table if exists \`users\``); - await db.run(sql`drop table if exists \`users_to_groups\``); - await db.run(sql`drop table if exists \`posts\``); - await db.run(sql`drop table if exists \`comments\``); - await db.run(sql`drop table if exists \`comment_likes\``); - - await db.run( - sql` - CREATE TABLE \`users\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text NOT NULL, - \`verified\` integer DEFAULT 0 NOT NULL, - \`invited_by\` integer - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`groups\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text NOT NULL, - \`description\` text - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`users_to_groups\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`user_id\` integer NOT NULL, - \`group_id\` integer NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`posts\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`content\` text NOT NULL, - \`owner_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`comments\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`content\` text NOT NULL, - \`creator\` integer, - \`post_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`comment_likes\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`creator\` integer, - \`comment_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); -}); - -afterAll(async () => { - await db.run(sql`drop table if exists \`groups\``); - await db.run(sql`drop table if exists \`users\``); - await db.run(sql`drop table if exists \`users_to_groups\``); - await db.run(sql`drop table if exists \`posts\``); - await db.run(sql`drop table if exists \`comments\``); - await db.run(sql`drop table if exists \`comment_likes\``); - - client.close(); -}); - -test('batch api example', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ - id: usersTable.id, - invitedBy: usersTable.invitedBy, - }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.select().from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - invitedBy: number | null; - }[], - ResultSet, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - invitedBy: null, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api only relational many -test('insert + findMany', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - ResultSet, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api relational many + one -test('insert + findMany + findFirst', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.query.usersTable.findFirst({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - ResultSet, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 1, name: 'John', verified: 0, invitedBy: null }, - ); -}); - -test('insert + db.all + db.get + db.values + db.run', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.run(sql`insert into users (id, name) values (2, 'Dan')`), - db.all(sql`select * from users`), - db.values(sql`select * from users`), - db.get(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - ResultSet, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - unknown[][], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }, - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ]); - - expect(batchResponse[3].map((row) => Array.prototype.slice.call(row))).toEqual([ - [1, 'John', 0, null], - [2, 'Dan', 0, null], - ]); - - expect(batchResponse[4]).toEqual( - { id: 1, name: 'John', verified: 0, invited_by: null }, - ); -}); - -// batch api combined rqb + raw call -test('insert + findManyWith + db.all', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.all(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - ResultSet, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ]); -}); - -// batch api for insert + update + select -test('insert + update + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)), - db.query.usersTable.findMany({}), - db.select().from(usersTable).where(eq(usersTable.id, 1)), - db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - ResultSet, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 1n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[4]).toEqual([ - { id: 1, invitedBy: null }, - ]); -}); - -// batch api for insert + delete + select -test('insert + delete + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.delete(usersTable).where(eq(usersTable.id, 1)).returning({ id: usersTable.id, invitedBy: usersTable.invitedBy }), - db.query.usersTable.findFirst({ - columns: { - id: true, - invitedBy: true, - }, - }), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - ResultSet, - { - id: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ columnTypes: [], columns: [], rows: [], rowsAffected: 1, lastInsertRowid: 2n }); - - expect(batchResponse[2]).toEqual([ - { id: 1, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 2, invitedBy: null }, - ); -}); - -// * additionally -// batch for all libsql cases, just replace simple calls with batch calls -// batch for all rqb cases, just replace simple calls with batch calls diff --git a/integration-tests/tests/__old/libsql.test.ts b/integration-tests/tests/__old/libsql.test.ts deleted file mode 100644 index 84f75258b..000000000 --- a/integration-tests/tests/__old/libsql.test.ts +++ /dev/null @@ -1,2800 +0,0 @@ -import 'dotenv/config'; - -import { type Client, createClient } from '@libsql/client'; -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import { - and, - asc, - avg, - avgDistinct, - count, - countDistinct, - eq, - exists, - getTableColumns, - gt, - gte, - inArray, - type InferModel, - lt, - max, - min, - Name, - name, - placeholder, - sql, - sum, - sumDistinct, - TransactionRollbackError, -} from 'drizzle-orm'; -import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'; -import { migrate } from 'drizzle-orm/libsql/migrator'; -import { - alias, - blob, - except, - foreignKey, - getTableConfig, - getViewConfig, - int, - integer, - intersect, - numeric, - primaryKey, - sqliteTable, - sqliteTableCreator, - sqliteView, - text, - union, - unionAll, -} from 'drizzle-orm/sqlite-core'; -import { type Equal, Expect, randomString } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -interface Context { - client: Client; - db: LibSQLDatabase; -} - -const test = anyTest as TestFn; - -const usersTable = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - verified: integer('verified', { mode: 'boolean' }).notNull().default(false), - json: blob('json', { mode: 'json' }).$type(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`strftime('%s', 'now')`), -}); - -const usersOnUpdate = sqliteTable('users_on_update', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), - updatedAt: integer('updated_at', { mode: 'timestamp_ms' }).$onUpdate(() => new Date()), - alwaysNull: text('always_null').$type().$onUpdate(() => null), - // uppercaseName: text('uppercase_name').$onUpdateFn(() => - // sql`upper(s.name)` - // ), This doesn't seem to be supported in sqlite -}); - -const users2Table = sqliteTable('users2', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const citiesTable = sqliteTable('cities', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const coursesTable = sqliteTable('courses', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = sqliteTable('course_categories', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = sqliteTable('orders', { - id: integer('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const usersMigratorTable = sqliteTable('users12', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const anotherUsersMigratorTable = sqliteTable('another_users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const pkExampleTable = sqliteTable('pk_example', { - id: integer('id').notNull(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => ({ - compositePk: primaryKey(table.id, table.name), -})); - -const bigIntExample = sqliteTable('big_int_example', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - bigInt: blob('big_int', { mode: 'bigint' }).notNull(), -}); - -// To test aggregate functions -const aggregateTable = sqliteTable('aggregate_table', { - id: integer('id').primaryKey({ autoIncrement: true }).notNull(), - name: text('name').notNull(), - a: integer('a'), - b: integer('b'), - c: integer('c'), - nullOnly: integer('null_only'), -}); - -test.before(async (t) => { - const ctx = t.context; - const url = process.env['LIBSQL_URL']; - const authToken = process.env['LIBSQL_AUTH_TOKEN']; - if (!url) { - throw new Error('LIBSQL_URL is not set'); - } - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = createClient({ url, authToken }); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to libsql'); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always(async (t) => { - t.context.client.close(); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - - await ctx.db.run(sql`drop table if exists ${usersTable}`); - await ctx.db.run(sql`drop table if exists ${users2Table}`); - await ctx.db.run(sql`drop table if exists ${citiesTable}`); - await ctx.db.run(sql`drop table if exists ${coursesTable}`); - await ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`); - await ctx.db.run(sql`drop table if exists ${orders}`); - await ctx.db.run(sql`drop table if exists ${bigIntExample}`); - await ctx.db.run(sql`drop table if exists ${pkExampleTable}`); - - await ctx.db.run(sql` - create table ${usersTable} ( - id integer primary key, - name text not null, - verified integer not null default 0, - json blob, - created_at integer not null default (strftime('%s', 'now')) - ) - `); - - await ctx.db.run(sql` - create table ${citiesTable} ( - id integer primary key, - name text not null - ) - `); - await ctx.db.run(sql` - create table ${courseCategoriesTable} ( - id integer primary key, - name text not null - ) - `); - - await ctx.db.run(sql` - create table ${users2Table} ( - id integer primary key, - name text not null, - city_id integer references ${citiesTable}(${name(citiesTable.id.name)}) - ) - `); - await ctx.db.run(sql` - create table ${coursesTable} ( - id integer primary key, - name text not null, - category_id integer references ${courseCategoriesTable}(${name(courseCategoriesTable.id.name)}) - ) - `); - await ctx.db.run(sql` - create table ${orders} ( - id integer primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `); - await ctx.db.run(sql` - create table ${pkExampleTable} ( - id integer not null, - name text not null, - email text not null, - primary key (id, name) - ) - `); - await ctx.db.run(sql` - create table ${bigIntExample} ( - id integer primary key, - name text not null, - big_int blob not null - ) - `); -}); - -async function setupSetOperationTest(db: LibSQLDatabase>) { - await db.run(sql`drop table if exists users2`); - await db.run(sql`drop table if exists cities`); - await db.run(sql` - create table \`cities\` ( - id integer primary key, - name text not null - ) - `); - - await db.run(sql` - create table \`users2\` ( - id integer primary key, - name text not null, - city_id integer references ${citiesTable}(${name(citiesTable.id.name)}) - ) - `); - - await db.insert(citiesTable).values([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - await db.insert(users2Table).values([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 2 }, - { id: 3, name: 'Jack', cityId: 3 }, - { id: 4, name: 'Peter', cityId: 3 }, - { id: 5, name: 'Ben', cityId: 2 }, - { id: 6, name: 'Jill', cityId: 1 }, - { id: 7, name: 'Mary', cityId: 2 }, - { id: 8, name: 'Sally', cityId: 1 }, - ]); -} - -async function setupAggregateFunctionsTest(db: LibSQLDatabase>) { - await db.run(sql`drop table if exists "aggregate_table"`); - await db.run( - sql` - create table "aggregate_table" ( - "id" integer primary key autoincrement not null, - "name" text not null, - "a" integer, - "b" integer, - "c" integer, - "null_only" integer - ); - `, - ); - await db.insert(aggregateTable).values([ - { name: 'value 1', a: 5, b: 10, c: 20 }, - { name: 'value 1', a: 5, b: 20, c: 30 }, - { name: 'value 2', a: 10, b: 50, c: 60 }, - { name: 'value 3', a: 20, b: 20, c: null }, - { name: 'value 4', a: null, b: 90, c: 120 }, - { name: 'value 5', a: 80, b: 10, c: null }, - { name: 'value 6', a: null, b: null, c: 150 }, - ]); -} - -test.serial('table config: foreign keys name', async (t) => { - const table = sqliteTable('cities', { - id: int('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: foreignKey({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk' }), - f1: foreignKey(() => ({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk_deprecated' })), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.foreignKeys.length, 2); - t.is(tableConfig.foreignKeys[0]!.getName(), 'custom_fk'); - t.is(tableConfig.foreignKeys[1]!.getName(), 'custom_fk_deprecated'); -}); - -test.serial('table config: primary keys name', async (t) => { - const table = sqliteTable('cities', { - id: int('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: primaryKey({ columns: [t.id, t.name], name: 'custom_pk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.primaryKeys.length, 1); - t.is(tableConfig.primaryKeys[0]!.getName(), 'custom_pk'); -}); - -test.serial('insert bigint values', async (t) => { - const { db } = t.context; - - await db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run(); - await db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run(); - await db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run(); - await db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run(); - await db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run(); - - const result = await db.select().from(bigIntExample).all(); - t.deepEqual(result, [ - { id: 1, name: 'one', bigInt: BigInt('0') }, - { id: 2, name: 'two', bigInt: BigInt('127') }, - { id: 3, name: 'three', bigInt: BigInt('32767') }, - { id: 4, name: 'four', bigInt: BigInt('1234567890') }, - { id: 5, name: 'five', bigInt: BigInt('12345678900987654321') }, - ]); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select().from(usersTable).all(); - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select({ name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = sqliteTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${usersDistinctTable}`); - await db.run(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]).run(); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ).all(); - - await db.run(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('query check: insert single empty row', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (null, ?, null)', - params: ['Dan'], - }); -}); - -test.serial('query check: insert multiple empty rows', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (null, ?, null), (null, ?, null)', - params: ['Dan', 'Dan'], - }); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = sqliteTable('empty_insert_single', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values({}).run(); - - const res = await db.select().from(users).all(); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = sqliteTable('empty_insert_multiple', { - id: integer('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values([{}, {}]).run(); - - const res = await db.select().from(users).all(); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('insert with auto increment', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'George' }, - { name: 'Austin' }, - ]).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'George' }, - { id: 4, name: 'Austin' }, - ]); -}); - -test.serial('insert with default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }).run(); - const result = await db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.insert(usersTable).values({ name: 'Jane' }).run(); - const result2 = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result2, [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', json: ['foo', 'bar'] }).run(); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', json: ['foo', 'bar'] }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]).run(); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }) - .all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)) - .all(); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const statement = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).prepare(); - const result = await statement.all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.run({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.all({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with exists', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const user = alias(usersTable, 'user'); - const result = await db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('migrator', async (t) => { - const { db } = t.context; - - await db.run(sql`drop table if exists another_users`); - await db.run(sql`drop table if exists users12`); - await db.run(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/sqlite' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result = await db.select().from(usersMigratorTable).all(); - - await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result2 = await db.select().from(anotherUsersMigratorTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - t.deepEqual(result2, [{ id: 1, name: 'John', email: 'email' }]); - - await db.run(sql`drop table another_users`); - await db.run(sql`drop table users12`); - await db.run(sql`drop table __drizzle_migrations`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.run(sql`drop table if exists another_users`); - await db.run(sql`drop table if exists users12`); - await db.run(sql`drop table if exists ${sql.identifier(customTable)}`); - - await migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); - - // test if the custom migrations table was created - const res = await db.all(sql`select * from ${sql.identifier(customTable)};`); - t.true(res.length > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.run(sql`drop table another_users`); - await db.run(sql`drop table users12`); - await db.run(sql`drop table ${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.run + select via db.all', async (t) => { - const { db } = t.context; - - await db.run(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.all<{ id: number; name: string }>(sql`select id, name from "users"`); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.get', async (t) => { - const { db } = t.context; - - const inserted = await db.get<{ id: number; name: string }>( - sql`insert into ${usersTable} (${new Name( - usersTable.name.name, - )}) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.run + select via db.get', async (t) => { - const { db } = t.context; - - await db.run(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.get<{ id: number; name: string }>( - sql`select ${usersTable.id}, ${usersTable.name} from ${usersTable}`, - ); - t.deepEqual(result, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.get w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.get, 'id' | 'name'>>( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all().then((res) => res[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all().then((res) => res[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all().then((res) => res[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)).all(); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]).run(); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]).run(); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name) - .all(); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]).run(); - - const regionalSales = await db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = await db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as int)`, - productSales: sql`cast(sum(${orders.amount}) as int)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product) - .all(); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('with ... update', async (t) => { - const { db } = t.context; - - const products = sqliteTable('products', { - id: integer('id').primaryKey(), - price: numeric('price').notNull(), - cheap: integer('cheap', { mode: 'boolean' }).notNull().default(false), - }); - - await db.run(sql`drop table if exists ${products}`); - await db.run(sql` - create table ${products} ( - id integer primary key, - price numeric not null, - cheap integer not null default 0 - ) - `); - - await db.insert(products).values([ - { price: '10.99' }, - { price: '25.85' }, - { price: '32.99' }, - { price: '2.50' }, - { price: '4.59' }, - ]); - - const averagePrice = db - .$with('average_price') - .as( - db - .select({ - value: sql`avg(${products.price})`.as('value'), - }) - .from(products), - ); - - const result = await db - .with(averagePrice) - .update(products) - .set({ - cheap: true, - }) - .where(lt(products.price, sql`(select * from ${averagePrice})`)) - .returning({ - id: products.id, - }); - - t.deepEqual(result, [ - { id: 1 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test.serial('with ... insert', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - username: text('username').notNull(), - admin: integer('admin', { mode: 'boolean' }).notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (username text not null, admin integer not null default 0)`); - - const userCount = db - .$with('user_count') - .as( - db - .select({ - value: sql`count(*)`.as('value'), - }) - .from(users), - ); - - const result = await db - .with(userCount) - .insert(users) - .values([ - { username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }, - ]) - .returning({ - admin: users.admin, - }); - - t.deepEqual(result, [{ admin: true }]); -}); - -test.serial('with ... delete', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const averageAmount = db - .$with('average_amount') - .as( - db - .select({ - value: sql`avg(${orders.amount})`.as('value'), - }) - .from(orders), - ); - - const result = await db - .with(averageAmount) - .delete(orders) - .where(gt(orders.amount, sql`(select * from ${averageAmount})`)) - .returning({ - id: orders.id, - }); - - t.deepEqual(result, [ - { id: 6 }, - { id: 7 }, - { id: 8 }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq).all(); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable).all(); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]).run(); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name) - .all(); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = sqliteView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = sqliteView('new_yorkers', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = sqliteView('new_yorkers', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.run(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]).run(); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - { - const result = await db.select().from(newYorkers1).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1).all(); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.run(sql`drop view ${newYorkers1}`); -}); - -test.serial('insert null timestamp', async (t) => { - const { db } = t.context; - - const test = sqliteTable('test', { - t: integer('t', { mode: 'timestamp' }), - }); - - await db.run(sql`create table ${test} (t timestamp)`); - - await db.insert(test).values({ t: null }).run(); - const res = await db.select().from(test).all(); - t.deepEqual(res, [{ t: null }]); - - await db.run(sql`drop table ${test}`); -}); - -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`).all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`.as('userName'), - userCity: sql`users.city`, - cityName: sql`cities.name`.as('cityName'), - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`) - .all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`.as('userName'), - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`.as('cityName'), - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `myprefix_${name}`); - - const users = sqliteTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }).run(); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = sqliteTable('products_transactions', { - id: integer('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`drop table if exists ${products}`); - - await db.run(sql`create table users_transactions (id integer not null primary key, balance integer not null)`); - await db.run( - sql`create table products_transactions (id integer not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().get(); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().get(); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)).run(); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)).run(); - }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.run(sql`drop table ${users}`); - await db.run(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table users_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table users_nested_transactions (id integer not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }).run(); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }).run(); - }); - }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table users_nested_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }).run(); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = sqliteTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = sqliteTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = sqliteTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.run(sql`drop table if exists ${internalStaff}`); - await db.run(sql`drop table if exists ${customUser}`); - await db.run(sql`drop table if exists ${ticket}`); - - await db.run(sql`create table internal_staff (user_id integer not null)`); - await db.run(sql`create table custom_user (id integer not null)`); - await db.run(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }).run(); - await db.insert(customUser).values({ id: 1 }).run(); - await db.insert(ticket).values({ staffId: 1 }).run(); - - const subq = await db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)) - .all(); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.run(sql`drop table ${internalStaff}`); - await db.run(sql`drop table ${customUser}`); - await db.run(sql`drop table ${ticket}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users_join_view', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = sqliteView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`drop view if exists ${newYorkers}`); - - await db.run( - sql`create table ${users} (id integer not null primary key, name text not null, city_id integer not null)`, - ); - await db.run(sql`create view ${newYorkers} as ${getViewConfig(newYorkers).query}`); - - db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]).run(); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)).all(); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.run(sql`drop view ${newYorkers}`); - await db.run(sql`drop table ${users}`); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing() - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk', async (t) => { - const { db } = t.context; - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing() - .run(); - - const res = await db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do nothing using target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk as target', async (t) => { - const { db } = t.context; - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing({ target: [pkExampleTable.id, pkExampleTable.name] }) - .run(); - - const res = await db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do update where', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([{ id: 1, name: 'John', verified: false }]) - .run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John1', verified: true }) - .onConflictDoUpdate({ - target: usersTable.id, - set: { name: 'John1', verified: true }, - where: eq(usersTable.verified, false), - }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name, verified: usersTable.verified }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1', verified: true }]); -}); - -test.serial('insert with onConflict do update using composite pk', async (t) => { - const { db } = t.context; - - await db.insert(pkExampleTable).values({ id: 1, name: 'John', email: 'john@example.com' }).run(); - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .onConflictDoUpdate({ target: [pkExampleTable.id, pkExampleTable.name], set: { email: 'john1@example.com' } }) - .run(); - - const res = await db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john1@example.com' }]); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined }).run()); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined }).run()); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined }).run()); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - CRUD', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - await db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)); - - const res1 = await db.select().from(users); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - await db.delete(users).where(eq(users.id, 1)); - - const res2 = await db.select().from(users); - - t.deepEqual(res2, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + async execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + sync execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('select + .get() for empty result', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const res = await db.select().from(users).where(eq(users.id, 1)).get(); - - t.is(res, undefined); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('set operations (union) from query builder with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const sq = db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - ).orderBy(asc(sql`name`)).as('sq'); - - const result = await db.select().from(sq).limit(5).offset(5); - - t.assert(result.length === 5); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 7, name: 'Mary' }, - { id: 1, name: 'New York' }, - { id: 4, name: 'Peter' }, - { id: 8, name: 'Sally' }, - ]); - - t.throws(() => { - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (union) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`name`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - union( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (union all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable), - ).orderBy(asc(citiesTable.id)).limit(5).offset(1); - - t.assert(result.length === 5); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).unionAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable), - ).orderBy(asc(citiesTable.id)).limit(5).offset(1); - }); -}); - -test.serial('set operations (union all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 3); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (intersect) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`name`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (intersect) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 0); - - t.deepEqual(result, []); - - t.throws(() => { - intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (except) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).except( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - db - .select() - .from(citiesTable).except( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - }); -}); - -test.serial('set operations (except) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await except( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable), - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - except( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable), - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (mixed) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).except( - ({ unionAll }) => - unionAll( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - db.select().from(citiesTable).where(eq(citiesTable.id, 2)), - ), - ); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select() - .from(citiesTable).except( - ({ unionAll }) => - unionAll( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - db.select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).where(eq(citiesTable.id, 2)), - ), - ); - }); -}); - -test.serial('set operations (mixed all) as function with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const sq = union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 7)), - ), - db - .select().from(citiesTable).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)).as('sq'); - - const result = await db.select().from(sq).limit(4).offset(1); - - t.assert(result.length === 4); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - ]); - - t.throws(() => { - union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 7)), - ), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('aggregate function: count', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: count() }).from(table); - const result2 = await db.select({ value: count(table.a) }).from(table); - const result3 = await db.select({ value: countDistinct(table.name) }).from(table); - - t.deepEqual(result1[0]?.value, 7); - t.deepEqual(result2[0]?.value, 5); - t.deepEqual(result3[0]?.value, 6); -}); - -test.serial('aggregate function: avg', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: avg(table.a) }).from(table); - const result2 = await db.select({ value: avg(table.nullOnly) }).from(table); - const result3 = await db.select({ value: avgDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '24'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '42.5'); -}); - -test.serial('aggregate function: sum', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: sum(table.b) }).from(table); - const result2 = await db.select({ value: sum(table.nullOnly) }).from(table); - const result3 = await db.select({ value: sumDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '200'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '170'); -}); - -test.serial('aggregate function: max', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: max(table.b) }).from(table); - const result2 = await db.select({ value: max(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 90); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('aggregate function: min', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: min(table.b) }).from(table); - const result2 = await db.select({ value: min(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 10); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { - const { db } = t.context; - - await db.run(sql`drop table if exists ${usersOnUpdate}`); - - await db.run( - sql` - create table ${usersOnUpdate} ( - id integer primary key autoincrement, - name text not null, - update_counter integer default 1 not null, - updated_at integer, - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - t.deepEqual(response, [ - { name: 'John', id: 1, updateCounter: 1, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, - ]); - const msDelay = 250; - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); - -test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { - const { db } = t.context; - - await db.run(sql`drop table if exists ${usersOnUpdate}`); - - await db.run( - sql` - create table ${usersOnUpdate} ( - id integer primary key autoincrement, - name text not null, - update_counter integer default 1, - updated_at integer, - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John', alwaysNull: 'this will be null after updating' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - - await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); - await db.update(usersOnUpdate).set({ updateCounter: null }).where(eq(usersOnUpdate.id, 2)); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - t.deepEqual(response, [ - { name: 'Angel', id: 1, updateCounter: 2, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: null, alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, - ]); - const msDelay = 250; - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); diff --git a/integration-tests/tests/__old/mysql-proxy.test.ts b/integration-tests/tests/__old/mysql-proxy.test.ts deleted file mode 100644 index aca682406..000000000 --- a/integration-tests/tests/__old/mysql-proxy.test.ts +++ /dev/null @@ -1,2122 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { asc, eq, gt, inArray, Name, placeholder, sql } from 'drizzle-orm'; -import { - alias, - boolean, - date, - datetime, - getTableConfig, - getViewConfig, - int, - json, - mysqlEnum, - mysqlTable, - mysqlTableCreator, - mysqlView, - serial, - text, - time, - timestamp, - unique, - uniqueIndex, - uniqueKeyName, - year, -} from 'drizzle-orm/mysql-core'; -import { drizzle as proxyDrizzle } from 'drizzle-orm/mysql-proxy'; -import type { MySqlRemoteDatabase } from 'drizzle-orm/mysql-proxy'; -import { migrate } from 'drizzle-orm/mysql-proxy/migrator'; -import getPort from 'get-port'; -import * as mysql from 'mysql2/promise'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, toLocalDate } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = mysqlTable('userstest', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: json('jsonb').$type(), - createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), -}); - -const users2Table = mysqlTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').references(() => citiesTable.id), -}); - -const citiesTable = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const datesTable = mysqlTable('datestable', { - date: date('date'), - dateAsString: date('date_as_string', { mode: 'string' }), - time: time('time', { fsp: 1 }), - datetime: datetime('datetime', { fsp: 2 }), - datetimeAsString: datetime('datetime_as_string', { fsp: 2, mode: 'string' }), - timestamp: timestamp('timestamp', { fsp: 3 }), - timestampAsString: timestamp('timestamp_as_string', { fsp: 3, mode: 'string' }), - year: year('year'), -}); - -const coursesTable = mysqlTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: int('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = mysqlTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = mysqlTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: int('amount').notNull(), - quantity: int('quantity').notNull(), -}); - -const usersMigratorTable = mysqlTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => { - return { - name: uniqueIndex('').on(table.name).using('btree'), - }; -}); - -// eslint-disable-next-line drizzle-internal/require-entity-kind -class ServerSimulator { - constructor(private db: mysql.Connection) {} - - async query(sql: string, params: any[], method: 'all' | 'execute') { - if (method === 'all') { - try { - const result = await this.db.query({ - sql, - values: params, - rowsAsArray: true, - typeCast: function(field: any, next: any) { - if (field.type === 'TIMESTAMP' || field.type === 'DATETIME' || field.type === 'DATE') { - return field.string(); - } - return next(); - }, - }); - - return { data: result[0] as any }; - } catch (e: any) { - return { error: e }; - } - } else if (method === 'execute') { - try { - const result = await this.db.query({ - sql, - values: params, - typeCast: function(field: any, next: any) { - if (field.type === 'TIMESTAMP' || field.type === 'DATETIME' || field.type === 'DATE') { - return field.string(); - } - return next(); - }, - }); - - return { data: result as any }; - } catch (e: any) { - return { error: e }; - } - } else { - return { error: 'Unknown method value' }; - } - } - - async migrations(queries: string[]) { - await this.db.query('START TRANSACTION'); - try { - for (const query of queries) { - await this.db.query(query); - } - await this.db.query('COMMIT'); - } catch (e) { - await this.db.query('ROLLBACK'); - throw e; - } - - return {}; - } -} - -interface Context { - docker: Docker; - mysqlContainer: Docker.Container; - db: MySqlRemoteDatabase; - client: mysql.Connection; - serverSimulator: ServerSimulator; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 3306 }); - const image = 'mysql:8'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.mysqlContainer = await docker.createContainer({ - Image: image, - Env: ['MYSQL_ROOT_PASSWORD=mysql', 'MYSQL_DATABASE=drizzle'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '3306/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.mysqlContainer.start(); - - return `mysql://root:mysql@127.0.0.1:${port}/drizzle`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 1000; - let timeLeft = 20000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = await mysql.createConnection(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to MySQL'); - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); - throw lastError; - } - - ctx.serverSimulator = new ServerSimulator(ctx.client); - - ctx.db = proxyDrizzle(async (sql, params, method) => { - try { - const response = await ctx.serverSimulator.query(sql, params, method); - - if (response.error !== undefined) { - throw response.error; - } - - return { rows: response.data }; - } catch (e: any) { - console.error('Error from mysql proxy server:', e.message); - throw e; - } - }, { logger: ENABLE_LOGGING }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - try { - const ctx = t.context; - await ctx.db.execute(sql`drop table if exists \`userstest\``); - await ctx.db.execute(sql`drop table if exists \`users2\``); - await ctx.db.execute(sql`drop table if exists \`cities\``); - - await ctx.db.execute( - sql` - create table \`userstest\` ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`users2\` ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references \`cities\`(\`id\`) - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`cities\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - } catch (error) { - console.log('error', error); - throw error; - } -}); - -test.serial('table configs: unique third param', async (t) => { - const cities1Table = mysqlTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: unique('custom_name').on(t.name, t.state), - f1: unique('custom_name1').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - t.assert(tableConfig.uniqueConstraints.length === 2); - - t.assert(tableConfig.uniqueConstraints[0]?.name === 'custom_name'); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); - - t.assert(tableConfig.uniqueConstraints[1]?.name, 'custom_name1'); - t.deepEqual(tableConfig.uniqueConstraints[1]?.columns.map((t) => t.name), ['name', 'state']); -}); - -test.serial('table configs: unique in column', async (t) => { - const cities1Table = mysqlTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - state: text('state').unique('custom'), - field: text('field').unique('custom_field'), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - t.assert(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name])); - t.assert(columnName?.isUnique); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - t.assert(columnState?.uniqueName === 'custom'); - t.assert(columnState?.isUnique); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - t.assert(columnField?.uniqueName === 'custom_field'); - t.assert(columnField?.isUnique); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = mysqlTable('users_distinct', { - id: int('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id int, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const [result, _] = await db.insert(usersTable).values({ name: 'John' }); - - t.deepEqual(result.insertId, 1); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(users[0].affectedRows, 1); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - t.is(users[0].changedRows, 1); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select().from(usersTable).where(eq(usersTable.id, 1)); - - t.is(updatedUsers[0].changedRows, 1); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(updatedUsers[0].changedRows, 1); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - - t.is(result[0].affectedRows, 4); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('$default with empty array', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`s_orders\``); - await db.execute( - sql` - create table \`s_orders\` ( - \`id\` serial primary key, - \`region\` text default ('Ukraine'), - \`product\` text not null - ) - `, - ); - - const users = mysqlTable('s_orders', { - id: serial('id').primaryKey(), - region: text('region').default('Ukraine'), - product: text('product').$defaultFn(() => 'random_string'), - }); - - await db.insert(users).values({}); - const selectedOrder = await db.select().from(users); - - t.deepEqual(selectedOrder, [{ - id: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: `select \`id\`, \`name\` from \`userstest\` group by \`userstest\`.\`id\`, \`userstest\`.\`name\``, - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into `users` (`id`, `name`, `state`) values (default, default, default)', - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into `users` (`id`, `name`, `state`) values (default, default, default), (default, default, default)', - params: [], - }); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = mysqlTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default ('Dan'), state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = mysqlTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default ('Dan'), state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('build query insert with onDuplicate', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into `userstest` (`id`, `name`, `verified`, `jsonb`, `created_at`) values (default, ?, default, ?, default) on duplicate key update `name` = ?', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('insert with onDuplicate', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert conflict', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await t.throwsAsync( - () => db.insert(usersTable).values({ id: 1, name: 'John1' }), - { - code: 'ER_DUP_ENTRY', - message: "Duplicate entry '1' for key 'userstest.PRIMARY'", - }, - ); -}); - -test.serial('insert conflict with ignore', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .ignore() - .values({ id: 1, name: 'John1' }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare(); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('migrator', async (t) => { - const { db, serverSimulator } = t.context; - - await db.execute(sql`drop table if exists userstest`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, async (queries) => { - try { - await serverSimulator.migrations(queries); - } catch (e) { - console.error(e); - throw new Error('Proxy server cannot run migrations'); - } - }, { migrationsFolder: './drizzle2/mysql-proxy/first' }); - - await t.notThrowsAsync(async () => { - await db.insert(usersTable).values({ name: 'John' }); - }); - - await t.throwsAsync(async () => { - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - }, { - message: "Table 'drizzle.users12' doesn't exist", - }); - - await migrate(db, async (queries) => { - try { - await serverSimulator.migrations(queries); - } catch (e) { - console.error(e); - throw new Error('Proxy server cannot run migrations'); - } - }, { migrationsFolder: './drizzle2/mysql-proxy/second' }); - - await t.notThrowsAsync(async () => { - await db.insert(usersTable).values({ name: 'John' }); - }); - - await t.notThrowsAsync(async () => { - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - }); - - await db.execute(sql`drop table userstest`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table __drizzle_migrations`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from ${usersTable}`); - t.deepEqual(result[0], [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute( - db.insert(usersTable).values({ name: 'John' }), - ); - t.is(inserted[0].affectedRows, 1); -}); - -test.serial('insert + select all possible dates', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`datestable\``); - await db.execute( - sql` - create table \`datestable\` ( - \`date\` date, - \`date_as_string\` date, - \`time\` time, - \`datetime\` datetime, - \`datetime_as_string\` datetime, - \`timestamp\` timestamp(3), - \`timestamp_as_string\` timestamp(3), - \`year\` year - ) - `, - ); - - const date = new Date('2022-11-11'); - const dateWithMilliseconds = new Date('2022-11-11 12:12:12.123'); - - await db.insert(datesTable).values({ - date: date, - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: date, - year: 22, - datetimeAsString: '2022-11-11 12:12:12', - timestamp: dateWithMilliseconds, - timestampAsString: '2022-11-11 12:12:12.123', - }); - - const res = await db.select().from(datesTable); - - t.assert(res[0]?.date instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.dateAsString === 'string'); - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - date: toLocalDate(new Date('2022-11-11')), - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: new Date('2022-11-11'), - year: 2022, - datetimeAsString: '2022-11-11 12:12:12', - timestamp: new Date('2022-11-11 12:12:12.123'), - timestampAsString: '2022-11-11 12:12:12.123', - }]); - - await db.execute(sql`drop table if exists \`datestable\``); -}); - -const tableWithEnums = mysqlTable('enums_test_case', { - id: serial('id').primaryKey(), - enum1: mysqlEnum('enum1', ['a', 'b', 'c']).notNull(), - enum2: mysqlEnum('enum2', ['a', 'b', 'c']).default('a'), - enum3: mysqlEnum('enum3', ['a', 'b', 'c']).notNull().default('b'), -}); - -test.serial('Mysql enum test case #1', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`enums_test_case\``); - - await db.execute(sql` - create table \`enums_test_case\` ( - \`id\` serial primary key, - \`enum1\` ENUM('a', 'b', 'c') not null, - \`enum2\` ENUM('a', 'b', 'c') default 'a', - \`enum3\` ENUM('a', 'b', 'c') not null default 'b' - ) - `); - - await db.insert(tableWithEnums).values([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum3: 'c' }, - { id: 3, enum1: 'a' }, - ]); - - const res = await db.select().from(tableWithEnums); - - await db.execute(sql`drop table \`enums_test_case\``); - - t.deepEqual(res, [ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum2: 'a', enum3: 'c' }, - { id: 3, enum1: 'a', enum2: 'a', enum3: 'b' }, - ]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId: 1, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: 1, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId: 1, - }, - cities: { - id: 1, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`courses\``); - await db.execute(sql`drop table if exists \`course_categories\``); - - await db.execute( - sql` - create table \`course_categories\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await db.execute( - sql` - create table \`courses\` ( - \`id\` serial primary key, - \`name\` text not null, - \`category_id\` int references \`course_categories\`(\`id\`) - ) - `, - ); - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); - - await db.execute(sql`drop table if exists \`courses\``); - await db.execute(sql`drop table if exists \`course_categories\``); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as unsigned)`, - productSales: sql`cast(sum(${orders.amount}) as unsigned)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`concat(${users2Table.name}, " modified")`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db.select().from(users2Table).for('update').toSQL(); - t.regex(query.sql, / for update$/); - } - { - const query = db.select().from(users2Table).for('share', { skipLocked: true }).toSQL(); - t.regex(query.sql, / for share skip locked$/); - } - { - const query = db.select().from(users2Table).for('update', { noWait: true }).toSQL(); - t.regex(query.sql, / for update no wait$/); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = mysqlView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `myprefix_${name}`); - - const users = mysqlTable('test_prefixed_table_with_unique_name', { - id: int('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id int not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as `test` from `users2` order by `test`'); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const date = new Date(Date.parse('2020-01-01T12:34:56+07:00')); - - await db.insert(usersTable).values({ name: 'With default times' }); - await db.insert(usersTable).values({ - name: 'Without default times', - createdAt: date, - }); - const users = await db.select().from(usersTable); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -// TODO: implement transactions -// test.serial('transaction', async (t) => { -// const { db } = t.context; - -// const users = mysqlTable('users_transactions', { -// id: serial('id').primaryKey(), -// balance: int('balance').notNull(), -// }); -// const products = mysqlTable('products_transactions', { -// id: serial('id').primaryKey(), -// price: int('price').notNull(), -// stock: int('stock').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); -// await db.execute(sql`drop table if exists ${products}`); - -// await db.execute(sql`create table users_transactions (id serial not null primary key, balance int not null)`); -// await db.execute( -// sql`create table products_transactions (id serial not null primary key, price int not null, stock int not null)`, -// ); - -// const [{ insertId: userId }] = await db.insert(users).values({ balance: 100 }); -// const user = await db.select().from(users).where(eq(users.id, userId)).then((rows) => rows[0]!); -// const [{ insertId: productId }] = await db.insert(products).values({ price: 10, stock: 10 }); -// const product = await db.select().from(products).where(eq(products.id, productId)).then((rows) => rows[0]!); - -// await db.transaction(async (tx) => { -// await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); -// await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); -// }); - -// const result = await db.select().from(users); - -// t.deepEqual(result, [{ id: 1, balance: 90 }]); - -// await db.execute(sql`drop table ${users}`); -// await db.execute(sql`drop table ${products}`); -// }); - -// TODO: implement transactions -// test.serial('transaction rollback', async (t) => { -// const { db } = t.context; - -// const users = mysqlTable('users_transactions_rollback', { -// id: serial('id').primaryKey(), -// balance: int('balance').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); - -// await db.execute( -// sql`create table users_transactions_rollback (id serial not null primary key, balance int not null)`, -// ); - -// await t.throwsAsync(async () => -// await db.transaction(async (tx) => { -// await tx.insert(users).values({ balance: 100 }); -// tx.rollback(); -// }), new TransactionRollbackError()); - -// const result = await db.select().from(users); - -// t.deepEqual(result, []); - -// await db.execute(sql`drop table ${users}`); -// }); - -// TODO: implement transactions -// test.serial('nested transaction', async (t) => { -// const { db } = t.context; - -// const users = mysqlTable('users_nested_transactions', { -// id: serial('id').primaryKey(), -// balance: int('balance').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); - -// await db.execute( -// sql`create table users_nested_transactions (id serial not null primary key, balance int not null)`, -// ); - -// await db.transaction(async (tx) => { -// await tx.insert(users).values({ balance: 100 }); - -// await tx.transaction(async (tx) => { -// await tx.update(users).set({ balance: 200 }); -// }); -// }); - -// const result = await db.select().from(users); - -// t.deepEqual(result, [{ id: 1, balance: 200 }]); - -// await db.execute(sql`drop table ${users}`); -// }); - -// TODO: implement transactions -// test.serial('nested transaction rollback', async (t) => { -// const { db } = t.context; - -// const users = mysqlTable('users_nested_transactions_rollback', { -// id: serial('id').primaryKey(), -// balance: int('balance').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); - -// await db.execute( -// sql`create table users_nested_transactions_rollback (id serial not null primary key, balance int not null)`, -// ); - -// await db.transaction(async (tx) => { -// await tx.insert(users).values({ balance: 100 }); - -// await t.throwsAsync(async () => -// await tx.transaction(async (tx) => { -// await tx.update(users).set({ balance: 200 }); -// tx.rollback(); -// }), new TransactionRollbackError()); -// }); - -// const result = await db.select().from(users); - -// t.deepEqual(result, [{ id: 1, balance: 100 }]); - -// await db.execute(sql`drop table ${users}`); -// }); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = mysqlTable('internal_staff', { - userId: int('user_id').notNull(), - }); - - const customUser = mysqlTable('custom_user', { - id: int('id').notNull(), - }); - - const ticket = mysqlTable('ticket', { - staffId: int('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -// TODO: implement iterator -// test.serial('select iterator', async (t) => { -// const { db } = t.context; - -// const users = mysqlTable('users_iterator', { -// id: serial('id').primaryKey(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); -// await db.execute(sql`create table ${users} (id serial not null primary key)`); - -// await db.insert(users).values([{}, {}, {}]); - -// const iter = db.select().from(users).iterator(); -// const result: InferModel[] = []; - -// for await (const row of iter) { -// result.push(row); -// } - -// t.deepEqual(result, [{ id: 1 }, { id: 2 }, { id: 3 }]); -// }); - -// TODO: implement iterator -// test.serial('select iterator w/ prepared statement', async (t) => { -// const { db } = t.context; - -// const users = mysqlTable('users_iterator', { -// id: serial('id').primaryKey(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); -// await db.execute(sql`create table ${users} (id serial not null primary key)`); - -// await db.insert(users).values([{}, {}, {}]); - -// const prepared = db.select().from(users).prepare(); -// const iter = prepared.iterator(); -// const result: InferModel[] = []; - -// for await (const row of iter) { -// result.push(row); -// } - -// t.deepEqual(result, [{ id: 1 }, { id: 2 }, { id: 3 }]); -// }); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('utc config for datetime', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`datestable\``); - await db.execute( - sql` - create table \`datestable\` ( - \`datetime_utc\` datetime(3), - \`datetime\` datetime(3), - \`datetime_as_string\` datetime - ) - `, - ); - const datesTable = mysqlTable('datestable', { - datetimeUTC: datetime('datetime_utc', { fsp: 3, mode: 'date' }), - datetime: datetime('datetime', { fsp: 3 }), - datetimeAsString: datetime('datetime_as_string', { mode: 'string' }), - }); - - const dateObj = new Date('2022-11-11'); - const dateUtc = new Date('2022-11-11T12:12:12.122Z'); - - await db.insert(datesTable).values({ - datetimeUTC: dateUtc, - datetime: dateObj, - datetimeAsString: '2022-11-11 12:12:12', - }); - - const res = await db.select().from(datesTable); - - const [rawSelect] = await db.execute(sql`select \`datetime_utc\` from \`datestable\``); - const selectedRow = (rawSelect as unknown as [{ datetime_utc: string }])[0]; - - t.is(selectedRow.datetime_utc, '2022-11-11 12:12:12.122'); - t.deepEqual(new Date(selectedRow.datetime_utc.replace(' ', 'T') + 'Z'), dateUtc); - - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetimeUTC instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - datetimeUTC: dateUtc, - datetime: new Date('2022-11-11'), - datetimeAsString: '2022-11-11 12:12:12', - }]); - - await db.execute(sql`drop table if exists \`datestable\``); -}); diff --git a/integration-tests/tests/__old/mysql-schema.test.ts b/integration-tests/tests/__old/mysql-schema.test.ts deleted file mode 100644 index 96720a422..000000000 --- a/integration-tests/tests/__old/mysql-schema.test.ts +++ /dev/null @@ -1,900 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { asc, eq, Name, sql } from 'drizzle-orm'; -import { - alias, - boolean, - date, - datetime, - getViewConfig, - int, - json, - mysqlEnum, - mysqlSchema, - mysqlTable, - mysqlTableCreator, - serial, - text, - time, - timestamp, - year, -} from 'drizzle-orm/mysql-core'; -import type { MySql2Database } from 'drizzle-orm/mysql2'; -import { drizzle } from 'drizzle-orm/mysql2'; -import getPort from 'get-port'; -import * as mysql from 'mysql2/promise'; -import { v4 as uuid } from 'uuid'; - -import { toLocalDate } from '../utils.ts'; - -const mySchema = mysqlSchema('mySchema'); - -const usersTable = mySchema.table('userstest', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: json('jsonb').$type(), - createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), -}); - -const users2Table = mySchema.table('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').references(() => citiesTable.id), -}); - -const citiesTable = mySchema.table('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const publicUsersTable = mysqlTable('userstest', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: json('jsonb').$type(), - createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), -}); - -const datesTable = mysqlTable('datestable', { - date: date('date'), - dateAsString: date('date_as_string', { mode: 'string' }), - time: time('time', { fsp: 1 }), - datetime: datetime('datetime', { fsp: 2 }), - datetimeAsString: datetime('datetime_as_string', { fsp: 2, mode: 'string' }), - year: year('year'), -}); - -interface Context { - docker: Docker; - mysqlContainer: Docker.Container; - db: MySql2Database; - client: mysql.Connection; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 3306 }); - const image = 'mysql:8'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.mysqlContainer = await docker.createContainer({ - Image: image, - Env: ['MYSQL_ROOT_PASSWORD=mysql', 'MYSQL_DATABASE=mysqltests'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '3306/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.mysqlContainer.start(); - - return `mysql://root:mysql@127.0.0.1:${port}/mysql`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 1000; - let timeLeft = 20000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = await mysql.createConnection(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to MySql'); - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client /* , { logger: new DefaultLogger() } */); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop table if exists \`datestable\``); - await ctx.db.execute(sql`drop schema if exists \`mySchema\``); - await ctx.db.execute(sql`create schema if not exists \`mySchema\``); - await ctx.db.execute( - sql` - create table \`mySchema\`.\`userstest\` ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`mySchema\`.\`cities\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`mySchema\`.\`users2\` ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references \`mySchema\`.\`cities\`(\`id\`) - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`datestable\` ( - \`date\` date, - \`date_as_string\` date, - \`time\` time, - \`datetime\` datetime, - \`datetime_as_string\` datetime, - \`year\` year - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = mysqlTable('users_distinct', { - id: int('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id int, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const [result, _] = await db.insert(usersTable).values({ name: 'John' }); - - t.deepEqual(result.insertId, 1); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(users[0].affectedRows, 1); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - t.is(users[0].changedRows, 1); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select().from(usersTable).where(eq(usersTable.id, 1)); - - t.is(updatedUsers[0].changedRows, 1); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(updatedUsers[0].changedRows, 1); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - - t.is(result[0].affectedRows, 4); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: - `select \`id\`, \`name\` from \`mySchema\`.\`userstest\` group by \`userstest\`.\`id\`, \`userstest\`.\`name\``, - params: [], - }); -}); - -test.serial('build query insert with onDuplicate', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into `mySchema`.`userstest` (`id`, `name`, `verified`, `jsonb`, `created_at`) values (default, ?, default, ?, default) on duplicate key update `name` = ?', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('insert with onDuplicate', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert conflict', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await t.throwsAsync( - () => db.insert(usersTable).values({ id: 1, name: 'John1' }), - { - code: 'ER_DUP_ENTRY', - message: "Duplicate entry '1' for key 'userstest.PRIMARY'", - }, - ); -}); - -test.serial('insert conflict with ignore', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .ignore() - .values({ id: 1, name: 'John1' }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare(); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: sql.placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, sql.placeholder('id'))) - .prepare(); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from ${usersTable}`); - t.deepEqual(result[0], [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute( - db.insert(usersTable).values({ name: 'John' }), - ); - t.is(inserted[0].affectedRows, 1); -}); - -test.serial('insert + select all possible dates', async (t) => { - const { db } = t.context; - - const date = new Date('2022-11-11'); - - await db.insert(datesTable).values({ - date: date, - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: date, - year: 22, - datetimeAsString: '2022-11-11 12:12:12', - }); - - const res = await db.select().from(datesTable); - - t.assert(res[0]?.date instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.dateAsString === 'string'); - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - date: toLocalDate(new Date('2022-11-11')), - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: new Date('2022-11-11'), - year: 2022, - datetimeAsString: '2022-11-11 12:12:12', - }]); -}); -test.serial('select from tables with same name from different schema using alias', async (t) => { - const { db } = t.context; - await db.execute(sql`drop table if exists \`userstest\``); - await db.execute( - sql` - create table \`userstest\` ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await db.insert(usersTable).values({ id: 10, name: 'Ivan' }); - await db.insert(publicUsersTable).values({ id: 11, name: 'Hans' }); - - const customerAlias = alias(publicUsersTable, 'customer'); - - const result = await db - .select().from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - userstest: { - id: 10, - name: 'Ivan', - verified: false, - jsonb: null, - createdAt: result[0]!.userstest.createdAt, - }, - customer: { - id: 11, - name: 'Hans', - verified: false, - jsonb: null, - createdAt: result[0]!.customer!.createdAt, - }, - }]); -}); - -const tableWithEnums = mySchema.table('enums_test_case', { - id: serial('id').primaryKey(), - enum1: mysqlEnum('enum1', ['a', 'b', 'c']).notNull(), - enum2: mysqlEnum('enum2', ['a', 'b', 'c']).default('a'), - enum3: mysqlEnum('enum3', ['a', 'b', 'c']).notNull().default('b'), -}); - -test.serial('Mysql enum test case #1', async (t) => { - const { db } = t.context; - - await db.execute(sql` - create table ${tableWithEnums} ( - \`id\` serial primary key, - \`enum1\` ENUM('a', 'b', 'c') not null, - \`enum2\` ENUM('a', 'b', 'c') default 'a', - \`enum3\` ENUM('a', 'b', 'c') not null default 'b' - ) - `); - - await db.insert(tableWithEnums).values([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum3: 'c' }, - { id: 3, enum1: 'a' }, - ]); - - const res = await db.select().from(tableWithEnums); - - await db.execute(sql`drop table ${tableWithEnums}`); - - t.deepEqual(res, [ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum2: 'a', enum3: 'c' }, - { id: 3, enum1: 'a', enum2: 'a', enum3: 'b' }, - ]); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = mySchema.view('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mySchema.view('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mySchema.view('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); diff --git a/integration-tests/tests/__old/mysql.custom.test.ts b/integration-tests/tests/__old/mysql.custom.test.ts deleted file mode 100644 index af07cc3ea..000000000 --- a/integration-tests/tests/__old/mysql.custom.test.ts +++ /dev/null @@ -1,853 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { asc, eq, Name, placeholder, sql } from 'drizzle-orm'; -import { - alias, - binary, - customType, - date, - datetime, - mysqlEnum, - mysqlTable, - mysqlTableCreator, - serial, - text, - time, - varchar, - year, -} from 'drizzle-orm/mysql-core'; -import type { MySql2Database } from 'drizzle-orm/mysql2'; -import { drizzle } from 'drizzle-orm/mysql2'; -import { migrate } from 'drizzle-orm/mysql2/migrator'; -import getPort from 'get-port'; -import * as mysql from 'mysql2/promise'; -import { v4 as uuid } from 'uuid'; - -import { toLocalDate } from '../utils.ts'; - -const customSerial = customType<{ data: number; notNull: true; default: true }>({ - dataType() { - return 'serial'; - }, -}); - -const customText = customType<{ data: string }>({ - dataType() { - return 'text'; - }, -}); - -const customBoolean = customType<{ data: boolean }>({ - dataType() { - return 'boolean'; - }, - fromDriver(value) { - if (typeof value === 'boolean') { - return value; - } - return value === 1; - }, -}); - -const customJson = (name: string) => - customType<{ data: TData; driverData: string }>({ - dataType() { - return 'json'; - }, - toDriver(value: TData): string { - return JSON.stringify(value); - }, - })(name); - -const customTimestamp = customType< - { data: Date; driverData: string; config: { fsp: number } } ->({ - dataType(config) { - const precision = config?.fsp === undefined ? '' : ` (${config.fsp})`; - return `timestamp${precision}`; - }, - fromDriver(value: string): Date { - return new Date(value); - }, -}); - -const customBinary = customType<{ data: string; driverData: Buffer; config: { length: number } }>({ - dataType(config) { - return config?.length === undefined - ? `binary` - : `binary(${config.length})`; - }, - - toDriver(value) { - return sql`UNHEX(${value})`; - }, - - fromDriver(value) { - return value.toString('hex'); - }, -}); - -const usersTable = mysqlTable('userstest', { - id: customSerial('id').primaryKey(), - name: customText('name').notNull(), - verified: customBoolean('verified').notNull().default(false), - jsonb: customJson('jsonb'), - createdAt: customTimestamp('created_at', { fsp: 2 }).notNull().default(sql`now()`), -}); - -const datesTable = mysqlTable('datestable', { - date: date('date'), - dateAsString: date('date_as_string', { mode: 'string' }), - time: time('time', { fsp: 1 }), - datetime: datetime('datetime', { fsp: 2 }), - datetimeAsString: datetime('datetime_as_string', { fsp: 2, mode: 'string' }), - year: year('year'), -}); - -export const testTable = mysqlTable('test_table', { - id: customBinary('id', { length: 16 }).primaryKey(), - sqlId: binary('sql_id', { length: 16 }), - rawId: varchar('raw_id', { length: 64 }), -}); - -const usersMigratorTable = mysqlTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - docker: Docker; - mysqlContainer: Docker.Container; - db: MySql2Database; - client: mysql.Connection; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 3306 }); - const image = 'mysql:8'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.mysqlContainer = await docker.createContainer({ - Image: image, - Env: ['MYSQL_ROOT_PASSWORD=mysql', 'MYSQL_DATABASE=drizzle'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '3306/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.mysqlContainer.start(); - - return `mysql://root:mysql@127.0.0.1:${port}/drizzle`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 1000; - let timeLeft = 20000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = await mysql.createConnection(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to MySQL'); - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: false }); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop table if exists \`userstest\``); - await ctx.db.execute(sql`drop table if exists \`datestable\``); - await ctx.db.execute(sql`drop table if exists \`test_table\``); - // await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table \`userstest\` ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`datestable\` ( - \`date\` date, - \`date_as_string\` date, - \`time\` time, - \`datetime\` datetime, - \`datetime_as_string\` datetime, - \`year\` year - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`test_table\` ( - \`id\` binary(16) primary key, - \`sql_id\` binary(16), - \`raw_id\` varchar(64) - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const [result, _] = await db.insert(usersTable).values({ name: 'John' }); - - t.deepEqual(result.insertId, 1); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(users[0].affectedRows, 1); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - t.is(users[0].changedRows, 1); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select().from(usersTable).where(eq(usersTable.id, 1)); - - t.is(updatedUsers[0].changedRows, 1); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(updatedUsers[0].changedRows, 1); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - - t.is(result[0].affectedRows, 4); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: `select \`id\`, \`name\` from \`userstest\` group by \`userstest\`.\`id\`, \`userstest\`.\`name\``, - params: [], - }); -}); - -test.serial('build query insert with onDuplicate', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into `userstest` (`id`, `name`, `verified`, `jsonb`, `created_at`) values (default, ?, default, ?, default) on duplicate key update `name` = ?', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('insert with onDuplicate', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert conflict', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await t.throwsAsync( - () => db.insert(usersTable).values({ id: 1, name: 'John1' }), - { - code: 'ER_DUP_ENTRY', - message: "Duplicate entry '1' for key 'userstest.PRIMARY'", - }, - ); -}); - -test.serial('insert conflict with ignore', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .ignore() - .values({ id: 1, name: 'John1' }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare(); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('migrator', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists cities_migration`); - await db.execute(sql`drop table if exists users_migration`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/mysql' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table cities_migration`); - await db.execute(sql`drop table users_migration`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table __drizzle_migrations`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from ${usersTable}`); - t.deepEqual(result[0], [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute( - db.insert(usersTable).values({ name: 'John' }), - ); - t.is(inserted[0].affectedRows, 1); -}); - -test.serial('insert + select all possible dates', async (t) => { - const { db } = t.context; - - const date = new Date('2022-11-11'); - - await db.insert(datesTable).values({ - date: date, - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: date, - year: 22, - datetimeAsString: '2022-11-11 12:12:12', - }); - - const res = await db.select().from(datesTable); - - t.assert(res[0]?.date instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.dateAsString === 'string'); - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - date: toLocalDate(new Date('2022-11-11')), - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: new Date('2022-11-11'), - year: 2022, - datetimeAsString: '2022-11-11 12:12:12', - }]); -}); - -const tableWithEnums = mysqlTable('enums_test_case', { - id: serial('id').primaryKey(), - enum1: mysqlEnum('enum1', ['a', 'b', 'c']).notNull(), - enum2: mysqlEnum('enum2', ['a', 'b', 'c']).default('a'), - enum3: mysqlEnum('enum3', ['a', 'b', 'c']).notNull().default('b'), -}); - -test.serial('Mysql enum test case #1', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`enums_test_case\``); - - await db.execute(sql` - create table \`enums_test_case\` ( - \`id\` serial primary key, - \`enum1\` ENUM('a', 'b', 'c') not null, - \`enum2\` ENUM('a', 'b', 'c') default 'a', - \`enum3\` ENUM('a', 'b', 'c') not null default 'b' - ) - `); - - await db.insert(tableWithEnums).values([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum3: 'c' }, - { id: 3, enum1: 'a' }, - ]); - - const res = await db.select().from(tableWithEnums); - - await db.execute(sql`drop table \`enums_test_case\``); - - t.deepEqual(res, [ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum2: 'a', enum3: 'c' }, - { id: 3, enum1: 'a', enum2: 'a', enum3: 'b' }, - ]); -}); - -test.serial('custom binary', async (t) => { - const { db } = t.context; - - const id = uuid().replace(/-/g, ''); - await db.insert(testTable).values({ - id, - sqlId: sql`UNHEX(${id})`, - rawId: id, - }); - - const res = await db.select().from(testTable); - - t.deepEqual(res, [{ - id, - sqlId: Buffer.from(id, 'hex'), - rawId: id, - }]); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); -}); diff --git a/integration-tests/tests/__old/mysql.prefixed.test.ts b/integration-tests/tests/__old/mysql.prefixed.test.ts deleted file mode 100644 index 39597b31a..000000000 --- a/integration-tests/tests/__old/mysql.prefixed.test.ts +++ /dev/null @@ -1,1780 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { - asc, - DefaultLogger, - eq, - getTableName, - gt, - inArray, - type InferModel, - Name, - placeholder, - sql, - TransactionRollbackError, -} from 'drizzle-orm'; -import { - alias, - boolean, - date, - datetime, - getViewConfig, - int, - json, - mysqlEnum, - mysqlTable as mysqlTableRaw, - mysqlTableCreator, - mysqlView, - serial, - text, - time, - timestamp, - uniqueIndex, - year, -} from 'drizzle-orm/mysql-core'; -import type { MySql2Database } from 'drizzle-orm/mysql2'; -import { drizzle } from 'drizzle-orm/mysql2'; -import { migrate } from 'drizzle-orm/mysql2/migrator'; -import getPort from 'get-port'; -import * as mysql from 'mysql2/promise'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, toLocalDate } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const tablePrefix = 'drizzle_tests_'; - -const mysqlTable = mysqlTableCreator((name) => `${tablePrefix}${name}`); - -const usersTable = mysqlTable('userstest', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: json('jsonb').$type(), - createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), -}); - -const users2Table = mysqlTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').references(() => citiesTable.id), -}); - -const citiesTable = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -interface Context { - docker: Docker; - mysqlContainer: Docker.Container; - db: MySql2Database; - client: mysql.Connection; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 3306 }); - const image = 'mysql:8'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.mysqlContainer = await docker.createContainer({ - Image: image, - Env: ['MYSQL_ROOT_PASSWORD=mysql', 'MYSQL_DATABASE=drizzle'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '3306/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.mysqlContainer.start(); - - return `mysql://root:mysql@127.0.0.1:${port}/drizzle`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 1000; - let timeLeft = 20000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = await mysql.createConnection(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to MySQL'); - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING ? new DefaultLogger() : undefined }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop table if exists ${usersTable}`); - await ctx.db.execute(sql`drop table if exists ${users2Table}`); - await ctx.db.execute(sql`drop table if exists ${citiesTable}`); - - await ctx.db.execute( - sql` - create table ${usersTable} ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await ctx.db.execute( - sql` - create table ${users2Table} ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references ${citiesTable}(\`id\`) - ) - `, - ); - - await ctx.db.execute( - sql` - create table ${citiesTable} ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = mysqlTable('users_distinct', { - id: int('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id int, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const [result, _] = await db.insert(usersTable).values({ name: 'John' }); - - t.deepEqual(result.insertId, 1); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(users[0].affectedRows, 1); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - t.is(users[0].changedRows, 1); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select().from(usersTable).where(eq(usersTable.id, 1)); - - t.is(updatedUsers[0].changedRows, 1); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(updatedUsers[0].changedRows, 1); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - - t.is(result[0].affectedRows, 4); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: `select \`id\`, \`name\` from \`${getTableName(usersTable)}\` group by \`${ - getTableName(usersTable) - }\`.\`id\`, \`${getTableName(usersTable)}\`.\`name\``, - params: [], - }); -}); - -test.serial('build query insert with onDuplicate', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: `insert into \`${ - getTableName(usersTable) - }\` (\`id\`, \`name\`, \`verified\`, \`jsonb\`, \`created_at\`) values (default, ?, default, ?, default) on duplicate key update \`name\` = ?`, - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('insert with onDuplicate', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert conflict', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await t.throwsAsync( - () => db.insert(usersTable).values({ id: 1, name: 'John1' }), - { - code: 'ER_DUP_ENTRY', - }, - ); -}); - -test.serial('insert conflict with ignore', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .ignore() - .values({ id: 1, name: 'John1' }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare(); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('migrator', async (t) => { - const { db } = t.context; - - const usersMigratorTable = mysqlTableRaw('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), - }, (table) => { - return { - name: uniqueIndex('').on(table.name).using('btree'), - }; - }); - - await db.execute(sql.raw(`drop table if exists cities_migration`)); - await db.execute(sql.raw(`drop table if exists users_migration`)); - await db.execute(sql.raw(`drop table if exists users12`)); - await db.execute(sql.raw(`drop table if exists __drizzle_migrations`)); - - await migrate(db, { migrationsFolder: './drizzle2/mysql' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql.raw(`drop table cities_migration`)); - await db.execute(sql.raw(`drop table users_migration`)); - await db.execute(sql.raw(`drop table users12`)); - await db.execute(sql.raw(`drop table __drizzle_migrations`)); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from ${usersTable}`); - t.deepEqual(result[0], [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute( - db.insert(usersTable).values({ name: 'John' }), - ); - t.is(inserted[0].affectedRows, 1); -}); - -test.serial('insert + select all possible dates', async (t) => { - const { db } = t.context; - - const datesTable = mysqlTable('datestable', { - date: date('date'), - dateAsString: date('date_as_string', { mode: 'string' }), - time: time('time', { fsp: 1 }), - datetime: datetime('datetime', { fsp: 2 }), - datetimeAsString: datetime('datetime_as_string', { fsp: 2, mode: 'string' }), - year: year('year'), - }); - - await db.execute(sql`drop table if exists ${datesTable}`); - await db.execute( - sql` - create table ${datesTable} ( - \`date\` date, - \`date_as_string\` date, - \`time\` time, - \`datetime\` datetime, - \`datetime_as_string\` datetime, - \`year\` year - ) - `, - ); - - const d = new Date('2022-11-11'); - - await db.insert(datesTable).values({ - date: d, - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: d, - year: 22, - datetimeAsString: '2022-11-11 12:12:12', - }); - - const res = await db.select().from(datesTable); - - t.assert(res[0]?.date instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.dateAsString === 'string'); - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - date: toLocalDate(new Date('2022-11-11')), - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: new Date('2022-11-11'), - year: 2022, - datetimeAsString: '2022-11-11 12:12:12', - }]); - - await db.execute(sql`drop table ${datesTable}`); -}); - -test.serial('Mysql enum test case #1', async (t) => { - const { db } = t.context; - - const tableWithEnums = mysqlTable('enums_test_case', { - id: serial('id').primaryKey(), - enum1: mysqlEnum('enum1', ['a', 'b', 'c']).notNull(), - enum2: mysqlEnum('enum2', ['a', 'b', 'c']).default('a'), - enum3: mysqlEnum('enum3', ['a', 'b', 'c']).notNull().default('b'), - }); - - await db.execute(sql`drop table if exists ${tableWithEnums}`); - - await db.execute(sql` - create table ${tableWithEnums} ( - \`id\` serial primary key, - \`enum1\` ENUM('a', 'b', 'c') not null, - \`enum2\` ENUM('a', 'b', 'c') default 'a', - \`enum3\` ENUM('a', 'b', 'c') not null default 'b' - ) - `); - - await db.insert(tableWithEnums).values([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum3: 'c' }, - { id: 3, enum1: 'a' }, - ]); - - const res = await db.select().from(tableWithEnums); - - await db.execute(sql`drop table ${tableWithEnums}`); - - t.deepEqual(res, [ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum2: 'a', enum3: 'c' }, - { id: 3, enum1: 'a', enum2: 'a', enum3: 'b' }, - ]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId: 1, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: 1, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId: 1, - }, - cities: { - id: 1, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - const coursesTable = mysqlTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: int('category_id').references(() => courseCategoriesTable.id), - }); - - const courseCategoriesTable = mysqlTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${coursesTable}`); - await db.execute(sql`drop table if exists ${courseCategoriesTable}`); - - await db.execute( - sql` - create table ${courseCategoriesTable} ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await db.execute( - sql` - create table ${coursesTable} ( - \`id\` serial primary key, - \`name\` text not null, - \`category_id\` int references ${courseCategoriesTable}(\`id\`) - ) - `, - ); - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - await db.execute(sql`drop table ${coursesTable}`); - await db.execute(sql`drop table ${courseCategoriesTable}`); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - const orders = mysqlTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: int('amount').notNull(), - quantity: int('quantity').notNull(), - }); - - await db.execute(sql`drop table if exists ${orders}`); - await db.execute( - sql` - create table ${orders} ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as unsigned)`, - productSales: sql`cast(sum(${orders.amount}) as unsigned)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - await db.execute(sql`drop table ${orders}`); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`concat(${users2Table.name}, " modified")`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db.select().from(users2Table).for('update').toSQL(); - t.regex(query.sql, / for update$/); - } - { - const query = db.select().from(users2Table).for('share', { skipLocked: true }).toSQL(); - t.regex(query.sql, / for share skip locked$/); - } - { - const query = db.select().from(users2Table).for('update', { noWait: true }).toSQL(); - t.regex(query.sql, / for update no wait$/); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = mysqlView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `myprefix_${name}`); - - const users = mysqlTable('test_prefixed_table_with_unique_name', { - id: int('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id int not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, `select something as \`test\` from \`${getTableName(users2Table)}\` order by \`test\``); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const date = new Date(Date.parse('2020-01-01T12:34:56+07:00')); - - await db.insert(usersTable).values({ name: 'With default times' }); - await db.insert(usersTable).values({ - name: 'Without default times', - createdAt: date, - }); - const users = await db.select().from(usersTable); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_transactions', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - const products = mysqlTable('products_transactions', { - id: serial('id').primaryKey(), - price: int('price').notNull(), - stock: int('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table ${users} (id serial not null primary key, balance int not null)`); - await db.execute( - sql`create table ${products} (id serial not null primary key, price int not null, stock int not null)`, - ); - - const [{ insertId: userId }] = await db.insert(users).values({ balance: 100 }); - const user = await db.select().from(users).where(eq(users.id, userId)).then((rows) => rows[0]!); - const [{ insertId: productId }] = await db.insert(products).values({ price: 10, stock: 10 }); - const product = await db.select().from(products).where(eq(products.id, productId)).then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, balance int not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - await db.execute(sql`drop table ${users}`); - - t.deepEqual(result, []); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, balance int not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - await db.execute(sql`drop table ${users}`); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, balance int not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - await db.execute(sql`drop table ${users}`); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = mysqlTable('internal_staff', { - userId: int('user_id').notNull(), - }); - - const customUser = mysqlTable('custom_user', { - id: int('id').notNull(), - }); - - const ticket = mysqlTable('ticket', { - staffId: int('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table ${internalStaff} (user_id integer not null)`); - await db.execute(sql`create table ${customUser} (id integer not null)`); - await db.execute(sql`create table ${ticket} (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select iterator', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_iterator', { - id: serial('id').primaryKey(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial not null primary key)`); - - await db.insert(users).values([{}, {}, {}]); - - const iter = db.select().from(users).iterator(); - const result: InferModel[] = []; - - for await (const row of iter) { - result.push(row); - } - - t.deepEqual(result, [{ id: 1 }, { id: 2 }, { id: 3 }]); -}); - -test.serial('select iterator w/ prepared statement', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_iterator', { - id: serial('id').primaryKey(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial not null primary key)`); - - await db.insert(users).values([{}, {}, {}]); - - const prepared = db.select().from(users).prepare(); - const iter = prepared.iterator(); - const result: InferModel[] = []; - - for await (const row of iter) { - result.push(row); - } - - t.deepEqual(result, [{ id: 1 }, { id: 2 }, { id: 3 }]); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); diff --git a/integration-tests/tests/__old/mysql.test.ts b/integration-tests/tests/__old/mysql.test.ts deleted file mode 100644 index c50f149ba..000000000 --- a/integration-tests/tests/__old/mysql.test.ts +++ /dev/null @@ -1,3001 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { - and, - asc, - avg, - avgDistinct, - count, - countDistinct, - DefaultLogger, - eq, - exists, - getTableColumns, - gt, - gte, - inArray, - type InferModel, - lt, - max, - min, - Name, - placeholder, - sql, - sum, - sumDistinct, - TransactionRollbackError, -} from 'drizzle-orm'; -import { - alias, - bigint, - boolean, - date, - datetime, - decimal, - except, - exceptAll, - foreignKey, - getTableConfig, - getViewConfig, - int, - intersect, - intersectAll, - json, - mediumint, - mysqlEnum, - mysqlTable, - mysqlTableCreator, - mysqlView, - primaryKey, - serial, - smallint, - text, - time, - timestamp, - tinyint, - union, - unionAll, - unique, - uniqueIndex, - uniqueKeyName, - year, -} from 'drizzle-orm/mysql-core'; -import type { MySql2Database } from 'drizzle-orm/mysql2'; -import { drizzle } from 'drizzle-orm/mysql2'; -import { migrate } from 'drizzle-orm/mysql2/migrator'; -import getPort from 'get-port'; -import * as mysql from 'mysql2/promise'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, toLocalDate } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = mysqlTable('userstest', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: json('jsonb').$type(), - createdAt: timestamp('created_at', { fsp: 2 }).notNull().defaultNow(), -}); - -const users2Table = mysqlTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').references(() => citiesTable.id), -}); - -const citiesTable = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const usersOnUpdate = mysqlTable('users_on_update', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - updateCounter: int('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), - updatedAt: datetime('updated_at', { mode: 'date', fsp: 3 }).$onUpdate(() => new Date()), - uppercaseName: text('uppercase_name').$onUpdateFn(() => sql`upper(name)`), - alwaysNull: text('always_null').$type().$onUpdateFn(() => null), // need to add $type because $onUpdate add a default value -}); - -const datesTable = mysqlTable('datestable', { - date: date('date'), - dateAsString: date('date_as_string', { mode: 'string' }), - time: time('time', { fsp: 1 }), - datetime: datetime('datetime', { fsp: 2 }), - datetimeAsString: datetime('datetime_as_string', { fsp: 2, mode: 'string' }), - timestamp: timestamp('timestamp', { fsp: 3 }), - timestampAsString: timestamp('timestamp_as_string', { fsp: 3, mode: 'string' }), - year: year('year'), -}); - -const coursesTable = mysqlTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: int('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = mysqlTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = mysqlTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: int('amount').notNull(), - quantity: int('quantity').notNull(), -}); - -const usersMigratorTable = mysqlTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => { - return { - name: uniqueIndex('').on(table.name).using('btree'), - }; -}); - -// To test aggregate functions -const aggregateTable = mysqlTable('aggregate_table', { - id: serial('id').notNull(), - name: text('name').notNull(), - a: int('a'), - b: int('b'), - c: int('c'), - nullOnly: int('null_only'), -}); - -interface Context { - docker: Docker; - mysqlContainer: Docker.Container; - db: MySql2Database; - client: mysql.Connection; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 3306 }); - const image = 'mysql:8'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.mysqlContainer = await docker.createContainer({ - Image: image, - Env: ['MYSQL_ROOT_PASSWORD=mysql', 'MYSQL_DATABASE=drizzle'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '3306/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.mysqlContainer.start(); - - return `mysql://root:mysql@127.0.0.1:${port}/drizzle`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 1000; - let timeLeft = 20000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = await mysql.createConnection(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to MySQL'); - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING ? new DefaultLogger() : undefined }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.mysqlContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop table if exists \`userstest\``); - await ctx.db.execute(sql`drop table if exists \`users2\``); - await ctx.db.execute(sql`drop table if exists \`cities\``); - - await ctx.db.execute( - sql` - create table \`userstest\` ( - \`id\` serial primary key, - \`name\` text not null, - \`verified\` boolean not null default false, - \`jsonb\` json, - \`created_at\` timestamp not null default now() - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`users2\` ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references \`cities\`(\`id\`) - ) - `, - ); - - await ctx.db.execute( - sql` - create table \`cities\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); -}); - -async function setupSetOperationTest(db: MySql2Database) { - await db.execute(sql`drop table if exists \`users2\``); - await db.execute(sql`drop table if exists \`cities\``); - await db.execute( - sql` - create table \`users2\` ( - \`id\` serial primary key, - \`name\` text not null, - \`city_id\` int references \`cities\`(\`id\`) - ) - `, - ); - - await db.execute( - sql` - create table \`cities\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await db.insert(citiesTable).values([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - await db.insert(users2Table).values([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 2 }, - { id: 3, name: 'Jack', cityId: 3 }, - { id: 4, name: 'Peter', cityId: 3 }, - { id: 5, name: 'Ben', cityId: 2 }, - { id: 6, name: 'Jill', cityId: 1 }, - { id: 7, name: 'Mary', cityId: 2 }, - { id: 8, name: 'Sally', cityId: 1 }, - ]); -} - -async function setupAggregateFunctionsTest(db: MySql2Database) { - await db.execute(sql`drop table if exists \`aggregate_table\``); - await db.execute( - sql` - create table \`aggregate_table\` ( - \`id\` integer primary key auto_increment not null, - \`name\` text not null, - \`a\` integer, - \`b\` integer, - \`c\` integer, - \`null_only\` integer - ); - `, - ); - await db.insert(aggregateTable).values([ - { name: 'value 1', a: 5, b: 10, c: 20 }, - { name: 'value 1', a: 5, b: 20, c: 30 }, - { name: 'value 2', a: 10, b: 50, c: 60 }, - { name: 'value 3', a: 20, b: 20, c: null }, - { name: 'value 4', a: null, b: 90, c: 120 }, - { name: 'value 5', a: 80, b: 10, c: null }, - { name: 'value 6', a: null, b: null, c: 150 }, - ]); -} - -test.serial('table config: unsigned ints', async (t) => { - const unsignedInts = mysqlTable('cities1', { - bigint: bigint('bigint', { mode: 'number', unsigned: true }), - int: int('int', { unsigned: true }), - smallint: smallint('smallint', { unsigned: true }), - mediumint: mediumint('mediumint', { unsigned: true }), - tinyint: tinyint('tinyint', { unsigned: true }), - }); - - const tableConfig = getTableConfig(unsignedInts); - - const bigintColumn = tableConfig.columns.find((c) => c.name === 'bigint')!; - const intColumn = tableConfig.columns.find((c) => c.name === 'int')!; - const smallintColumn = tableConfig.columns.find((c) => c.name === 'smallint')!; - const mediumintColumn = tableConfig.columns.find((c) => c.name === 'mediumint')!; - const tinyintColumn = tableConfig.columns.find((c) => c.name === 'tinyint')!; - - t.is(bigintColumn.getSQLType(), 'bigint unsigned'); - t.is(intColumn.getSQLType(), 'int unsigned'); - t.is(smallintColumn.getSQLType(), 'smallint unsigned'); - t.is(mediumintColumn.getSQLType(), 'mediumint unsigned'); - t.is(tinyintColumn.getSQLType(), 'tinyint unsigned'); -}); - -test.serial('table config: signed ints', async (t) => { - const unsignedInts = mysqlTable('cities1', { - bigint: bigint('bigint', { mode: 'number' }), - int: int('int'), - smallint: smallint('smallint'), - mediumint: mediumint('mediumint'), - tinyint: tinyint('tinyint'), - }); - - const tableConfig = getTableConfig(unsignedInts); - - const bigintColumn = tableConfig.columns.find((c) => c.name === 'bigint')!; - const intColumn = tableConfig.columns.find((c) => c.name === 'int')!; - const smallintColumn = tableConfig.columns.find((c) => c.name === 'smallint')!; - const mediumintColumn = tableConfig.columns.find((c) => c.name === 'mediumint')!; - const tinyintColumn = tableConfig.columns.find((c) => c.name === 'tinyint')!; - - t.is(bigintColumn.getSQLType(), 'bigint'); - t.is(intColumn.getSQLType(), 'int'); - t.is(smallintColumn.getSQLType(), 'smallint'); - t.is(mediumintColumn.getSQLType(), 'mediumint'); - t.is(tinyintColumn.getSQLType(), 'tinyint'); -}); - -test.serial('table config: foreign keys name', async (t) => { - const table = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: foreignKey({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.foreignKeys.length, 1); - t.is(tableConfig.foreignKeys[0]!.getName(), 'custom_fk'); -}); - -test.serial('table config: primary keys name', async (t) => { - const table = mysqlTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: primaryKey({ columns: [t.id, t.name], name: 'custom_pk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.primaryKeys.length, 1); - t.is(tableConfig.primaryKeys[0]!.getName(), 'custom_pk'); -}); - -test.serial('table configs: unique third param', async (t) => { - const cities1Table = mysqlTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: unique('custom_name').on(t.name, t.state), - f1: unique('custom_name1').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - t.assert(tableConfig.uniqueConstraints.length === 2); - - t.assert(tableConfig.uniqueConstraints[0]?.name === 'custom_name'); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); - - t.assert(tableConfig.uniqueConstraints[1]?.name, 'custom_name1'); - t.deepEqual(tableConfig.uniqueConstraints[1]?.columns.map((t) => t.name), ['name', 'state']); -}); - -test.serial('table configs: unique in column', async (t) => { - const cities1Table = mysqlTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - state: text('state').unique('custom'), - field: text('field').unique('custom_field'), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - t.assert(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name])); - t.assert(columnName?.isUnique); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - t.assert(columnState?.uniqueName === 'custom'); - t.assert(columnState?.isUnique); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - t.assert(columnField?.uniqueName === 'custom_field'); - t.assert(columnField?.isUnique); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = mysqlTable('users_distinct', { - id: int('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id int, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const [result, _] = await db.insert(usersTable).values({ name: 'John' }); - - t.deepEqual(result.insertId, 1); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(users[0].affectedRows, 1); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - t.is(users[0].changedRows, 1); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select().from(usersTable).where(eq(usersTable.id, 1)); - - t.is(updatedUsers[0].changedRows, 1); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - // not timezone based timestamp, thats why it should not work here - // t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 2000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const updatedUsers = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')); - - const users = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(updatedUsers[0].changedRows, 1); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const deletedUser = await db.delete(usersTable).where(eq(usersTable.name, 'John')); - - t.is(deletedUser[0].affectedRows, 1); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - - t.is(result[0].affectedRows, 4); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('select with exists', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const user = alias(usersTable, 'user'); - const result = await db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }]); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('$default with empty array', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`s_orders\``); - await db.execute( - sql` - create table \`s_orders\` ( - \`id\` serial primary key, - \`region\` text default ('Ukraine'), - \`product\` text not null - ) - `, - ); - - const users = mysqlTable('s_orders', { - id: serial('id').primaryKey(), - region: text('region').default('Ukraine'), - product: text('product').$defaultFn(() => 'random_string'), - }); - - await db.insert(users).values({}); - const selectedOrder = await db.select().from(users); - - t.deepEqual(selectedOrder, [{ - id: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: `select \`id\`, \`name\` from \`userstest\` group by \`userstest\`.\`id\`, \`userstest\`.\`name\``, - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into `users` (`id`, `name`, `state`) values (default, default, default)', - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into `users` (`id`, `name`, `state`) values (default, default, default), (default, default, default)', - params: [], - }); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = mysqlTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default ('Dan'), state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = mysqlTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default ('Dan'), state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('build query insert with onDuplicate', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into `userstest` (`id`, `name`, `verified`, `jsonb`, `created_at`) values (default, ?, default, ?, default) on duplicate key update `name` = ?', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('insert with onDuplicate', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onDuplicateKeyUpdate({ set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert conflict', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await t.throwsAsync( - () => db.insert(usersTable).values({ id: 1, name: 'John1' }), - { - code: 'ER_DUP_ENTRY', - message: "Duplicate entry '1' for key 'userstest.PRIMARY'", - }, - ); -}); - -test.serial('insert conflict with ignore', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .ignore() - .values({ id: 1, name: 'John1' }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `prefixed_${name}`); - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare(); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('migrator', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists cities_migration`); - await db.execute(sql`drop table if exists users_migration`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/mysql' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table cities_migration`); - await db.execute(sql`drop table users_migration`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table __drizzle_migrations`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from ${usersTable}`); - t.deepEqual(result[0], [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute( - db.insert(usersTable).values({ name: 'John' }), - ); - t.is(inserted[0].affectedRows, 1); -}); - -test.serial('insert + select all possible dates', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`datestable\``); - await db.execute( - sql` - create table \`datestable\` ( - \`date\` date, - \`date_as_string\` date, - \`time\` time, - \`datetime\` datetime, - \`datetime_as_string\` datetime, - \`timestamp\` timestamp(3), - \`timestamp_as_string\` timestamp(3), - \`year\` year - ) - `, - ); - - const date = new Date('2022-11-11'); - const dateWithMilliseconds = new Date('2022-11-11 12:12:12.123'); - - await db.insert(datesTable).values({ - date: date, - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: date, - year: 22, - datetimeAsString: '2022-11-11 12:12:12', - timestamp: dateWithMilliseconds, - timestampAsString: '2022-11-11 12:12:12.123', - }); - - const res = await db.select().from(datesTable); - - t.assert(res[0]?.date instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.dateAsString === 'string'); - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - date: toLocalDate(new Date('2022-11-11')), - dateAsString: '2022-11-11', - time: '12:12:12', - datetime: new Date('2022-11-11'), - year: 2022, - datetimeAsString: '2022-11-11 12:12:12', - timestamp: new Date('2022-11-11 12:12:12.123'), - timestampAsString: '2022-11-11 12:12:12.123', - }]); - - await db.execute(sql`drop table if exists \`datestable\``); -}); - -const tableWithEnums = mysqlTable('enums_test_case', { - id: serial('id').primaryKey(), - enum1: mysqlEnum('enum1', ['a', 'b', 'c']).notNull(), - enum2: mysqlEnum('enum2', ['a', 'b', 'c']).default('a'), - enum3: mysqlEnum('enum3', ['a', 'b', 'c']).notNull().default('b'), -}); - -test.serial('Mysql enum test case #1', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`enums_test_case\``); - - await db.execute(sql` - create table \`enums_test_case\` ( - \`id\` serial primary key, - \`enum1\` ENUM('a', 'b', 'c') not null, - \`enum2\` ENUM('a', 'b', 'c') default 'a', - \`enum3\` ENUM('a', 'b', 'c') not null default 'b' - ) - `); - - await db.insert(tableWithEnums).values([ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum3: 'c' }, - { id: 3, enum1: 'a' }, - ]); - - const res = await db.select().from(tableWithEnums); - - await db.execute(sql`drop table \`enums_test_case\``); - - t.deepEqual(res, [ - { id: 1, enum1: 'a', enum2: 'b', enum3: 'c' }, - { id: 2, enum1: 'a', enum2: 'a', enum3: 'c' }, - { id: 3, enum1: 'a', enum2: 'a', enum3: 'b' }, - ]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId: 1, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: 1, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane' }]); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId: 1, - }, - cities: { - id: 1, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`courses\``); - await db.execute(sql`drop table if exists \`course_categories\``); - - await db.execute( - sql` - create table \`course_categories\` ( - \`id\` serial primary key, - \`name\` text not null - ) - `, - ); - - await db.execute( - sql` - create table \`courses\` ( - \`id\` serial primary key, - \`name\` text not null, - \`category_id\` int references \`course_categories\`(\`id\`) - ) - `, - ); - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); - - await db.execute(sql`drop table if exists \`courses\``); - await db.execute(sql`drop table if exists \`course_categories\``); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as unsigned)`, - productSales: sql`cast(sum(${orders.amount}) as unsigned)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('with ... update', async (t) => { - const { db } = t.context; - - const products = mysqlTable('products', { - id: serial('id').primaryKey(), - price: decimal('price', { - precision: 15, - scale: 2, - }).notNull(), - cheap: boolean('cheap').notNull().default(false), - }); - - await db.execute(sql`drop table if exists ${products}`); - await db.execute(sql` - create table ${products} ( - id serial primary key, - price decimal(15, 2) not null, - cheap boolean not null default false - ) - `); - - await db.insert(products).values([ - { price: '10.99' }, - { price: '25.85' }, - { price: '32.99' }, - { price: '2.50' }, - { price: '4.59' }, - ]); - - const averagePrice = db - .$with('average_price') - .as( - db - .select({ - value: sql`avg(${products.price})`.as('value'), - }) - .from(products), - ); - - await db - .with(averagePrice) - .update(products) - .set({ - cheap: true, - }) - .where(lt(products.price, sql`(select * from ${averagePrice})`)); - - const result = await db - .select({ - id: products.id, - }) - .from(products) - .where(eq(products.cheap, true)); - - t.deepEqual(result, [ - { id: 1 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test.serial('with ... delete', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`orders\``); - await db.execute( - sql` - create table \`orders\` ( - \`id\` serial primary key, - \`region\` text not null, - \`product\` text not null, - \`amount\` int not null, - \`quantity\` int not null - ) - `, - ); - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const averageAmount = db - .$with('average_amount') - .as( - db - .select({ - value: sql`avg(${orders.amount})`.as('value'), - }) - .from(orders), - ); - - await db - .with(averageAmount) - .delete(orders) - .where(gt(orders.amount, sql`(select * from ${averageAmount})`)); - - const result = await db - .select({ - id: orders.id, - }) - .from(orders); - - t.deepEqual(result, [ - { id: 1 }, - { id: 2 }, - { id: 3 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`concat(${users2Table.name}, " modified")`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db.select().from(users2Table).for('update').toSQL(); - t.regex(query.sql, / for update$/); - } - { - const query = db.select().from(users2Table).for('share', { skipLocked: true }).toSQL(); - t.regex(query.sql, / for share skip locked$/); - } - { - const query = db.select().from(users2Table).for('update', { noWait: true }).toSQL(); - t.regex(query.sql, / for update no wait$/); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = mysqlView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mysqlView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const mysqlTable = mysqlTableCreator((name) => `myprefix_${name}`); - - const users = mysqlTable('test_prefixed_table_with_unique_name', { - id: int('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id int not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as `test` from `users2` order by `test`'); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const date = new Date(Date.parse('2020-01-01T12:34:56+07:00')); - - await db.insert(usersTable).values({ name: 'With default times' }); - await db.insert(usersTable).values({ - name: 'Without default times', - createdAt: date, - }); - const users = await db.select().from(usersTable); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_transactions', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - const products = mysqlTable('products_transactions', { - id: serial('id').primaryKey(), - price: int('price').notNull(), - stock: int('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance int not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price int not null, stock int not null)`, - ); - - const [{ insertId: userId }] = await db.insert(users).values({ balance: 100 }); - const user = await db.select().from(users).where(eq(users.id, userId)).then((rows) => rows[0]!); - const [{ insertId: productId }] = await db.insert(products).values({ price: 10, stock: 10 }); - const product = await db.select().from(products).where(eq(products.id, productId)).then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance int not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - t.deepEqual(result, []); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance int not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: int('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance int not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = mysqlTable('internal_staff', { - userId: int('user_id').notNull(), - }); - - const customUser = mysqlTable('custom_user', { - id: int('id').notNull(), - }); - - const ticket = mysqlTable('ticket', { - staffId: int('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: int('city_id').notNull(), - }); - - const newYorkers = mysqlView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select iterator', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_iterator', { - id: serial('id').primaryKey(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial not null primary key)`); - - await db.insert(users).values([{}, {}, {}]); - - const iter = db.select().from(users).iterator(); - const result: InferModel[] = []; - - for await (const row of iter) { - result.push(row); - } - - t.deepEqual(result, [{ id: 1 }, { id: 2 }, { id: 3 }]); -}); - -test.serial('select iterator w/ prepared statement', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users_iterator', { - id: serial('id').primaryKey(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial not null primary key)`); - - await db.insert(users).values([{}, {}, {}]); - - const prepared = db.select().from(users).prepare(); - const iter = prepared.iterator(); - const result: InferModel[] = []; - - for await (const row of iter) { - result.push(row); - } - - t.deepEqual(result, [{ id: 1 }, { id: 2 }, { id: 3 }]); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = mysqlTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('utc config for datetime', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists \`datestable\``); - await db.execute( - sql` - create table \`datestable\` ( - \`datetime_utc\` datetime(3), - \`datetime\` datetime(3), - \`datetime_as_string\` datetime - ) - `, - ); - const datesTable = mysqlTable('datestable', { - datetimeUTC: datetime('datetime_utc', { fsp: 3, mode: 'date' }), - datetime: datetime('datetime', { fsp: 3 }), - datetimeAsString: datetime('datetime_as_string', { mode: 'string' }), - }); - - const dateObj = new Date('2022-11-11'); - const dateUtc = new Date('2022-11-11T12:12:12.122Z'); - - await db.insert(datesTable).values({ - datetimeUTC: dateUtc, - datetime: dateObj, - datetimeAsString: '2022-11-11 12:12:12', - }); - - const res = await db.select().from(datesTable); - - const [rawSelect] = await db.execute(sql`select \`datetime_utc\` from \`datestable\``); - const selectedRow = (rawSelect as unknown as [{ datetime_utc: string }])[0]; - - t.is(selectedRow.datetime_utc, '2022-11-11 12:12:12.122'); - t.deepEqual(new Date(selectedRow.datetime_utc.replace(' ', 'T') + 'Z'), dateUtc); - - t.assert(res[0]?.datetime instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(res[0]?.datetimeUTC instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(typeof res[0]?.datetimeAsString === 'string'); - - t.deepEqual(res, [{ - datetimeUTC: dateUtc, - datetime: new Date('2022-11-11'), - datetimeAsString: '2022-11-11 12:12:12', - }]); - - await db.execute(sql`drop table if exists \`datestable\``); -}); - -test.serial('set operations (union) from query builder with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - const sq = db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).as('sq'); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).union( - db.select().from(sq), - ).limit(8); - - t.assert(result.length === 8); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'Jack' }, - { id: 4, name: 'Peter' }, - { id: 5, name: 'Ben' }, - ]); - - // union should throw if selected fields are not in the same order - t.throws(() => - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).union( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table), - ) - ); -}); - -test.serial('set operations (union) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - union( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (union all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)).limit(3); - - t.assert(result.length === 3); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).unionAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (union all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - unionAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - }); -}); - -test.serial('set operations (intersect) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - }); -}); - -test.serial('set operations (intersect) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - - t.assert(result.length === 0); - - t.deepEqual(result, []); - - t.throws(() => { - intersect( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(1); - }); -}); - -test.serial('set operations (intersect all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).intersectAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).limit(2).intersectAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).limit(2), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (intersect all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersectAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - intersectAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (except) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).except( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); -}); - -test.serial('set operations (except) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await except( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable), - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(3); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - except( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable), - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(3); - }); -}); - -test.serial('set operations (except all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).exceptAll( - db - .select({ id: citiesTable.id, name: citiesTable.name }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select() - .from(citiesTable).exceptAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (except all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await exceptAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(6).orderBy(asc(sql.identifier('id'))); - - t.assert(result.length === 6); - - t.deepEqual(result, [ - { id: 2, name: 'Jane' }, - { id: 3, name: 'Jack' }, - { id: 4, name: 'Peter' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - { id: 7, name: 'Mary' }, - ]); - - t.throws(() => { - exceptAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).limit(6); - }); -}); - -test.serial('set operations (mixed) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(citiesTable).except( - ({ unionAll }) => - unionAll( - db - .select() - .from(citiesTable).where(gt(citiesTable.id, 1)), - db.select().from(citiesTable).where(eq(citiesTable.id, 2)), - ).orderBy(asc(citiesTable.id)).limit(1).offset(1), - ); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select() - .from(citiesTable).except( - ({ unionAll }) => - unionAll( - db - .select({ name: citiesTable.name, id: citiesTable.id }) - .from(citiesTable).where(gt(citiesTable.id, 1)), - db.select().from(citiesTable).where(eq(citiesTable.id, 2)), - ), - ); - }); -}); - -test.serial('set operations (mixed all) as function with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const sq = except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 7)), - ).orderBy(asc(sql.identifier('id'))).as('sq'); - - const result = await union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db.select().from(sq).limit(1), - db - .select().from(citiesTable).where(gt(citiesTable.id, 1)), - ); - - t.assert(result.length === 4); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 5, name: 'Ben' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 7)), - ).limit(1), - db - .select().from(citiesTable).where(gt(citiesTable.id, 1)), - ); - }); -}); - -test.serial('aggregate function: count', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: count() }).from(table); - const result2 = await db.select({ value: count(table.a) }).from(table); - const result3 = await db.select({ value: countDistinct(table.name) }).from(table); - - t.deepEqual(result1[0]?.value, 7); - t.deepEqual(result2[0]?.value, 5); - t.deepEqual(result3[0]?.value, 6); -}); - -test.serial('aggregate function: avg', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: avg(table.b) }).from(table); - const result2 = await db.select({ value: avg(table.nullOnly) }).from(table); - const result3 = await db.select({ value: avgDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '33.3333'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '42.5000'); -}); - -test.serial('aggregate function: sum', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: sum(table.b) }).from(table); - const result2 = await db.select({ value: sum(table.nullOnly) }).from(table); - const result3 = await db.select({ value: sumDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '200'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '170'); -}); - -test.serial('aggregate function: max', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: max(table.b) }).from(table); - const result2 = await db.select({ value: max(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 90); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('aggregate function: min', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: min(table.b) }).from(table); - const result2 = await db.select({ value: min(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 10); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial not null primary key, - name text not null, - update_counter integer default 1 not null, - updated_at datetime(3), - uppercase_name text, - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate); - - const response = await db.select({ ...rest }).from(usersOnUpdate); - - t.deepEqual(response, [ - { name: 'John', id: 1, updateCounter: 1, uppercaseName: 'JOHN', alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, uppercaseName: 'JANE', alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, uppercaseName: 'JACK', alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, uppercaseName: 'JILL', alwaysNull: null }, - ]); - const msDelay = 250; - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); - -test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial not null primary key, - name text not null, - update_counter integer default 1 not null, - updated_at datetime(3), - uppercase_name text, - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John', alwaysNull: 'this will will be null after updating' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - const initial = await db.select({ updatedAt }).from(usersOnUpdate); - - await db.update(usersOnUpdate).set({ name: 'Angel', uppercaseName: null }).where(eq(usersOnUpdate.id, 1)); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate); - - const response = await db.select({ ...rest }).from(usersOnUpdate); - - t.deepEqual(response, [ - { name: 'Angel', id: 1, updateCounter: 2, uppercaseName: null, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, uppercaseName: 'JANE', alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, uppercaseName: 'JACK', alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, uppercaseName: 'JILL', alwaysNull: null }, - ]); - const msDelay = 250; - - t.assert(initial[0]?.updatedAt?.valueOf() !== justDates[0]?.updatedAt?.valueOf()); - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); diff --git a/integration-tests/tests/__old/neon-http-batch.test.ts b/integration-tests/tests/__old/neon-http-batch.test.ts deleted file mode 100644 index 1e380ae52..000000000 --- a/integration-tests/tests/__old/neon-http-batch.test.ts +++ /dev/null @@ -1,556 +0,0 @@ -import 'dotenv/config'; -import { neon } from '@neondatabase/serverless'; -import type { NeonQueryFunction } from '@neondatabase/serverless'; -import type { InferSelectModel } from 'drizzle-orm'; -import { eq, relations, sql } from 'drizzle-orm'; -import { drizzle } from 'drizzle-orm/neon-http'; -import type { NeonHttpDatabase, NeonHttpQueryResult } from 'drizzle-orm/neon-http'; -import { type AnyPgColumn, integer, pgTable, primaryKey, serial, text, timestamp } from 'drizzle-orm/pg-core'; -import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; - -const ENABLE_LOGGING = false; - -export const usersTable = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: integer('verified').notNull().default(0), - invitedBy: integer('invited_by').references((): AnyPgColumn => usersTable.id), -}); -export const usersConfig = relations(usersTable, ({ one, many }) => ({ - invitee: one(usersTable, { - fields: [usersTable.invitedBy], - references: [usersTable.id], - }), - usersToGroups: many(usersToGroupsTable), - posts: many(postsTable), -})); - -export const groupsTable = pgTable('groups', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - description: text('description'), -}); -export const groupsConfig = relations(groupsTable, ({ many }) => ({ - usersToGroups: many(usersToGroupsTable), -})); - -export const usersToGroupsTable = pgTable( - 'users_to_groups', - { - id: serial('id'), - userId: integer('user_id').notNull().references(() => usersTable.id), - groupId: integer('group_id').notNull().references(() => groupsTable.id), - }, - (t) => ({ - pk: primaryKey({ columns: [t.userId, t.groupId] }), - }), -); -export const usersToGroupsConfig = relations(usersToGroupsTable, ({ one }) => ({ - group: one(groupsTable, { - fields: [usersToGroupsTable.groupId], - references: [groupsTable.id], - }), - user: one(usersTable, { - fields: [usersToGroupsTable.userId], - references: [usersTable.id], - }), -})); - -export const postsTable = pgTable('posts', { - id: serial('id').primaryKey(), - content: text('content').notNull(), - ownerId: integer('owner_id').references(() => usersTable.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); -export const postsConfig = relations(postsTable, ({ one, many }) => ({ - author: one(usersTable, { - fields: [postsTable.ownerId], - references: [usersTable.id], - }), - comments: many(commentsTable), -})); - -export const commentsTable = pgTable('comments', { - id: serial('id').primaryKey(), - content: text('content').notNull(), - creator: integer('creator').references(() => usersTable.id), - postId: integer('post_id').references(() => postsTable.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); -export const commentsConfig = relations(commentsTable, ({ one, many }) => ({ - post: one(postsTable, { - fields: [commentsTable.postId], - references: [postsTable.id], - }), - author: one(usersTable, { - fields: [commentsTable.creator], - references: [usersTable.id], - }), - likes: many(commentLikesTable), -})); - -export const commentLikesTable = pgTable('comment_likes', { - id: serial('id').primaryKey(), - creator: integer('creator').references(() => usersTable.id), - commentId: integer('comment_id').references(() => commentsTable.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); -export const commentLikesConfig = relations(commentLikesTable, ({ one }) => ({ - comment: one(commentsTable, { - fields: [commentLikesTable.commentId], - references: [commentsTable.id], - }), - author: one(usersTable, { - fields: [commentLikesTable.creator], - references: [usersTable.id], - }), -})); - -const schema = { - usersTable, - postsTable, - commentsTable, - usersToGroupsTable, - groupsTable, - commentLikesConfig, - commentsConfig, - postsConfig, - usersToGroupsConfig, - groupsConfig, - usersConfig, -}; - -declare module 'vitest' { - export interface TestContext { - neonHttpDb: NeonHttpDatabase; - neonHttpClient: NeonQueryFunction; - } -} - -let db: NeonHttpDatabase; -let client: NeonQueryFunction; - -beforeAll(async () => { - const connectionString = process.env['NEON_CONNECTION_STRING']; - if (!connectionString) { - throw new Error('NEON_CONNECTION_STRING is not defined'); - } - - client = neon(connectionString); - db = drizzle(client, { schema, logger: ENABLE_LOGGING }); -}); - -beforeEach(async (ctx) => { - ctx.neonHttpDb = db; - ctx.neonHttpClient = client; - - await db.execute(sql`drop table if exists comment_likes`); - await db.execute(sql`drop table if exists comments`); - await db.execute(sql`drop table if exists posts`); - await db.execute(sql`drop table if exists users_to_groups`); - await db.execute(sql`drop table if exists groups`); - await db.execute(sql`drop table if exists users`); - - await db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified int not null default 0, - invited_by int references users(id) - ) - `, - ); - await db.execute( - sql` - create table groups ( - id serial primary key, - name text not null, - description text - ) - `, - ); - await db.execute( - sql` - create table users_to_groups ( - id serial, - user_id int not null references users(id), - group_id int not null references groups(id), - primary key (user_id, group_id) - ) - `, - ); - await db.execute( - sql` - create table posts ( - id serial primary key, - content text not null, - owner_id int references users(id), - created_at timestamp not null default now() - ) - `, - ); - await db.execute( - sql` - create table comments ( - id serial primary key, - content text not null, - creator int references users(id), - post_id int references posts(id), - created_at timestamp not null default now() - ) - `, - ); - await db.execute( - sql` - create table comment_likes ( - id serial primary key, - creator int references users(id), - comment_id int references comments(id), - created_at timestamp not null default now() - ) - `, - ); -}); - -afterAll(async () => { - await db.execute(sql`drop table if exists comment_likes`); - await db.execute(sql`drop table if exists comments`); - await db.execute(sql`drop table if exists posts`); - await db.execute(sql`drop table if exists users_to_groups`); - await db.execute(sql`drop table if exists groups`); - await db.execute(sql`drop table if exists users`); -}); - -test('batch api example', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ - id: usersTable.id, - invitedBy: usersTable.invitedBy, - }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.select().from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - invitedBy: number | null; - }[], - NeonHttpQueryResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - invitedBy: null, - }]); - - expect(batchResponse[1]).toMatchObject({ rows: [], rowCount: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api only relational many -test('insert + findMany', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - NeonHttpQueryResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toMatchObject({ rows: [], rowCount: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api relational many + one -test('insert + findMany + findFirst', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.query.usersTable.findFirst({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - NeonHttpQueryResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toMatchObject({ rows: [], rowCount: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 1, name: 'John', verified: 0, invitedBy: null }, - ); -}); - -test('insert + db.execute', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.execute(sql`insert into users (id, name) values (2, 'Dan')`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - NeonHttpQueryResult>, - ]>(); - - expect(batchResponse.length).eq(2); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toMatchObject({ rowAsArray: false, rows: [], rowCount: 1 }); -}); - -// batch api combined rqb + raw call -test('insert + findManyWith + db.all', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.execute(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - NeonHttpQueryResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - NeonHttpQueryResult<{ - id: number; - name: string; - verified: number; - invitedBy: number | null; - }>, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toMatchObject({ rowAsArray: true, rows: [], rowCount: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toMatchObject({ - rows: [ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ], - }); -}); - -// batch api for insert + update + select -test('insert + update + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)), - db.query.usersTable.findMany({}), - db.select().from(usersTable).where(eq(usersTable.id, 1)), - db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - NeonHttpQueryResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toMatchObject({ rows: [], rowCount: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[4]).toEqual([ - { id: 1, invitedBy: null }, - ]); -}); - -// batch api for insert + delete + select -test('insert + delete + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.delete(usersTable).where(eq(usersTable.id, 1)).returning({ id: usersTable.id, invitedBy: usersTable.invitedBy }), - db.query.usersTable.findFirst({ - columns: { - id: true, - invitedBy: true, - }, - }), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - NeonHttpQueryResult, - { - id: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toMatchObject({ rows: [], rowCount: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 2, invitedBy: null }, - ); -}); - -test('select raw', async () => { - await db.insert(usersTable).values([{ id: 1, name: 'John' }, { id: 2, name: 'Dan' }]); - const batchResponse = await db.batch([ - db.execute>(sql`select * from users`), - db.execute>(sql`select * from users where id = 1`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - NeonHttpQueryResult<{ - id: number; - name: string; - verified: number; - invited_by: number | null; - }>, - NeonHttpQueryResult<{ - id: number; - name: string; - verified: number; - invited_by: number | null; - }>, - ]>(); - - expect(batchResponse.length).eq(2); - - expect(batchResponse[0]).toMatchObject({ - rows: [ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ], - }); - - expect(batchResponse[1]).toMatchObject({ - rows: [ - { id: 1, name: 'John', verified: 0, invited_by: null }, - ], - }); -}); - -// * additionally -// batch for all neon cases, just replace simple calls with batch calls -// batch for all rqb cases, just replace simple calls with batch calls diff --git a/integration-tests/tests/__old/neon-http.test.ts b/integration-tests/tests/__old/neon-http.test.ts deleted file mode 100644 index dcb487b9d..000000000 --- a/integration-tests/tests/__old/neon-http.test.ts +++ /dev/null @@ -1,2708 +0,0 @@ -import 'dotenv/config'; - -import { neon, type NeonQueryFunction } from '@neondatabase/serverless'; -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - eq, - gt, - gte, - inArray, - lt, - name, - placeholder, - type SQL, - sql, - type SQLWrapper, - TransactionRollbackError, -} from 'drizzle-orm'; -import { drizzle, type NeonHttpDatabase } from 'drizzle-orm/neon-http'; -import { migrate } from 'drizzle-orm/neon-http/migrator'; -import { - alias, - boolean, - char, - cidr, - date, - getMaterializedViewConfig, - getViewConfig, - inet, - integer, - interval, - jsonb, - macaddr, - macaddr8, - type PgColumn, - pgEnum, - pgMaterializedView, - pgTable, - pgTableCreator, - pgView, - serial, - text, - time, - timestamp, - uuid as pgUuid, - varchar, -} from 'drizzle-orm/pg-core'; -import pg from 'pg'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, randomString } from '../utils.ts'; - -const { Client } = pg; - -const ENABLE_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const network = pgTable('network_table', { - inet: inet('inet').notNull(), - cidr: cidr('cidr').notNull(), - macaddr: macaddr('macaddr').notNull(), - macaddr8: macaddr8('macaddr8').notNull(), -}); - -const salEmp = pgTable('sal_emp', { - name: text('name'), - payByQuarter: integer('pay_by_quarter').array(), - schedule: text('schedule').array().array(), -}); - -const _tictactoe = pgTable('tictactoe', { - squares: integer('squares').array(3).array(3), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - db: NeonHttpDatabase; - ddlRunner: pg.Client; - client: NeonQueryFunction; -} - -const test = anyTest as TestFn; - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['NEON_CONNECTION_STRING']; - if (!connectionString) { - throw new Error('NEON_CONNECTION_STRING is not defined'); - } - - ctx.client = neon(connectionString); - ctx.ddlRunner = new Client(connectionString); - await ctx.ddlRunner.connect(); - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.ddlRunner?.end().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - // await ctx.ddlRunner.query(`drop schema public cascade`); - // await ctx.ddlRunner.query(`create schema public`); - await ctx.db.execute(sql`drop table if exists users cascade`); - await ctx.db.execute(sql`drop table if exists cities cascade`); - await ctx.db.execute(sql`drop table if exists users2 cascade`); - await ctx.db.execute(sql`drop table if exists course_categories cascade`); - await ctx.db.execute(sql`drop table if exists courses cascade`); - await ctx.db.execute(sql`drop table if exists orders cascade`); - await ctx.db.execute(sql`drop table if exists network_table cascade`); - await ctx.db.execute(sql`drop table if exists sal_emp cascade`); - await ctx.db.execute(sql`drop table if exists tictactoe cascade`); - - await ctx.ddlRunner.query( - ` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table cities ( - id serial primary key, - name text not null, - state char(2) - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table network_table ( - inet inet not null, - cidr cidr not null, - macaddr macaddr not null, - macaddr8 macaddr8 not null - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table sal_emp ( - name text not null, - pay_by_quarter integer[] not null, - schedule text[][] not null - ) - `, - ); - await ctx.ddlRunner.query( - ` - create table tictactoe ( - squares integer[3][3] not null - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 1000); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .delete(usersTable) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 1000); - t.deepEqual(users, [ - { id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 1000); - t.deepEqual(users, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }) - .from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('char insert', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]); -}); - -test.serial('char update', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]); -}); - -test.serial('char delete', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, []); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }) - .from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [ - { - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }, - ]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db - .insert(usersTable) - .values({ - verified: true, - name: placeholder('name'), - }) - .prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -test.serial('migrator : default migration strategy', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom schema', async (t) => { - const { db } = t.context; - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); - t.true(rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); - t.true(rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); -}); - -test.serial('migrator : migrate with custom table and custom schema', async (t) => { - const { db } = t.context; - const customTable = randomString(); - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { - migrationsFolder: './drizzle2/pg', - migrationsTable: customTable, - migrationsSchema: customSchema, - }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, - ); - t.true(rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute( - sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`, - ); - - const result = await db.execute<{ id: number; name: string }>( - sql`select id, name from "users"`, - ); - t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select() - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - state: null, - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db - .insert(courseCategoriesTable) - .values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db - .insert(coursesTable) - .values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare('query')); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: '2' }]); -}); - -test.serial('select count w/ custom mapper', async (t) => { - const { db } = t.context; - - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('network types', async (t) => { - const { db } = t.context; - - const value: typeof network.$inferSelect = { - inet: '127.0.0.1', - cidr: '192.168.100.128/25', - macaddr: '08:00:2b:01:02:03', - macaddr8: '08:00:2b:01:02:03:04:05', - }; - - await db.insert(network).values(value); - - const res = await db.select().from(network); - - t.deepEqual(res, [value]); -}); - -test.serial('array types', async (t) => { - const { db } = t.context; - - const values: typeof salEmp.$inferSelect[] = [ - { - name: 'John', - payByQuarter: [10000, 10000, 10000, 10000], - schedule: [['meeting', 'lunch'], ['training', 'presentation']], - }, - { - name: 'Carol', - payByQuarter: [20000, 25000, 25000, 25000], - schedule: [['breakfast', 'consulting'], ['meeting', 'lunch']], - }, - ]; - - await db.insert(salEmp).values(values); - - const res = await db.select().from(salEmp); - - t.deepEqual(res, values); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db - .select() - .from(users2Table) - .for('update') - .toSQL(); - - t.regex( - query.sql, - / for update$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('update', { of: [users2Table, coursesTable] }) - .toSQL(); - - t.regex( - query.sql, - / for update of "users2", "courses"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table }) - .toSQL(); - - t.regex( - query.sql, - /for no key update of "users2"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table, skipLocked: true }) - .toSQL(); - - t.regex( - query.sql, - / for no key update of "users2" skip locked$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('share', { of: users2Table, noWait: true }) - .toSQL(); - - t.regex( - query.sql, - // eslint-disable-next-line unicorn/better-regex - /for share of "users2" no wait$/, - ); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('materialized view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgMaterializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -// TODO: copy to SQLite and MySQL, add to docs -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from enum', async (t) => { - const { db } = t.context; - - const muscleEnum = pgEnum('muscle', [ - 'abdominals', - 'hamstrings', - 'adductors', - 'quadriceps', - 'biceps', - 'shoulders', - 'chest', - 'middle_back', - 'calves', - 'glutes', - 'lower_back', - 'lats', - 'triceps', - 'traps', - 'forearms', - 'neck', - 'abductors', - ]); - - const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - - const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - - const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - - const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - - const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - - const exercises = pgTable('exercises', { - id: serial('id').primaryKey(), - name: varchar('name').notNull(), - force: forceEnum('force'), - level: levelEnum('level'), - mechanic: mechanicEnum('mechanic'), - equipment: equipmentEnum('equipment'), - instructions: text('instructions'), - category: categoryEnum('category'), - primaryMuscles: muscleEnum('primary_muscles').array(), - secondaryMuscles: muscleEnum('secondary_muscles').array(), - createdAt: timestamp('created_at').notNull().default(sql`now()`), - updatedAt: timestamp('updated_at').notNull().default(sql`now()`), - }); - - await db.execute(sql`drop table if exists ${exercises}`); - await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - - await db.execute( - sql`create type ${ - name(muscleEnum.enumName) - } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, - ); - await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); - await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); - await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute( - sql`create type ${ - name(equipmentEnum.enumName) - } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, - ); - await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); - await db.execute(sql` - create table ${exercises} ( - id serial primary key, - name varchar not null, - force force, - level level, - mechanic mechanic, - equipment equipment, - instructions text, - category category, - primary_muscles muscle[], - secondary_muscles muscle[], - created_at timestamp not null default now(), - updated_at timestamp not null default now() - ) - `); - - await db.insert(exercises).values({ - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - }); - - const result = await db.select().from(exercises); - - t.deepEqual(result, [ - { - id: 1, - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - createdAt: result[0]!.createdAt, - updatedAt: result[0]!.updatedAt, - }, - ]); - - await db.execute(sql`drop table ${exercises}`); - await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('select from sql', async (t) => { - const { db } = t.context; - - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - await t.notThrowsAsync(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('all date and time columns', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - dateString: date('date_string', { mode: 'string' }).notNull(), - time: time('time', { precision: 3 }).notNull(), - datetime: timestamp('datetime').notNull(), - datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), - datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), - datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), - datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), - interval: interval('interval').notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - date_string date not null, - time time(3) not null, - datetime timestamp not null, - datetime_wtz timestamp with time zone not null, - datetime_string timestamp not null, - datetime_full_precision timestamp(6) not null, - datetime_wtz_string timestamp with time zone not null, - interval interval not null - ) - `); - - const someDatetime = new Date('2022-01-01T00:00:00.123Z'); - const fullPrecision = '2022-01-01T00:00:00.123456'; - const someTime = '23:23:12.432'; - - await db.insert(table).values({ - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01T00:00:00.123Z', - datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), - datetimeWTZString: '2022-01-01T00:00:00.123Z', - interval: '1 day', - }); - - const result = await db.select().from(table); - - Expect< - Equal<{ - id: number; - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - }[], typeof result> - >; - - Expect< - Equal<{ - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - id?: number | undefined; - }, typeof table.$inferInsert> - >; - - t.deepEqual(result.length, 1); - - t.like(result[0], { - id: 1, - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01 00:00:00.123', - datetimeFullPrecision: fullPrecision.replace('T', ' '), - datetimeWTZString: '2022-01-01 00:00:00.123+00', - interval: { - days: 1, - }, - }); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), - timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null, - timestamp_date timestamp(3) with time zone not null, - timestamp_date_2 timestamp(3) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - const timestampDate = new Date(); - const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); - - const timestampString2 = '2022-01-01 00:00:00.123456-0400'; - const timestampDate2 = new Date(); - const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); - - await db.insert(table).values([ - { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, - { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, - ]); - - const result = await db.select().from(table); - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - timestamp_date: string; - timestamp_date_2: string; - }>(sql`select * from ${table}`); - - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestamp: '2022-01-01 02:00:00.123456+00', - timestampAsDate: timestampDate, - timestampTimeZones: timestampDateWTZ, - }, - { - id: 2, - timestamp: '2022-01-01 04:00:00.123456+00', - timestampAsDate: timestampDate2, - timestampTimeZones: timestampDateWTZ2, - }, - ]); - - t.deepEqual(result2.rows, [ - { - id: 1, - timestamp_string: '2022-01-01 02:00:00.123456+00', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - { - id: 2, - timestamp_string: '2022-01-01 04:00:00.123456+00', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - ]); - - t.deepEqual( - result[0]?.timestampTimeZones.getTime(), - new Date((result2.rows[0] as any).timestamp_date_2 as any).getTime(), - ); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), - timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null, - timestamp_string2 timestamp(3) not null, - timestamp_date timestamp(3) not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456'; - const timestampString2 = '2022-01-02 00:00:00.123 -0300'; - const timestampDate = new Date('2022-01-01 00:00:00.123Z'); - - const timestampString_2 = '2022-01-01 00:00:00.123456'; - const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; - const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); - - await db.insert(table).values([ - { timestampString, timestampString2, timestampDate }, - { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, - ]); - - const result = await db.select().from(table); - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - timestamp_string2: string; - timestamp_date: string; - }>(sql`select * from ${table}`); - - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - t.deepEqual(result, [ - { - id: 1, - timestampString: timestampString, - timestampString2: '2022-01-02 00:00:00.123', - timestampDate: timestampDate, - }, - { - id: 2, - timestampString: timestampString_2, - timestampString2: '2022-01-01 00:00:00.123', - timestampDate: timestampDate2, - }, - ]); - - t.deepEqual(result2.rows, [ - { - id: 1, - timestamp_string: timestampString, - timestamp_string2: '2022-01-02 00:00:00.123', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), - }, - { - id: 2, - timestamp_string: timestampString_2, - timestamp_string2: '2022-01-01 00:00:00.123', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), - }, - ]); - - t.deepEqual((result2.rows[0] as any).timestamp_string, '2022-01-01 00:00:00.123456'); - // need to add the 'Z', otherwise javascript assumes it's in local time - t.deepEqual(new Date((result2.rows[0] as any).timestamp_date + 'Z' as any).getTime(), timestampDate.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = pgTable('products_transactions', { - id: serial('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - - const error = await t.throwsAsync(() => - db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }) - ); - - t.is(error!.message, 'No transactions support in neon-http driver'); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test.serial.skip('transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - t.deepEqual(result, []); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`, - ); - - const error = await t.throwsAsync(() => - db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }) - ); - - t.is(error!.message, 'No transactions support in neon-http driver'); - - // const result = await db.select().from(users); - - // t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial.skip('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = pgTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = pgTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('table selection with single table', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - - await db.insert(users).values({ name: 'John', cityId: 1 }); - - const result = await db.select({ users }).from(users); - - t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('set null to jsonb field', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - jsonb: jsonb('jsonb'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, jsonb jsonb)`, - ); - - const result = await db.insert(users).values({ jsonb: null }).returning(); - - t.deepEqual(result, [{ id: 1, jsonb: null }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('array operators', async (t) => { - const { db } = t.context; - - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - t.deepEqual(contains, [{ id: 3 }, { id: 5 }]); - t.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]); - t.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - t.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]); -}); diff --git a/integration-tests/tests/__old/pg-proxy.test.ts b/integration-tests/tests/__old/pg-proxy.test.ts deleted file mode 100644 index 17231b4ee..000000000 --- a/integration-tests/tests/__old/pg-proxy.test.ts +++ /dev/null @@ -1,2937 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - eq, - gt, - gte, - inArray, - lt, - name, - placeholder, - type SQL, - sql, - type SQLWrapper, -} from 'drizzle-orm'; -import { - alias, - boolean, - char, - cidr, - date, - getMaterializedViewConfig, - getTableConfig, - getViewConfig, - inet, - integer, - interval, - jsonb, - macaddr, - macaddr8, - type PgColumn, - pgEnum, - pgMaterializedView, - pgTable, - pgTableCreator, - pgView, - serial, - text, - time, - timestamp, - unique, - uniqueKeyName, - uuid as pgUuid, - varchar, -} from 'drizzle-orm/pg-core'; -import { drizzle as proxyDrizzle } from 'drizzle-orm/pg-proxy'; -import type { PgRemoteDatabase } from 'drizzle-orm/pg-proxy'; -import { migrate } from 'drizzle-orm/pg-proxy/migrator'; -import getPort from 'get-port'; -import pg from 'pg'; -import { v4 as uuid } from 'uuid'; -import type { Equal } from '../utils.ts'; -import { Expect } from '../utils.ts'; - -// eslint-disable-next-line drizzle-internal/require-entity-kind -class ServerSimulator { - constructor(private db: pg.Client) { - const { types } = pg; - - types.setTypeParser(types.builtins.TIMESTAMPTZ, (val) => val); - types.setTypeParser(types.builtins.TIMESTAMP, (val) => val); - types.setTypeParser(types.builtins.DATE, (val) => val); - types.setTypeParser(types.builtins.INTERVAL, (val) => val); - } - - async query(sql: string, params: any[], method: 'all' | 'execute') { - if (method === 'all') { - try { - const result = await this.db.query({ - text: sql, - values: params, - rowMode: 'array', - }); - - return { data: result.rows as any }; - } catch (e: any) { - return { error: e }; - } - } else if (method === 'execute') { - try { - const result = await this.db.query({ - text: sql, - values: params, - }); - - return { data: result.rows as any }; - } catch (e: any) { - return { error: e }; - } - } else { - return { error: 'Unknown method value' }; - } - } - - async migrations(queries: string[]) { - await this.db.query('BEGIN'); - try { - for (const query of queries) { - await this.db.query(query); - } - await this.db.query('COMMIT'); - } catch (e) { - await this.db.query('ROLLBACK'); - throw e; - } - - return {}; - } -} - -const { Client } = pg; - -const ENABLE_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id' as string).primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const network = pgTable('network_table', { - inet: inet('inet').notNull(), - cidr: cidr('cidr').notNull(), - macaddr: macaddr('macaddr').notNull(), - macaddr8: macaddr8('macaddr8').notNull(), -}); - -const salEmp = pgTable('sal_emp', { - name: text('name'), - payByQuarter: integer('pay_by_quarter').array(), - schedule: text('schedule').array().array(), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - docker: Docker; - pgContainer: Docker.Container; - db: PgRemoteDatabase; - client: pg.Client; - serverSimulator: ServerSimulator; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 5432 }); - const image = 'postgres:14'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.pgContainer = await docker.createContainer({ - Image: image, - Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '5432/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.pgContainer.start(); - - return `postgres://postgres:postgres@localhost:${port}/postgres`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['PG_CONNECTION_STRING'] ?? (await createDockerDB(ctx)); - - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = new Client(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to Postgres'); - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); - throw lastError; - } - - ctx.serverSimulator = new ServerSimulator(ctx.client); - - ctx.db = proxyDrizzle(async (sql, params, method) => { - try { - const response = await ctx.serverSimulator.query(sql, params, method); - - if (response.error !== undefined) { - throw response.error; - } - - return { rows: response.data }; - } catch (e: any) { - console.error('Error from pg proxy server:', e.message); - throw e; - } - }, { - logger: ENABLE_LOGGING, - }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.db.execute( - sql` - create table cities ( - id serial primary key, - name text not null, - state char(2) - ) - `, - ); - await ctx.db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.db.execute( - sql` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - ); - await ctx.db.execute( - sql` - create table network_table ( - inet inet not null, - cidr cidr not null, - macaddr macaddr not null, - macaddr8 macaddr8 not null - ) - `, - ); - await ctx.db.execute( - sql` - create table sal_emp ( - name text not null, - pay_by_quarter integer[] not null, - schedule text[][] not null - ) - `, - ); - await ctx.db.execute( - sql` - create table tictactoe ( - squares integer[3][3] not null - ) - `, - ); -}); - -test.serial('table configs: unique third param', async (t) => { - const cities1Table = pgTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), - }, (t) => ({ - f: unique('custom_name').on(t.name, t.state).nullsNotDistinct(), - f1: unique('custom_name1').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - t.assert(tableConfig.uniqueConstraints.length === 2); - - t.assert(tableConfig.uniqueConstraints[0]?.name === 'custom_name'); - t.assert(tableConfig.uniqueConstraints[0]?.nullsNotDistinct); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); - - t.assert(tableConfig.uniqueConstraints[1]?.name, 'custom_name1'); - t.assert(!tableConfig.uniqueConstraints[1]?.nullsNotDistinct); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); -}); - -test.serial('table configs: unique in column', async (t) => { - const cities1Table = pgTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - state: char('state', { length: 2 }).unique('custom'), - field: char('field', { length: 2 }).unique('custom_field', { nulls: 'not distinct' }), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - t.assert(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name])); - t.assert(columnName?.isUnique); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - t.assert(columnState?.uniqueName === 'custom'); - t.assert(columnState?.isUnique); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - t.assert(columnField?.uniqueName === 'custom_field'); - t.assert(columnField?.isUnique); - t.assert(columnField?.uniqueType === 'not distinct'); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - const insertedOrder = await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }) - .returning(); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(insertedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .delete(usersTable) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }) - .from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('char insert', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]); -}); - -test.serial('char update', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]); -}); - -test.serial('char delete', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, []); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }) - .from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [ - { - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }, - ]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db - .insert(usersTable) - .values({ - verified: true, - name: placeholder('name'), - }) - .prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -// TODO change tests to new structure -test.serial('migrator', async (t) => { - const { db, serverSimulator } = t.context; - - await db.execute(sql`drop table if exists users`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, async (queries) => { - try { - await serverSimulator.migrations(queries); - } catch (e) { - console.error(e); - throw new Error('Proxy server cannot run migrations'); - } - }, { migrationsFolder: './drizzle2/pg-proxy/first' }); - - await t.notThrowsAsync(async () => { - await db.insert(usersTable).values({ name: 'John' }); - }); - - await t.throwsAsync(async () => { - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - }, { - message: 'relation "users12" does not exist', - }); - - await migrate(db, async (queries) => { - try { - await serverSimulator.migrations(queries); - } catch (e) { - console.error(e); - throw new Error('Proxy server cannot run migrations'); - } - }, { migrationsFolder: './drizzle2/pg-proxy/second' }); - - await t.notThrowsAsync(async () => { - await db.insert(usersTable).values({ name: 'John' }); - }); - - await t.notThrowsAsync(async () => { - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - }); - - await db.execute(sql`drop table users`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute( - sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`, - ); - - const result = await db.execute<{ id: number; name: string }>( - sql`select id, name from "users"`, - ); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted, [{ id: 1, name: 'John' }]); -}); - -test.serial('Query check: Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default)', - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default), (default, default, default)', - params: [], - }); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select() - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - state: null, - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db - .insert(courseCategoriesTable) - .values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db - .insert(coursesTable) - .values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare('query')); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: '2' }]); -}); - -test.serial('select count w/ custom mapper', async (t) => { - const { db } = t.context; - - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('network types', async (t) => { - const { db } = t.context; - - const value: typeof network.$inferSelect = { - inet: '127.0.0.1', - cidr: '192.168.100.128/25', - macaddr: '08:00:2b:01:02:03', - macaddr8: '08:00:2b:01:02:03:04:05', - }; - - await db.insert(network).values(value); - - const res = await db.select().from(network); - - t.deepEqual(res, [value]); -}); - -test.serial('array types', async (t) => { - const { db } = t.context; - - const values: typeof salEmp.$inferSelect[] = [ - { - name: 'John', - payByQuarter: [10000, 10000, 10000, 10000], - schedule: [['meeting', 'lunch'], ['training', 'presentation']], - }, - { - name: 'Carol', - payByQuarter: [20000, 25000, 25000, 25000], - schedule: [['breakfast', 'consulting'], ['meeting', 'lunch']], - }, - ]; - - await db.insert(salEmp).values(values); - - const res = await db.select().from(salEmp); - - t.deepEqual(res, values); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db - .select() - .from(users2Table) - .for('update') - .toSQL(); - - t.regex( - query.sql, - / for update$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('update', { of: [users2Table, coursesTable] }) - .toSQL(); - - t.regex( - query.sql, - / for update of "users2", "courses"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table }) - .toSQL(); - - t.regex( - query.sql, - /for no key update of "users2"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table, skipLocked: true }) - .toSQL(); - - t.regex( - query.sql, - / for no key update of "users2" skip locked$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('share', { of: users2Table, noWait: true }) - .toSQL(); - - t.regex( - query.sql, - // eslint-disable-next-line unicorn/better-regex - /for share of "users2" no wait$/, - ); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('materialized view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgMaterializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -// TODO: copy to SQLite and MySQL, add to docs -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from enum', async (t) => { - const { db } = t.context; - - const muscleEnum = pgEnum('muscle', [ - 'abdominals', - 'hamstrings', - 'adductors', - 'quadriceps', - 'biceps', - 'shoulders', - 'chest', - 'middle_back', - 'calves', - 'glutes', - 'lower_back', - 'lats', - 'triceps', - 'traps', - 'forearms', - 'neck', - 'abductors', - ]); - - const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - - const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - - const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - - const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - - const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - - const exercises = pgTable('exercises', { - id: serial('id').primaryKey(), - name: varchar('name').notNull(), - force: forceEnum('force'), - level: levelEnum('level'), - mechanic: mechanicEnum('mechanic'), - equipment: equipmentEnum('equipment'), - instructions: text('instructions'), - category: categoryEnum('category'), - primaryMuscles: muscleEnum('primary_muscles').array(), - secondaryMuscles: muscleEnum('secondary_muscles').array(), - createdAt: timestamp('created_at').notNull().default(sql`now()`), - updatedAt: timestamp('updated_at').notNull().default(sql`now()`), - }); - - await db.execute(sql`drop table if exists ${exercises}`); - await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - - await db.execute( - sql`create type ${ - name(muscleEnum.enumName) - } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, - ); - await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); - await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); - await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute( - sql`create type ${ - name(equipmentEnum.enumName) - } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, - ); - await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); - await db.execute(sql` - create table ${exercises} ( - id serial primary key, - name varchar not null, - force force, - level level, - mechanic mechanic, - equipment equipment, - instructions text, - category category, - primary_muscles muscle[], - secondary_muscles muscle[], - created_at timestamp not null default now(), - updated_at timestamp not null default now() - ) - `); - - await db.insert(exercises).values({ - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - }); - - const result = await db.select().from(exercises); - - t.deepEqual(result, [ - { - id: 1, - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - createdAt: result[0]!.createdAt, - updatedAt: result[0]!.updatedAt, - }, - ]); - - await db.execute(sql`drop table ${exercises}`); - await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('select from sql', async (t) => { - const { db } = t.context; - - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - await t.notThrowsAsync(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('all date and time columns', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - dateString: date('date_string', { mode: 'string' }).notNull(), - time: time('time', { precision: 3 }).notNull(), - datetime: timestamp('datetime').notNull(), - datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), - datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), - datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), - datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), - interval: interval('interval').notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - date_string date not null, - time time(3) not null, - datetime timestamp not null, - datetime_wtz timestamp with time zone not null, - datetime_string timestamp not null, - datetime_full_precision timestamp(6) not null, - datetime_wtz_string timestamp with time zone not null, - interval interval not null - ) - `); - - const someDatetime = new Date('2022-01-01T00:00:00.123Z'); - const fullPrecision = '2022-01-01T00:00:00.123456Z'; - const someTime = '23:23:12.432'; - - await db.insert(table).values({ - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01T00:00:00.123Z', - datetimeFullPrecision: fullPrecision, - datetimeWTZString: '2022-01-01T00:00:00.123Z', - interval: '1 day', - }); - - const result = await db.select().from(table); - - Expect< - Equal<{ - id: number; - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - }[], typeof result> - >; - - Expect< - Equal<{ - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - id?: number | undefined; - }, typeof table.$inferInsert> - >; - - t.deepEqual(result, [ - { - id: 1, - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01 00:00:00.123', - datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), - datetimeWTZString: '2022-01-01 00:00:00.123+00', - interval: '1 day', - }, - ]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone second case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date(); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as date and check that timezones are the same - // There is no way to check timezone in Date object, as it is always represented internally in UTC - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); - - // 3. Compare both dates - t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC - const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones - - // 1. Insert date as new dates with different time zones - await db.insert(table).values([ - { timestamp: insertedDate }, - { timestamp: insertedDate2 }, - ]); - - // 2, Select and compare both dates - const result = await db.select().from(table); - - t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format without timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone second case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01T02:00:00.123456-02' }, - ]); - - // 2, Select as raw query and check that values are the same - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123+04'); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as raw query as string - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC - t.deepEqual(new Date(result[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -// TODO: implement transaction -// test.serial('transaction', async (t) => { -// const { db } = t.context; - -// const users = pgTable('users_transactions', { -// id: serial('id').primaryKey(), -// balance: integer('balance').notNull(), -// }); -// const products = pgTable('products_transactions', { -// id: serial('id').primaryKey(), -// price: integer('price').notNull(), -// stock: integer('stock').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); -// await db.execute(sql`drop table if exists ${products}`); - -// await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); -// await db.execute( -// sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, -// ); - -// const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); -// const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - -// await db.transaction(async (tx) => { -// await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); -// await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); -// }); - -// const result = await db.select().from(users); - -// t.deepEqual(result, [{ id: 1, balance: 90 }]); - -// await db.execute(sql`drop table ${users}`); -// await db.execute(sql`drop table ${products}`); -// }); - -// TODO: implement transaction -// test.serial('transaction rollback', async (t) => { - -// const { db } = t.context; - -// const users = pgTable('users_transactions_rollback', { -// id: serial('id').primaryKey(), -// balance: integer('balance').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); - -// await db.execute( -// sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`, -// ); - -// await t.throwsAsync(async () => -// await db.transaction(async (tx) => { -// await tx.insert(users).values({ balance: 100 }); -// tx.rollback(); -// }), new TransactionRollbackError()); - -// const result = await db.select().from(users); - -// t.deepEqual(result, []); - -// await db.execute(sql`drop table ${users}`); -// }); - -// TODO: implement transaction -// test.serial('nested transaction', async (t) => { -// const { db } = t.context; - -// const users = pgTable('users_nested_transactions', { -// id: serial('id').primaryKey(), -// balance: integer('balance').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); - -// await db.execute( -// sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`, -// ); - -// await db.transaction(async (tx) => { -// await tx.insert(users).values({ balance: 100 }); - -// await tx.transaction(async (tx) => { -// await tx.update(users).set({ balance: 200 }); -// }); -// }); - -// const result = await db.select().from(users); - -// t.deepEqual(result, [{ id: 1, balance: 200 }]); - -// await db.execute(sql`drop table ${users}`); -// }); - -// TODO: implement transaction -// test.serial('nested transaction rollback', async (_t) => { -// const { db } = t.context; - -// const users = pgTable('users_nested_transactions_rollback', { -// id: serial('id').primaryKey(), -// balance: integer('balance').notNull(), -// }); - -// await db.execute(sql`drop table if exists ${users}`); - -// await db.execute( -// sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`, -// ); - -// await db.transaction(async (tx) => { -// await tx.insert(users).values({ balance: 100 }); - -// await t.throwsAsync(async () => -// await tx.transaction(async (tx) => { -// await tx.update(users).set({ balance: 200 }); -// tx.rollback(); -// }), new TransactionRollbackError()); -// }); - -// const result = await db.select().from(users); - -// t.deepEqual(result, [{ id: 1, balance: 100 }]); - -// await db.execute(sql`drop table ${users}`); -// }); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = pgTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = pgTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('table selection with single table', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - - await db.insert(users).values({ name: 'John', cityId: 1 }); - - const result = await db.select({ users }).from(users); - - t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('set null to jsonb field', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - jsonb: jsonb('jsonb'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, jsonb jsonb)`, - ); - - const result = await db.insert(users).values({ jsonb: null }).returning(); - - t.deepEqual(result, [{ id: 1, jsonb: null }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('array operators', async (t) => { - const { db } = t.context; - - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - t.deepEqual(contains, [{ id: 3 }, { id: 5 }]); - t.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]); - t.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - t.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]); -}); diff --git a/integration-tests/tests/__old/pg-schema.test.ts b/integration-tests/tests/__old/pg-schema.test.ts deleted file mode 100644 index fdcb9d2b6..000000000 --- a/integration-tests/tests/__old/pg-schema.test.ts +++ /dev/null @@ -1,994 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { asc, eq, Name, placeholder, sql } from 'drizzle-orm'; -import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; -import { drizzle } from 'drizzle-orm/node-postgres'; -import { - alias, - boolean, - char, - getMaterializedViewConfig, - getViewConfig, - integer, - jsonb, - PgDialect, - pgSchema, - pgTable, - pgTableCreator, - serial, - text, - timestamp, -} from 'drizzle-orm/pg-core'; -import getPort from 'get-port'; -import pg from 'pg'; -import { v4 as uuid } from 'uuid'; - -const { Client } = pg; - -const ENABLE_LOGGING = false; - -const mySchema = pgSchema('mySchema'); - -const usersTable = mySchema.table('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const citiesTable = mySchema.table('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const users2Table = mySchema.table('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const publicUsersTable = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -interface Context { - docker: Docker; - pgContainer: Docker.Container; - db: NodePgDatabase; - client: pg.Client; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 5432 }); - const image = 'postgres:14'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.pgContainer = await docker.createContainer({ - Image: image, - Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '5432/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.pgContainer.start(); - - return `postgres://postgres:postgres@localhost:${port}/postgres`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['PG_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = new Client(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to Postgres'); - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema if exists public cascade`); - await ctx.db.execute(sql`drop schema if exists ${mySchema} cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql`create schema ${mySchema}`, - ); - await ctx.db.execute( - sql` - create table ${usersTable} ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.db.execute( - sql` - create table ${citiesTable} ( - id serial primary key, - name text not null, - state char(2) - ) - `, - ); - await ctx.db.execute( - sql` - create table ${users2Table} ( - id serial primary key, - name text not null, - city_id integer references "mySchema".cities(id) - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "mySchema"."users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from "mySchema"."users"`); - t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${new Name( - usersTable.name.name, - )}) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "mySchema"."users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "mySchema"."users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id","name") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "mySchema"."users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "mySchema"."users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing(); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('select from tables with same name from different schema using alias', async (t) => { - const { db } = t.context; - - await db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - - await db.insert(usersTable).values({ id: 10, name: 'Ivan' }); - await db.insert(publicUsersTable).values({ id: 11, name: 'Hans' }); - - const customerAlias = alias(publicUsersTable, 'customer'); - - const result = await db - .select().from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(customerAlias.id, 11)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - verified: false, - jsonb: null, - createdAt: result[0]!.users.createdAt, - }, - customer: { - id: 11, - name: 'Hans', - verified: false, - jsonb: null, - createdAt: result[0]!.customer!.createdAt, - }, - }]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = mySchema.view('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mySchema.view('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mySchema.view('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('materialized view', async (t) => { - const { db } = t.context; - - const newYorkers1 = mySchema.materializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = mySchema.materializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = mySchema.materializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -test.serial('enum', async (t) => { - const { db } = t.context; - - const colors = mySchema.enum('colors', ['red', 'green', 'blue']); - - t.deepEqual(colors.schema, 'mySchema'); - - const { sql: query } = new PgDialect().sqlToQuery(sql`${colors}`); - t.deepEqual(query, '"mySchema"."colors"'); - - await db.execute(sql`create type ${colors} as enum ('red', 'green', 'blue')`); - - const result = await db.execute<{ enum_range: string }>(sql`select enum_range(null::${colors})`); - t.deepEqual(result.rows, [{ enum_range: '{red,green,blue}' }]); - - await db.execute(sql`drop type ${colors}`); -}); diff --git a/integration-tests/tests/__old/pg.custom.test.ts b/integration-tests/tests/__old/pg.custom.test.ts deleted file mode 100644 index faa9f8501..000000000 --- a/integration-tests/tests/__old/pg.custom.test.ts +++ /dev/null @@ -1,842 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { asc, eq, name, placeholder, sql } from 'drizzle-orm'; -import type { NodePgDatabase } from 'drizzle-orm/node-postgres'; -import { drizzle } from 'drizzle-orm/node-postgres'; -import { migrate } from 'drizzle-orm/node-postgres/migrator'; -import { alias, customType, pgTable, pgTableCreator, serial, text } from 'drizzle-orm/pg-core'; -import getPort from 'get-port'; -import pg from 'pg'; -import { v4 as uuid } from 'uuid'; -import { randomString } from '../utils.ts'; - -const { Client } = pg; - -const customSerial = customType<{ data: number; notNull: true; default: true }>({ - dataType() { - return 'serial'; - }, -}); - -const customText = customType<{ data: string }>({ - dataType() { - return 'text'; - }, -}); - -const customBoolean = customType<{ data: boolean }>({ - dataType() { - return 'boolean'; - }, -}); - -const customJsonb = (name: string) => - customType<{ data: TData; driverData: string }>({ - dataType() { - return 'jsonb'; - }, - toDriver(value: TData): string { - return JSON.stringify(value); - }, - })(name); - -const customTimestamp = customType< - { data: Date; driverData: string; config: { withTimezone: boolean; precision?: number } } ->({ - dataType(config) { - const precision = config?.precision === undefined ? '' : ` (${config.precision})`; - return `timestamp${precision}${config?.withTimezone ? ' with time zone' : ''}`; - }, - fromDriver(value: string): Date { - return new Date(value); - }, -}); - -const usersTable = pgTable('users', { - id: customSerial('id').primaryKey(), - name: customText('name').notNull(), - verified: customBoolean('verified').notNull().default(false), - jsonb: customJsonb('jsonb'), - createdAt: customTimestamp('created_at', { withTimezone: true }).notNull().default(sql`now()`), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - docker: Docker; - pgContainer: Docker.Container; - db: NodePgDatabase; - client: pg.Client; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 5432 }); - const image = 'postgres:14'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.pgContainer = await docker.createContainer({ - Image: image, - Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '5432/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.pgContainer.start(); - - return `postgres://postgres:postgres@localhost:${port}/postgres`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['PG_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = new Client(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to Postgres'); - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: false }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -test.serial('migrator : default migration strategy', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom schema', async (t) => { - const { db } = t.context; - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); - t.true(rowCount! > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); - t.true(rowCount! > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); -}); - -test.serial('migrator : migrate with custom table and custom schema', async (t) => { - const { db } = t.context; - const customTable = randomString(); - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { - migrationsFolder: './drizzle2/pg', - migrationsTable: customTable, - migrationsSchema: customSchema, - }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, - ); - t.true(rowCount! > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from "users"`); - t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name(usersTable.name.name) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing(); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); diff --git a/integration-tests/tests/__old/pg.test.ts b/integration-tests/tests/__old/pg.test.ts deleted file mode 100644 index 3370396b2..000000000 --- a/integration-tests/tests/__old/pg.test.ts +++ /dev/null @@ -1,4368 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - avg, - avgDistinct, - count, - countDistinct, - eq, - exists, - getTableColumns, - gt, - gte, - ilike, - inArray, - lt, - max, - min, - name, - or, - placeholder, - type SQL, - sql, - type SQLWrapper, - sum, - sumDistinct, - TransactionRollbackError, -} from 'drizzle-orm'; -import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres'; -import { migrate } from 'drizzle-orm/node-postgres/migrator'; -import { - alias, - boolean, - char, - cidr, - date, - except, - exceptAll, - foreignKey, - getMaterializedViewConfig, - getTableConfig, - getViewConfig, - // index, - inet, - integer, - intersect, - intersectAll, - interval, - jsonb, - macaddr, - macaddr8, - numeric, - type PgColumn, - pgEnum, - pgMaterializedView, - pgSchema, - pgTable, - pgTableCreator, - pgView, - primaryKey, - serial, - text, - time, - timestamp, - union, - unionAll, - unique, - uniqueKeyName, - uuid as pgUuid, - varchar, -} from 'drizzle-orm/pg-core'; -import getPort from 'get-port'; -import pg from 'pg'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, randomString } from '../utils.ts'; - -const { Client } = pg; - -const ENABLE_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id' as string).primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const usersOnUpdate = pgTable('users_on_update', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), - updatedAt: timestamp('updated_at', { mode: 'date', precision: 3 }).$onUpdate(() => new Date()), - alwaysNull: text('always_null').$type().$onUpdate(() => null), - // uppercaseName: text('uppercase_name').$onUpdateFn(() => sql`upper(name)`), looks like this is not supported in pg -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const cities2Table = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const network = pgTable('network_table', { - inet: inet('inet').notNull(), - cidr: cidr('cidr').notNull(), - macaddr: macaddr('macaddr').notNull(), - macaddr8: macaddr8('macaddr8').notNull(), -}); - -const salEmp = pgTable('sal_emp', { - name: text('name'), - payByQuarter: integer('pay_by_quarter').array(), - schedule: text('schedule').array().array(), -}); - -const _tictactoe = pgTable('tictactoe', { - squares: integer('squares').array(3).array(3), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -// To test aggregate functions -const aggregateTable = pgTable('aggregate_table', { - id: serial('id').notNull(), - name: text('name').notNull(), - a: integer('a'), - b: integer('b'), - c: integer('c'), - nullOnly: integer('null_only'), -}); - -interface Context { - docker: Docker; - pgContainer: Docker.Container; - db: NodePgDatabase; - client: pg.Client; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 5432 }); - const image = 'postgres:14'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.pgContainer = await docker.createContainer({ - Image: image, - Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '5432/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.pgContainer.start(); - - return `postgres://postgres:postgres@localhost:${port}/postgres`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['PG_CONNECTION_STRING'] ?? (await createDockerDB(ctx)); - - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = new Client(connectionString); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to Postgres'); - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.db.execute( - sql` - create table cities ( - id serial primary key, - name text not null, - state char(2) - ) - `, - ); - await ctx.db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.db.execute( - sql` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - ); - await ctx.db.execute( - sql` - create table network_table ( - inet inet not null, - cidr cidr not null, - macaddr macaddr not null, - macaddr8 macaddr8 not null - ) - `, - ); - await ctx.db.execute( - sql` - create table sal_emp ( - name text not null, - pay_by_quarter integer[] not null, - schedule text[][] not null - ) - `, - ); - await ctx.db.execute( - sql` - create table tictactoe ( - squares integer[3][3] not null - ) - `, - ); -}); - -async function setupSetOperationTest(db: NodePgDatabase) { - await db.execute(sql`drop table if exists users2`); - await db.execute(sql`drop table if exists cities`); - await db.execute( - sql` - create table cities ( - id serial primary key, - name text not null - ) - `, - ); - await db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - - await db.insert(cities2Table).values([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - await db.insert(users2Table).values([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 2 }, - { id: 3, name: 'Jack', cityId: 3 }, - { id: 4, name: 'Peter', cityId: 3 }, - { id: 5, name: 'Ben', cityId: 2 }, - { id: 6, name: 'Jill', cityId: 1 }, - { id: 7, name: 'Mary', cityId: 2 }, - { id: 8, name: 'Sally', cityId: 1 }, - ]); -} - -async function setupAggregateFunctionsTest(db: NodePgDatabase) { - await db.execute(sql`drop table if exists "aggregate_table"`); - await db.execute( - sql` - create table "aggregate_table" ( - "id" serial not null, - "name" text not null, - "a" integer, - "b" integer, - "c" integer, - "null_only" integer - ); - `, - ); - await db.insert(aggregateTable).values([ - { name: 'value 1', a: 5, b: 10, c: 20 }, - { name: 'value 1', a: 5, b: 20, c: 30 }, - { name: 'value 2', a: 10, b: 50, c: 60 }, - { name: 'value 3', a: 20, b: 20, c: null }, - { name: 'value 4', a: null, b: 90, c: 120 }, - { name: 'value 5', a: 80, b: 10, c: null }, - { name: 'value 6', a: null, b: null, c: 150 }, - ]); -} - -test.serial('table configs: unique third param', async (t) => { - const cities1Table = pgTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), - }, (t) => ({ - f: unique('custom_name').on(t.name, t.state).nullsNotDistinct(), - f1: unique('custom_name1').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - t.assert(tableConfig.uniqueConstraints.length === 2); - - t.assert(tableConfig.uniqueConstraints[0]?.name === 'custom_name'); - t.assert(tableConfig.uniqueConstraints[0]?.nullsNotDistinct); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); - - t.assert(tableConfig.uniqueConstraints[1]?.name, 'custom_name1'); - t.assert(!tableConfig.uniqueConstraints[1]?.nullsNotDistinct); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); -}); - -test.serial('table configs: unique in column', async (t) => { - const cities1Table = pgTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - state: char('state', { length: 2 }).unique('custom'), - field: char('field', { length: 2 }).unique('custom_field', { nulls: 'not distinct' }), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - t.assert(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name])); - t.assert(columnName?.isUnique); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - t.assert(columnState?.uniqueName === 'custom'); - t.assert(columnState?.isUnique); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - t.assert(columnField?.uniqueName === 'custom_field'); - t.assert(columnField?.isUnique); - t.assert(columnField?.uniqueType === 'not distinct'); -}); - -test.serial('table config: foreign keys name', async (t) => { - const table = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: foreignKey({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.foreignKeys.length, 1); - t.is(tableConfig.foreignKeys[0]!.getName(), 'custom_fk'); -}); - -test.serial('table config: primary keys name', async (t) => { - const table = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: primaryKey({ columns: [t.id, t.name], name: 'custom_pk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.primaryKeys.length, 1); - t.is(tableConfig.primaryKeys[0]!.getName(), 'custom_pk'); -}); - -// test.serial('table configs: all possible index properties', async () => { -// const cities1Table = pgTable('cities1', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// state: char('state', { length: 2 }), -// }, (t) => ({ -// f: index('custom_name').using('hnsw', sql`${t.name} vector_ip_ops`, t.state.desc()), -// f4: index('custom_name').on(sql`${t.name} vector_ip_ops`, t.state.desc().nullsLast()).where(sql``).with({ -// length: 12, -// }), -// })); - -// const tableConfig = getTableConfig(cities1Table); - -// console.log(tableConfig.indexes[0]?.config.columns); -// }); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - const insertedOrder = await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }) - .returning(); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(insertedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - age: integer('age').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text, age integer)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John', age: 24 }, - { id: 1, name: 'John', age: 24 }, - { id: 2, name: 'John', age: 25 }, - { id: 1, name: 'Jane', age: 24 }, - { id: 1, name: 'Jane', age: 26 }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - const users4 = await db.selectDistinctOn([usersDistinctTable.id, usersDistinctTable.age]).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.id, usersDistinctTable.age); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [ - { id: 1, name: 'Jane', age: 24 }, - { id: 1, name: 'Jane', age: 26 }, - { id: 1, name: 'John', age: 24 }, - { id: 2, name: 'John', age: 25 }, - ]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); - - t.deepEqual(users4, [ - { id: 1, name: 'John', age: 24 }, - { id: 1, name: 'Jane', age: 26 }, - { id: 2, name: 'John', age: 25 }, - ]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .delete(usersTable) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }) - .from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('char insert', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]); -}); - -test.serial('char update', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]); -}); - -test.serial('char delete', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, []); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with exists', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const user = alias(usersTable, 'user'); - const result = await db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }) - .from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [ - { - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }, - ]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db - .insert(usersTable) - .values({ - verified: true, - name: placeholder('name'), - }) - .prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -// TODO change tests to new structure -test.serial('migrator : default migration strategy', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom schema', async (t) => { - const { db } = t.context; - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); - t.true(rowCount && rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); - t.true(rowCount && rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); -}); - -test.serial('migrator : migrate with custom table and custom schema', async (t) => { - const { db } = t.context; - const customTable = randomString(); - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { - migrationsFolder: './drizzle2/pg', - migrationsTable: customTable, - migrationsSchema: customSchema, - }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, - ); - t.true(rowCount && rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute( - sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`, - ); - - const result = await db.execute<{ id: number; name: string }>( - sql`select id, name from "users"`, - ); - t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('Query check: Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default)', - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default), (default, default, default)', - params: [], - }); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select() - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - state: null, - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db - .insert(courseCategoriesTable) - .values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db - .insert(coursesTable) - .values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result1 = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - const result2 = await db - .with(regionalSales, topRegions) - .selectDistinct({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - const result3 = await db - .with(regionalSales, topRegions) - .selectDistinctOn([orders.region], { - region: orders.region, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region) - .orderBy(orders.region); - - t.deepEqual(result1, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); - t.deepEqual(result2, result1); - t.deepEqual(result3, [ - { - region: 'Europe', - productUnits: 8, - productSales: 80, - }, - { - region: 'US', - productUnits: 16, - productSales: 160, - }, - ]); -}); - -test.serial('with ... update', async (t) => { - const { db } = t.context; - - const products = pgTable('products', { - id: serial('id').primaryKey(), - price: numeric('price').notNull(), - cheap: boolean('cheap').notNull().default(false), - }); - - await db.execute(sql`drop table if exists ${products}`); - await db.execute(sql` - create table ${products} ( - id serial primary key, - price numeric not null, - cheap boolean not null default false - ) - `); - - await db.insert(products).values([ - { price: '10.99' }, - { price: '25.85' }, - { price: '32.99' }, - { price: '2.50' }, - { price: '4.59' }, - ]); - - const averagePrice = db - .$with('average_price') - .as( - db - .select({ - value: sql`avg(${products.price})`.as('value'), - }) - .from(products), - ); - - const result = await db - .with(averagePrice) - .update(products) - .set({ - cheap: true, - }) - .where(lt(products.price, sql`(select * from ${averagePrice})`)) - .returning({ - id: products.id, - }); - - t.deepEqual(result, [ - { id: 1 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test.serial('with ... insert', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - username: text('username').notNull(), - admin: boolean('admin').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (username text not null, admin boolean not null default false)`); - - const userCount = db - .$with('user_count') - .as( - db - .select({ - value: sql`count(*)`.as('value'), - }) - .from(users), - ); - - const result = await db - .with(userCount) - .insert(users) - .values([ - { username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }, - ]) - .returning({ - admin: users.admin, - }); - - t.deepEqual(result, [{ admin: true }]); -}); - -test.serial('with ... delete', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const averageAmount = db - .$with('average_amount') - .as( - db - .select({ - value: sql`avg(${orders.amount})`.as('value'), - }) - .from(orders), - ); - - const result = await db - .with(averageAmount) - .delete(orders) - .where(gt(orders.amount, sql`(select * from ${averageAmount})`)) - .returning({ - id: orders.id, - }); - - t.deepEqual(result, [ - { id: 6 }, - { id: 7 }, - { id: 8 }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare('query')); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: '2' }]); -}); - -test.serial('select count w/ custom mapper', async (t) => { - const { db } = t.context; - - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('network types', async (t) => { - const { db } = t.context; - - const value: typeof network.$inferSelect = { - inet: '127.0.0.1', - cidr: '192.168.100.128/25', - macaddr: '08:00:2b:01:02:03', - macaddr8: '08:00:2b:01:02:03:04:05', - }; - - await db.insert(network).values(value); - - const res = await db.select().from(network); - - t.deepEqual(res, [value]); -}); - -test.serial('array types', async (t) => { - const { db } = t.context; - - const values: typeof salEmp.$inferSelect[] = [ - { - name: 'John', - payByQuarter: [10000, 10000, 10000, 10000], - schedule: [['meeting', 'lunch'], ['training', 'presentation']], - }, - { - name: 'Carol', - payByQuarter: [20000, 25000, 25000, 25000], - schedule: [['breakfast', 'consulting'], ['meeting', 'lunch']], - }, - ]; - - await db.insert(salEmp).values(values); - - const res = await db.select().from(salEmp); - - t.deepEqual(res, values); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db - .select() - .from(users2Table) - .for('update') - .toSQL(); - - t.regex( - query.sql, - / for update$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('update', { of: [users2Table, coursesTable] }) - .toSQL(); - - t.regex( - query.sql, - / for update of "users2", "courses"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table }) - .toSQL(); - - t.regex( - query.sql, - /for no key update of "users2"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table, skipLocked: true }) - .toSQL(); - - t.regex( - query.sql, - / for no key update of "users2" skip locked$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('share', { of: users2Table, noWait: true }) - .toSQL(); - - t.regex( - query.sql, - // eslint-disable-next-line unicorn/better-regex - /for share of "users2" no wait$/, - ); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('materialized view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgMaterializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -test.serial('select from existing view', async (t) => { - const { db } = t.context; - - const schema = pgSchema('test_schema'); - - const newYorkers = schema.view('new_yorkers', { - id: integer('id').notNull(), - }).existing(); - - await db.execute(sql`drop schema if exists ${schema} cascade`); - await db.execute(sql`create schema ${schema}`); - await db.execute(sql`create view ${newYorkers} as select id from ${usersTable}`); - - await db.insert(usersTable).values({ id: 100, name: 'John' }); - - const result = await db.select({ - id: usersTable.id, - }).from(usersTable).innerJoin(newYorkers, eq(newYorkers.id, usersTable.id)); - - t.deepEqual(result, [{ id: 100 }]); -}); - -// TODO: copy to SQLite and MySQL, add to docs -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from enum', async (t) => { - const { db } = t.context; - - const muscleEnum = pgEnum('muscle', [ - 'abdominals', - 'hamstrings', - 'adductors', - 'quadriceps', - 'biceps', - 'shoulders', - 'chest', - 'middle_back', - 'calves', - 'glutes', - 'lower_back', - 'lats', - 'triceps', - 'traps', - 'forearms', - 'neck', - 'abductors', - ]); - - const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - - const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - - const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - - const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - - const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - - const exercises = pgTable('exercises', { - id: serial('id').primaryKey(), - name: varchar('name').notNull(), - force: forceEnum('force'), - level: levelEnum('level'), - mechanic: mechanicEnum('mechanic'), - equipment: equipmentEnum('equipment'), - instructions: text('instructions'), - category: categoryEnum('category'), - primaryMuscles: muscleEnum('primary_muscles').array(), - secondaryMuscles: muscleEnum('secondary_muscles').array(), - createdAt: timestamp('created_at').notNull().default(sql`now()`), - updatedAt: timestamp('updated_at').notNull().default(sql`now()`), - }); - - await db.execute(sql`drop table if exists ${exercises}`); - await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - - await db.execute( - sql`create type ${ - name(muscleEnum.enumName) - } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, - ); - await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); - await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); - await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute( - sql`create type ${ - name(equipmentEnum.enumName) - } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, - ); - await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); - await db.execute(sql` - create table ${exercises} ( - id serial primary key, - name varchar not null, - force force, - level level, - mechanic mechanic, - equipment equipment, - instructions text, - category category, - primary_muscles muscle[], - secondary_muscles muscle[], - created_at timestamp not null default now(), - updated_at timestamp not null default now() - ) - `); - - await db.insert(exercises).values({ - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - }); - - const result = await db.select().from(exercises); - - t.deepEqual(result, [ - { - id: 1, - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - createdAt: result[0]!.createdAt, - updatedAt: result[0]!.updatedAt, - }, - ]); - - await db.execute(sql`drop table ${exercises}`); - await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -}); - -test.serial('all date and time columns', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - dateString: date('date_string', { mode: 'string' }).notNull(), - time: time('time', { precision: 3 }).notNull(), - datetime: timestamp('datetime').notNull(), - datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), - datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), - datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), - datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), - interval: interval('interval').notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - date_string date not null, - time time(3) not null, - datetime timestamp not null, - datetime_wtz timestamp with time zone not null, - datetime_string timestamp not null, - datetime_full_precision timestamp(6) not null, - datetime_wtz_string timestamp with time zone not null, - interval interval not null - ) - `); - - const someDatetime = new Date('2022-01-01T00:00:00.123Z'); - const fullPrecision = '2022-01-01T00:00:00.123456Z'; - const someTime = '23:23:12.432'; - - await db.insert(table).values({ - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01T00:00:00.123Z', - datetimeFullPrecision: fullPrecision, - datetimeWTZString: '2022-01-01T00:00:00.123Z', - interval: '1 day', - }); - - const result = await db.select().from(table); - - Expect< - Equal<{ - id: number; - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - }[], typeof result> - >; - - Expect< - Equal<{ - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - id?: number | undefined; - }, typeof table.$inferInsert> - >; - - t.deepEqual(result, [ - { - id: 1, - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01 00:00:00.123', - datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), - datetimeWTZString: '2022-01-01 00:00:00.123+00', - interval: '1 day', - }, - ]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone second case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date(); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as date and check that timezones are the same - // There is no way to check timezone in Date object, as it is always represented internally in UTC - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); - - // 3. Compare both dates - t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC - const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones - - // 1. Insert date as new dates with different time zones - await db.insert(table).values([ - { timestamp: insertedDate }, - { timestamp: insertedDate2 }, - ]); - - // 2, Select and compare both dates - const result = await db.select().from(table); - - t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format without timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone second case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01T02:00:00.123456-02' }, - ]); - - // 2, Select as raw query and check that values are the same - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123+04'); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as raw query as string - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC - t.deepEqual(new Date(result.rows[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode date for timestamp with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const timestampString = new Date('2022-01-01 00:00:00.456-0200'); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: timestampString }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone in UTC timezone', async (t) => { - const { db } = t.context; - - // get current timezone from db - const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); - - // set timezone to UTC - await db.execute(sql`set time zone 'UTC'`); - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); - - await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone in different timezone', async (t) => { - const { db } = t.context; - - // get current timezone from db - const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); - - // set timezone to HST (UTC - 10) - await db.execute(sql`set time zone 'HST'`); - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-1000'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456-10' }]); - - await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('select from sql', async (t) => { - const { db } = t.context; - - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - await t.notThrowsAsync(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = pgTable('products_transactions', { - id: serial('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - t.deepEqual(result, []); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = pgTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = pgTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('table selection with single table', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - - await db.insert(users).values({ name: 'John', cityId: 1 }); - - const result = await db.select({ users }).from(users); - - t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('set null to jsonb field', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - jsonb: jsonb('jsonb'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, jsonb jsonb)`, - ); - - const result = await db.insert(users).values({ jsonb: null }).returning(); - - t.deepEqual(result, [{ id: 1, jsonb: null }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('array operators', async (t) => { - const { db } = t.context; - - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - t.deepEqual(contains, [{ id: 3 }, { id: 5 }]); - t.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]); - t.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - t.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]); -}); - -test.serial('set operations (union) from query builder with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const sq = db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).as('sq'); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).union( - db.select().from(sq), - ).orderBy(asc(sql`name`)).limit(2).offset(1); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 3, name: 'Jack' }, - { id: 2, name: 'Jane' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name, name2: users2Table.name }) - .from(cities2Table).union( - // @ts-expect-error - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (union) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`name`)).limit(1).offset(1); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - union( - db - .select({ name: citiesTable.name, id: cities2Table.id }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (union all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).unionAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 4); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).unionAll( - db - .select({ name: citiesTable.name, id: cities2Table.id }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (union all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await unionAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 3); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - unionAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (intersect) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).intersect( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`name`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).intersect( - // @ts-expect-error - db - .select({ id: cities2Table.id, name: citiesTable.name, id2: cities2Table.id }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (intersect) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersect( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 0); - - t.deepEqual(result, []); - - t.throws(() => { - intersect( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (intersect all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).intersectAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).intersectAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (intersect all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersectAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - intersectAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (except) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(cities2Table).except( - db - .select() - .from(cities2Table).where(gt(citiesTable.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - db - .select() - .from(cities2Table).except( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - ); - }); -}); - -test.serial('set operations (except) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await except( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table), - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - except( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (except all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(cities2Table).exceptAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ name: cities2Table.name, id: cities2Table.id }) - .from(cities2Table).exceptAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (except all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await exceptAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)).limit(5).offset(2); - - t.assert(result.length === 4); - - t.deepEqual(result, [ - { id: 4, name: 'Peter' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - { id: 7, name: 'Mary' }, - ]); - - t.throws(() => { - exceptAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (mixed) from query builder with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - const sq = db - .select() - .from(cities2Table).where(gt(citiesTable.id, 1)).as('sq'); - - const result = await db - .select() - .from(cities2Table).except( - ({ unionAll }) => - unionAll( - db.select().from(sq), - db.select().from(cities2Table).where(eq(citiesTable.id, 2)), - ), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - db - .select() - .from(cities2Table).except( - ({ unionAll }) => - unionAll( - db - .select({ name: cities2Table.name, id: cities2Table.id }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - db.select().from(cities2Table).where(eq(citiesTable.id, 2)), - ), - ); - }); -}); - -test.serial('set operations (mixed all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 7)), - ), - db - .select().from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 6); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - { id: 8, name: 'Sally' }, - ]); - - t.throws(() => { - union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 7)), - ), - db - .select().from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('aggregate function: count', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: count() }).from(table); - const result2 = await db.select({ value: count(table.a) }).from(table); - const result3 = await db.select({ value: countDistinct(table.name) }).from(table); - - t.deepEqual(result1[0]?.value, 7); - t.deepEqual(result2[0]?.value, 5); - t.deepEqual(result3[0]?.value, 6); -}); - -test.serial('aggregate function: avg', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: avg(table.b) }).from(table); - const result2 = await db.select({ value: avg(table.nullOnly) }).from(table); - const result3 = await db.select({ value: avgDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '33.3333333333333333'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '42.5000000000000000'); -}); - -test.serial('aggregate function: sum', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: sum(table.b) }).from(table); - const result2 = await db.select({ value: sum(table.nullOnly) }).from(table); - const result3 = await db.select({ value: sumDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '200'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '170'); -}); - -test.serial('aggregate function: max', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: max(table.b) }).from(table); - const result2 = await db.select({ value: max(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 90); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('aggregate function: min', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: min(table.b) }).from(table); - const result2 = await db.select({ value: min(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 10); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('array mapping and parsing', async (t) => { - const { db } = t.context; - - const arrays = pgTable('arrays_tests', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - nested: text('nested').array().array(), - numbers: integer('numbers').notNull().array(), - }); - - await db.execute(sql`drop table if exists ${arrays}`); - await db.execute(sql` - create table ${arrays} ( - id serial primary key, - tags text[], - nested text[][], - numbers integer[] - ) - `); - - await db.insert(arrays).values({ - tags: ['', 'b', 'c'], - nested: [['1', ''], ['3', '\\a']], - numbers: [1, 2, 3], - }); - - const result = await db.select().from(arrays); - - t.deepEqual(result, [{ - id: 1, - tags: ['', 'b', 'c'], - nested: [['1', ''], ['3', '\\a']], - numbers: [1, 2, 3], - }]); - - await db.execute(sql`drop table ${arrays}`); -}); - -test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial primary key, - name text not null, - update_counter integer default 1 not null, - updated_at timestamp(3), - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - t.deepEqual(response, [ - { name: 'John', id: 1, updateCounter: 1, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, - ]); - const msDelay = 250; - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); - -test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial primary key, - name text not null, - update_counter integer default 1, - updated_at timestamp(3), - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John', alwaysNull: 'this will be null after updating' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - const initial = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); - await db.update(usersOnUpdate).set({ updateCounter: null }).where(eq(usersOnUpdate.id, 2)); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - t.deepEqual(response, [ - { name: 'Angel', id: 1, updateCounter: 2, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: null, alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, - ]); - const msDelay = 250; - - t.assert(initial[0]?.updatedAt?.valueOf() !== justDates[0]?.updatedAt?.valueOf()); - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); - -test.serial('test if method with sql operators', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - age: integer('age').notNull(), - city: text('city').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute(sql` - create table ${users} ( - id serial primary key, - name text not null, - age integer not null, - city text not null - ) - `); - - await db.insert(users).values([ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition1 = true; - - const [result1] = await db.select().from(users).where(eq(users.id, 1).if(condition1)); - - t.deepEqual(result1, { id: 1, name: 'John', age: 20, city: 'New York' }); - - const condition2 = 1; - - const [result2] = await db.select().from(users).where(sql`${users.id} = 1`.if(condition2)); - - t.deepEqual(result2, { id: 1, name: 'John', age: 20, city: 'New York' }); - - const condition3 = 'non-empty string'; - - const result3 = await db.select().from(users).where( - or(eq(users.id, 1).if(condition3), eq(users.id, 2).if(condition3)), - ); - - t.deepEqual(result3, [{ id: 1, name: 'John', age: 20, city: 'New York' }, { - id: 2, - name: 'Alice', - age: 21, - city: 'New York', - }]); - - const condtition4 = false; - - const result4 = await db.select().from(users).where(eq(users.id, 1).if(condtition4)); - - t.deepEqual(result4, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition5 = undefined; - - const result5 = await db.select().from(users).where(sql`${users.id} = 1`.if(condition5)); - - t.deepEqual(result5, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition6 = null; - - const result6 = await db.select().from(users).where( - or(eq(users.id, 1).if(condition6), eq(users.id, 2).if(condition6)), - ); - - t.deepEqual(result6, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition7 = { - term1: 0, - term2: 1, - }; - - const result7 = await db.select().from(users).where( - and(gt(users.age, 20).if(condition7.term1), eq(users.city, 'New York').if(condition7.term2)), - ); - - t.deepEqual(result7, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - ]); - - const condition8 = { - term1: '', - term2: 'non-empty string', - }; - - const result8 = await db.select().from(users).where( - or(lt(users.age, 21).if(condition8.term1), eq(users.city, 'London').if(condition8.term2)), - ); - - t.deepEqual(result8, [ - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition9 = { - term1: 1, - term2: true, - }; - - const result9 = await db.select().from(users).where( - and(inArray(users.city, ['New York', 'London']).if(condition9.term1), ilike(users.name, 'a%').if(condition9.term2)), - ); - - t.deepEqual(result9, [ - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - ]); - - const condition10 = { - term1: 4, - term2: 19, - }; - - const result10 = await db.select().from(users).where( - and( - sql`length(${users.name}) <= ${condition10.term1}`.if(condition10.term1), - gt(users.age, condition10.term2).if(condition10.term2 > 20), - ), - ); - - t.deepEqual(result10, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition11 = true; - - const result11 = await db.select().from(users).where( - or(eq(users.city, 'New York'), gte(users.age, 22))!.if(condition11), - ); - - t.deepEqual(result11, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition12 = false; - - const result12 = await db.select().from(users).where( - and(eq(users.city, 'London'), gte(users.age, 23))!.if(condition12), - ); - - t.deepEqual(result12, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition13 = true; - - const result13 = await db.select().from(users).where(sql`(city = 'New York' or age >= 22)`.if(condition13)); - - t.deepEqual(result13, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - const condition14 = false; - - const result14 = await db.select().from(users).where(sql`(city = 'London' and age >= 23)`.if(condition14)); - - t.deepEqual(result14, [ - { id: 1, name: 'John', age: 20, city: 'New York' }, - { id: 2, name: 'Alice', age: 21, city: 'New York' }, - { id: 3, name: 'Nick', age: 22, city: 'London' }, - { id: 4, name: 'Lina', age: 23, city: 'London' }, - ]); - - await db.execute(sql`drop table ${users}`); -}); diff --git a/integration-tests/tests/__old/pglite.test.ts b/integration-tests/tests/__old/pglite.test.ts deleted file mode 100644 index 4bd936f71..000000000 --- a/integration-tests/tests/__old/pglite.test.ts +++ /dev/null @@ -1,4072 +0,0 @@ -import 'dotenv/config'; - -import { PGlite } from '@electric-sql/pglite'; -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - avg, - avgDistinct, - count, - countDistinct, - eq, - exists, - getTableColumns, - gt, - gte, - inArray, - lt, - max, - min, - name, - placeholder, - type SQL, - sql, - type SQLWrapper, - sum, - sumDistinct, - TransactionRollbackError, -} from 'drizzle-orm'; -import { - alias, - boolean, - char, - cidr, - date, - except, - exceptAll, - foreignKey, - getMaterializedViewConfig, - getTableConfig, - getViewConfig, - inet, - integer, - intersect, - intersectAll, - interval, - jsonb, - macaddr, - macaddr8, - numeric, - type PgColumn, - pgEnum, - pgMaterializedView, - pgTable, - pgTableCreator, - pgView, - primaryKey, - serial, - text, - time, - timestamp, - union, - unionAll, - unique, - uniqueKeyName, - uuid as pgUuid, - varchar, -} from 'drizzle-orm/pg-core'; -import { drizzle, type PgliteDatabase } from 'drizzle-orm/pglite'; -import { migrate } from 'drizzle-orm/pglite/migrator'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, randomString } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id' as string).primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const usersOnUpdate = pgTable('users_on_update', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - updateCounter: integer('update_counter').default(sql`1`).$onUpdateFn(() => sql`update_counter + 1`), - updatedAt: timestamp('updated_at', { mode: 'date', precision: 3 }).$onUpdate(() => new Date()), - alwaysNull: text('always_null').$type().$onUpdate(() => null), - // uppercaseName: text('uppercase_name').$onUpdateFn(() => sql`upper(name)`), looks like this is not supported in pg -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const cities2Table = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const network = pgTable('network_table', { - inet: inet('inet').notNull(), - cidr: cidr('cidr').notNull(), - macaddr: macaddr('macaddr').notNull(), - macaddr8: macaddr8('macaddr8').notNull(), -}); - -const salEmp = pgTable('sal_emp', { - name: text('name'), - payByQuarter: integer('pay_by_quarter').array(), - schedule: text('schedule').array().array(), -}); - -const _tictactoe = pgTable('tictactoe', { - squares: integer('squares').array(3).array(3), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -// To test aggregate functions -const aggregateTable = pgTable('aggregate_table', { - id: serial('id').notNull(), - name: text('name').notNull(), - a: integer('a'), - b: integer('b'), - c: integer('c'), - nullOnly: integer('null_only'), -}); - -interface Context { - db: PgliteDatabase; - client: PGlite; -} - -const test = anyTest as TestFn; - -test.before(async (t) => { - const ctx = t.context; - - ctx.client = new PGlite(); - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.db.execute( - sql` - create table cities ( - id serial primary key, - name text not null, - state char(2) - ) - `, - ); - await ctx.db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.db.execute( - sql` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - ); - await ctx.db.execute( - sql` - create table network_table ( - inet inet not null, - cidr cidr not null, - macaddr macaddr not null, - macaddr8 macaddr8 not null - ) - `, - ); - await ctx.db.execute( - sql` - create table sal_emp ( - name text not null, - pay_by_quarter integer[] not null, - schedule text[][] not null - ) - `, - ); - await ctx.db.execute( - sql` - create table tictactoe ( - squares integer[3][3] not null - ) - `, - ); -}); - -async function setupSetOperationTest(db: PgliteDatabase) { - await db.execute(sql`drop table if exists users2`); - await db.execute(sql`drop table if exists cities`); - await db.execute( - sql` - create table cities ( - id serial primary key, - name text not null - ) - `, - ); - await db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - - await db.insert(cities2Table).values([ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - await db.insert(users2Table).values([ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 2 }, - { id: 3, name: 'Jack', cityId: 3 }, - { id: 4, name: 'Peter', cityId: 3 }, - { id: 5, name: 'Ben', cityId: 2 }, - { id: 6, name: 'Jill', cityId: 1 }, - { id: 7, name: 'Mary', cityId: 2 }, - { id: 8, name: 'Sally', cityId: 1 }, - ]); -} - -async function setupAggregateFunctionsTest(db: PgliteDatabase) { - await db.execute(sql`drop table if exists "aggregate_table"`); - await db.execute( - sql` - create table "aggregate_table" ( - "id" serial not null, - "name" text not null, - "a" integer, - "b" integer, - "c" integer, - "null_only" integer - ); - `, - ); - await db.insert(aggregateTable).values([ - { name: 'value 1', a: 5, b: 10, c: 20 }, - { name: 'value 1', a: 5, b: 20, c: 30 }, - { name: 'value 2', a: 10, b: 50, c: 60 }, - { name: 'value 3', a: 20, b: 20, c: null }, - { name: 'value 4', a: null, b: 90, c: 120 }, - { name: 'value 5', a: 80, b: 10, c: null }, - { name: 'value 6', a: null, b: null, c: 150 }, - ]); -} - -test.serial('table configs: unique third param', async (t) => { - const cities1Table = pgTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), - }, (t) => ({ - f: unique('custom_name').on(t.name, t.state).nullsNotDistinct(), - f1: unique('custom_name1').on(t.name, t.state), - })); - - const tableConfig = getTableConfig(cities1Table); - - t.assert(tableConfig.uniqueConstraints.length === 2); - - t.assert(tableConfig.uniqueConstraints[0]?.name === 'custom_name'); - t.assert(tableConfig.uniqueConstraints[0]?.nullsNotDistinct); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); - - t.assert(tableConfig.uniqueConstraints[1]?.name, 'custom_name1'); - t.assert(!tableConfig.uniqueConstraints[1]?.nullsNotDistinct); - t.deepEqual(tableConfig.uniqueConstraints[0]?.columns.map((t) => t.name), ['name', 'state']); -}); - -test.serial('table configs: unique in column', async (t) => { - const cities1Table = pgTable('cities1', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - state: char('state', { length: 2 }).unique('custom'), - field: char('field', { length: 2 }).unique('custom_field', { nulls: 'not distinct' }), - }); - - const tableConfig = getTableConfig(cities1Table); - - const columnName = tableConfig.columns.find((it) => it.name === 'name'); - t.assert(columnName?.uniqueName === uniqueKeyName(cities1Table, [columnName!.name])); - t.assert(columnName?.isUnique); - - const columnState = tableConfig.columns.find((it) => it.name === 'state'); - t.assert(columnState?.uniqueName === 'custom'); - t.assert(columnState?.isUnique); - - const columnField = tableConfig.columns.find((it) => it.name === 'field'); - t.assert(columnField?.uniqueName === 'custom_field'); - t.assert(columnField?.isUnique); - t.assert(columnField?.uniqueType === 'not distinct'); -}); - -test.serial('table config: foreign keys name', async (t) => { - const table = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: foreignKey({ foreignColumns: [t.id], columns: [t.id], name: 'custom_fk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.foreignKeys.length, 1); - t.is(tableConfig.foreignKeys[0]!.getName(), 'custom_fk'); -}); - -test.serial('table config: primary keys name', async (t) => { - const table = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: text('state'), - }, (t) => ({ - f: primaryKey({ columns: [t.id, t.name], name: 'custom_pk' }), - })); - - const tableConfig = getTableConfig(table); - - t.is(tableConfig.primaryKeys.length, 1); - t.is(tableConfig.primaryKeys[0]!.getName(), 'custom_pk'); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - const insertedOrder = await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }) - .returning(); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(insertedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - age: integer('age').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text, age integer)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John', age: 24 }, - { id: 1, name: 'John', age: 24 }, - { id: 2, name: 'John', age: 25 }, - { id: 1, name: 'Jane', age: 24 }, - { id: 1, name: 'Jane', age: 26 }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - const users4 = await db.selectDistinctOn([usersDistinctTable.id, usersDistinctTable.age]).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.id, usersDistinctTable.age); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [ - { id: 1, name: 'Jane', age: 24 }, - { id: 1, name: 'Jane', age: 26 }, - { id: 1, name: 'John', age: 24 }, - { id: 2, name: 'John', age: 25 }, - ]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); - - t.deepEqual(users4, [ - { id: 1, name: 'John', age: 24 }, - { id: 1, name: 'Jane', age: 26 }, - { id: 2, name: 'John', age: 25 }, - ]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .delete(usersTable) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }) - .from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('char insert', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]); -}); - -test.serial('char update', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]); -}); - -test.serial('char delete', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, []); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with exists', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const user = alias(usersTable, 'user'); - const result = await db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }) - .from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [ - { - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }, - ]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db - .insert(usersTable) - .values({ - verified: true, - name: placeholder('name'), - }) - .prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -// TODO change tests to new structure -test.serial('migrator : default migration strategy', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom schema', async (t) => { - const { db } = t.context; - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); - - // test if the custom migrations table was created - const { rows } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); - t.true(rows.length! > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { rows } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); - t.true(rows.length! > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); -}); - -test.serial('migrator : migrate with custom table and custom schema', async (t) => { - const { db } = t.context; - const customTable = randomString(); - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { - migrationsFolder: './drizzle2/pg', - migrationsTable: customTable, - migrationsSchema: customSchema, - }); - - // test if the custom migrations table was created - const { rows } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, - ); - t.true(rows.length! > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute( - sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`, - ); - - const result = await db.execute<{ id: number; name: string }>( - sql`select id, name from "users"`, - ); - t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('Query check: Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default)', - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default), (default, default, default)', - params: [], - }); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select() - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - state: null, - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db - .insert(courseCategoriesTable) - .values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db - .insert(coursesTable) - .values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result1 = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - const result2 = await db - .with(regionalSales, topRegions) - .selectDistinct({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - const result3 = await db - .with(regionalSales, topRegions) - .selectDistinctOn([orders.region], { - region: orders.region, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region) - .orderBy(orders.region); - - t.deepEqual(result1, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); - t.deepEqual(result2, result1); - t.deepEqual(result3, [ - { - region: 'Europe', - productUnits: 8, - productSales: 80, - }, - { - region: 'US', - productUnits: 16, - productSales: 160, - }, - ]); -}); - -test.serial('with ... update', async (t) => { - const { db } = t.context; - - const products = pgTable('products', { - id: serial('id').primaryKey(), - price: numeric('price').notNull(), - cheap: boolean('cheap').notNull().default(false), - }); - - await db.execute(sql`drop table if exists ${products}`); - await db.execute(sql` - create table ${products} ( - id serial primary key, - price numeric not null, - cheap boolean not null default false - ) - `); - - await db.insert(products).values([ - { price: '10.99' }, - { price: '25.85' }, - { price: '32.99' }, - { price: '2.50' }, - { price: '4.59' }, - ]); - - const averagePrice = db - .$with('average_price') - .as( - db - .select({ - value: sql`avg(${products.price})`.as('value'), - }) - .from(products), - ); - - const result = await db - .with(averagePrice) - .update(products) - .set({ - cheap: true, - }) - .where(lt(products.price, sql`(select * from ${averagePrice})`)) - .returning({ - id: products.id, - }); - - t.deepEqual(result, [ - { id: 1 }, - { id: 4 }, - { id: 5 }, - ]); -}); - -test.serial('with ... insert', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - username: text('username').notNull(), - admin: boolean('admin').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (username text not null, admin boolean not null default false)`); - - const userCount = db - .$with('user_count') - .as( - db - .select({ - value: sql`count(*)`.as('value'), - }) - .from(users), - ); - - const result = await db - .with(userCount) - .insert(users) - .values([ - { username: 'user1', admin: sql`((select * from ${userCount}) = 0)` }, - ]) - .returning({ - admin: users.admin, - }); - - t.deepEqual(result, [{ admin: true }]); -}); - -test.serial('with ... delete', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const averageAmount = db - .$with('average_amount') - .as( - db - .select({ - value: sql`avg(${orders.amount})`.as('value'), - }) - .from(orders), - ); - - const result = await db - .with(averageAmount) - .delete(orders) - .where(gt(orders.amount, sql`(select * from ${averageAmount})`)) - .returning({ - id: orders.id, - }); - - t.deepEqual(result, [ - { id: 6 }, - { id: 7 }, - { id: 8 }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare('query')); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('select count w/ custom mapper', async (t) => { - const { db } = t.context; - - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('network types', async (t) => { - const { db } = t.context; - - const value: typeof network.$inferSelect = { - inet: '127.0.0.1', - cidr: '192.168.100.128/25', - macaddr: '08:00:2b:01:02:03', - macaddr8: '08:00:2b:01:02:03:04:05', - }; - - await db.insert(network).values(value); - - const res = await db.select().from(network); - - t.deepEqual(res, [value]); -}); - -test.serial('array types', async (t) => { - const { db } = t.context; - - const values: typeof salEmp.$inferSelect[] = [ - { - name: 'John', - payByQuarter: [10000, 10000, 10000, 10000], - schedule: [['meeting', 'lunch'], ['training', 'presentation']], - }, - { - name: 'Carol', - payByQuarter: [20000, 25000, 25000, 25000], - schedule: [['breakfast', 'consulting'], ['meeting', 'lunch']], - }, - ]; - - await db.insert(salEmp).values(values); - - const res = await db.select().from(salEmp); - - t.deepEqual(res, values); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db - .select() - .from(users2Table) - .for('update') - .toSQL(); - - t.regex( - query.sql, - / for update$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('update', { of: [users2Table, coursesTable] }) - .toSQL(); - - t.regex( - query.sql, - / for update of "users2", "courses"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table }) - .toSQL(); - - t.regex( - query.sql, - /for no key update of "users2"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table, skipLocked: true }) - .toSQL(); - - t.regex( - query.sql, - / for no key update of "users2" skip locked$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('share', { of: users2Table, noWait: true }) - .toSQL(); - - t.regex( - query.sql, - // eslint-disable-next-line unicorn/better-regex - /for share of "users2" no wait$/, - ); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial.skip('materialized view', async (t) => { - // Disabled due to bug in PGlite: - // https://github.com/electric-sql/pglite/issues/63 - const { db } = t.context; - - const newYorkers1 = pgMaterializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -// TODO: copy to SQLite and MySQL, add to docs -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from enum', async (t) => { - const { db } = t.context; - - const muscleEnum = pgEnum('muscle', [ - 'abdominals', - 'hamstrings', - 'adductors', - 'quadriceps', - 'biceps', - 'shoulders', - 'chest', - 'middle_back', - 'calves', - 'glutes', - 'lower_back', - 'lats', - 'triceps', - 'traps', - 'forearms', - 'neck', - 'abductors', - ]); - - const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - - const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - - const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - - const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - - const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - - const exercises = pgTable('exercises', { - id: serial('id').primaryKey(), - name: varchar('name').notNull(), - force: forceEnum('force'), - level: levelEnum('level'), - mechanic: mechanicEnum('mechanic'), - equipment: equipmentEnum('equipment'), - instructions: text('instructions'), - category: categoryEnum('category'), - primaryMuscles: muscleEnum('primary_muscles').array(), - secondaryMuscles: muscleEnum('secondary_muscles').array(), - createdAt: timestamp('created_at').notNull().default(sql`now()`), - updatedAt: timestamp('updated_at').notNull().default(sql`now()`), - }); - - await db.execute(sql`drop table if exists ${exercises}`); - await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - - await db.execute( - sql`create type ${ - name(muscleEnum.enumName) - } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, - ); - await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); - await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); - await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute( - sql`create type ${ - name(equipmentEnum.enumName) - } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, - ); - await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); - await db.execute(sql` - create table ${exercises} ( - id serial primary key, - name varchar not null, - force force, - level level, - mechanic mechanic, - equipment equipment, - instructions text, - category category, - primary_muscles muscle[], - secondary_muscles muscle[], - created_at timestamp not null default now(), - updated_at timestamp not null default now() - ) - `); - - await db.insert(exercises).values({ - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - }); - - const result = await db.select().from(exercises); - - t.deepEqual(result, [ - { - id: 1, - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - createdAt: result[0]!.createdAt, - updatedAt: result[0]!.updatedAt, - }, - ]); - - await db.execute(sql`drop table ${exercises}`); - await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -}); - -test.serial('all date and time columns', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - dateString: date('date_string', { mode: 'string' }).notNull(), - time: time('time', { precision: 3 }).notNull(), - datetime: timestamp('datetime').notNull(), - datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), - datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), - datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), - datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), - interval: interval('interval').notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - date_string date not null, - time time(3) not null, - datetime timestamp not null, - datetime_wtz timestamp with time zone not null, - datetime_string timestamp not null, - datetime_full_precision timestamp(6) not null, - datetime_wtz_string timestamp with time zone not null, - interval interval not null - ) - `); - - const someDatetime = new Date('2022-01-01T00:00:00.123Z'); - const fullPrecision = '2022-01-01T00:00:00.123456Z'; - const someTime = '23:23:12.432'; - - await db.insert(table).values({ - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01T00:00:00.123Z', - datetimeFullPrecision: fullPrecision, - datetimeWTZString: '2022-01-01T00:00:00.123Z', - interval: '1 day', - }); - - const result = await db.select().from(table); - - Expect< - Equal<{ - id: number; - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - }[], typeof result> - >; - - Expect< - Equal<{ - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - id?: number | undefined; - }, typeof table.$inferInsert> - >; - - t.deepEqual(result, [ - { - id: 1, - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01 00:00:00.123', - datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), - datetimeWTZString: '2022-01-01 00:00:00.123+00', - interval: '1 day', - }, - ]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone second case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date(); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as date and check that timezones are the same - // There is no way to check timezone in Date object, as it is always represented internally in UTC - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); - - // 3. Compare both dates - t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC - const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones - - // 1. Insert date as new dates with different time zones - await db.insert(table).values([ - { timestamp: insertedDate }, - { timestamp: insertedDate2 }, - ]); - - // 2, Select and compare both dates - const result = await db.select().from(table); - - t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format without timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone second case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01T02:00:00.123456-02' }, - ]); - - // 2, Select as raw query and check that values are the same - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123+04'); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as raw query as string - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC - t.deepEqual(new Date(result.rows[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode date for timestamp with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const timestampString = new Date('2022-01-01 00:00:00.456-0200'); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: timestampString }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial.skip('test mode string for timestamp with timezone in UTC timezone', async (t) => { - // Disabled due to bug in PGlite: - // https://github.com/electric-sql/pglite/issues/62 - const { db } = t.context; - - // get current timezone from db - const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); - - // set timezone to UTC - await db.execute(sql`set time zone 'UTC'`); - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); - - await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial.skip('test mode string for timestamp with timezone in different timezone', async (t) => { - // Disabled due to bug in PGlite: - // https://github.com/electric-sql/pglite/issues/62 - const { db } = t.context; - - // get current timezone from db - const timezone = await db.execute<{ TimeZone: string }>(sql`show timezone`); - - // set timezone to HST (UTC - 10) - await db.execute(sql`set time zone 'HST'`); - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-1000'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual(result2.rows, [{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456-10' }]); - - await db.execute(sql`set time zone '${sql.raw(timezone.rows[0]!.TimeZone)}'`); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('select from sql', async (t) => { - const { db } = t.context; - - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - await t.notThrowsAsync(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = pgTable('products_transactions', { - id: serial('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - t.deepEqual(result, []); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = pgTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = pgTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('table selection with single table', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - - await db.insert(users).values({ name: 'John', cityId: 1 }); - - const result = await db.select({ users }).from(users); - - t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('set null to jsonb field', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - jsonb: jsonb('jsonb'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, jsonb jsonb)`, - ); - - const result = await db.insert(users).values({ jsonb: null }).returning(); - - t.deepEqual(result, [{ id: 1, jsonb: null }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('array operators', async (t) => { - const { db } = t.context; - - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - t.deepEqual(contains, [{ id: 3 }, { id: 5 }]); - t.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]); - t.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - t.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]); -}); - -test.serial('set operations (union) from query builder with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const sq = db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).as('sq'); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).union( - db.select().from(sq), - ).orderBy(asc(sql`name`)).limit(2).offset(1); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 3, name: 'Jack' }, - { id: 2, name: 'Jane' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name, name2: users2Table.name }) - .from(cities2Table).union( - // @ts-expect-error - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (union) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`name`)).limit(1).offset(1); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - union( - db - .select({ name: citiesTable.name, id: cities2Table.id }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (union all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).unionAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 4); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).unionAll( - db - .select({ name: citiesTable.name, id: cities2Table.id }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (union all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await unionAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 3); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - unionAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (intersect) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).intersect( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`name`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).intersect( - // @ts-expect-error - db - .select({ id: cities2Table.id, name: citiesTable.name, id2: cities2Table.id }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`name`)); - }); -}); - -test.serial('set operations (intersect) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersect( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 0); - - t.deepEqual(result, []); - - t.throws(() => { - intersect( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (intersect all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).intersectAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - { id: 2, name: 'London' }, - ]); - - t.throws(() => { - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).limit(2).intersectAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(cities2Table).limit(2), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (intersect all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await intersectAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); - - t.throws(() => { - intersectAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ); - }); -}); - -test.serial('set operations (except) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(cities2Table).except( - db - .select() - .from(cities2Table).where(gt(citiesTable.id, 1)), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - db - .select() - .from(cities2Table).except( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - ); - }); -}); - -test.serial('set operations (except) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await except( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table), - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - except( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (except all) from query builder', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await db - .select() - .from(cities2Table).exceptAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 2); - - t.deepEqual(result, [ - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - ]); - - t.throws(() => { - db - .select({ name: cities2Table.name, id: cities2Table.id }) - .from(cities2Table).exceptAll( - db - .select({ id: cities2Table.id, name: citiesTable.name }) - .from(cities2Table).where(eq(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (except all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await exceptAll( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)).limit(5).offset(2); - - t.assert(result.length === 4); - - t.deepEqual(result, [ - { id: 4, name: 'Peter' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - { id: 7, name: 'Mary' }, - ]); - - t.throws(() => { - exceptAll( - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gt(users2Table.id, 7)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('set operations (mixed) from query builder with subquery', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - const sq = db - .select() - .from(cities2Table).where(gt(citiesTable.id, 1)).as('sq'); - - const result = await db - .select() - .from(cities2Table).except( - ({ unionAll }) => - unionAll( - db.select().from(sq), - db.select().from(cities2Table).where(eq(citiesTable.id, 2)), - ), - ); - - t.assert(result.length === 1); - - t.deepEqual(result, [ - { id: 1, name: 'New York' }, - ]); - - t.throws(() => { - db - .select() - .from(cities2Table).except( - ({ unionAll }) => - unionAll( - db - .select({ name: cities2Table.name, id: cities2Table.id }) - .from(cities2Table).where(gt(citiesTable.id, 1)), - db.select().from(cities2Table).where(eq(citiesTable.id, 2)), - ), - ); - }); -}); - -test.serial('set operations (mixed all) as function', async (t) => { - const { db } = t.context; - - await setupSetOperationTest(db); - - const result = await union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 7)), - ), - db - .select().from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - - t.assert(result.length === 6); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'London' }, - { id: 3, name: 'Tampa' }, - { id: 5, name: 'Ben' }, - { id: 6, name: 'Jill' }, - { id: 8, name: 'Sally' }, - ]); - - t.throws(() => { - union( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(eq(users2Table.id, 1)), - except( - db - .select({ id: users2Table.id, name: users2Table.name }) - .from(users2Table).where(gte(users2Table.id, 5)), - db - .select({ name: users2Table.name, id: users2Table.id }) - .from(users2Table).where(eq(users2Table.id, 7)), - ), - db - .select().from(cities2Table).where(gt(citiesTable.id, 1)), - ).orderBy(asc(sql`id`)); - }); -}); - -test.serial('aggregate function: count', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: count() }).from(table); - const result2 = await db.select({ value: count(table.a) }).from(table); - const result3 = await db.select({ value: countDistinct(table.name) }).from(table); - - t.deepEqual(result1[0]?.value, 7); - t.deepEqual(result2[0]?.value, 5); - t.deepEqual(result3[0]?.value, 6); -}); - -test.serial('aggregate function: avg', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: avg(table.b) }).from(table); - const result2 = await db.select({ value: avg(table.nullOnly) }).from(table); - const result3 = await db.select({ value: avgDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '33.3333333333333333'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '42.5000000000000000'); -}); - -test.serial('aggregate function: sum', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: sum(table.b) }).from(table); - const result2 = await db.select({ value: sum(table.nullOnly) }).from(table); - const result3 = await db.select({ value: sumDistinct(table.b) }).from(table); - - t.deepEqual(result1[0]?.value, '200'); - t.deepEqual(result2[0]?.value, null); - t.deepEqual(result3[0]?.value, '170'); -}); - -test.serial('aggregate function: max', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: max(table.b) }).from(table); - const result2 = await db.select({ value: max(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 90); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('aggregate function: min', async (t) => { - const { db } = t.context; - const table = aggregateTable; - await setupAggregateFunctionsTest(db); - - const result1 = await db.select({ value: min(table.b) }).from(table); - const result2 = await db.select({ value: min(table.nullOnly) }).from(table); - - t.deepEqual(result1[0]?.value, 10); - t.deepEqual(result2[0]?.value, null); -}); - -test.serial('array mapping and parsing', async (t) => { - const { db } = t.context; - - const arrays = pgTable('arrays_tests', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - nested: text('nested').array().array(), - numbers: integer('numbers').notNull().array(), - }); - - await db.execute(sql`drop table if exists ${arrays}`); - await db.execute(sql` - create table ${arrays} ( - id serial primary key, - tags text[], - nested text[][], - numbers integer[] - ) - `); - - await db.insert(arrays).values({ - tags: ['', 'b', 'c'], - nested: [['1', ''], ['3', '\\a']], - numbers: [1, 2, 3], - }); - - const result = await db.select().from(arrays); - - t.deepEqual(result, [{ - id: 1, - tags: ['', 'b', 'c'], - nested: [['1', ''], ['3', '\\a']], - numbers: [1, 2, 3], - }]); - - await db.execute(sql`drop table ${arrays}`); -}); - -test.serial('test $onUpdateFn and $onUpdate works as $default', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial primary key, - name text not null, - update_counter integer default 1 not null, - updated_at timestamp(3), - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - t.deepEqual(response, [ - { name: 'John', id: 1, updateCounter: 1, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: 1, alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, - ]); - const msDelay = 250; - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); - -test.serial('test $onUpdateFn and $onUpdate works updating', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists ${usersOnUpdate}`); - - await db.execute( - sql` - create table ${usersOnUpdate} ( - id serial primary key, - name text not null, - update_counter integer default 1, - updated_at timestamp(3), - always_null text - ) - `, - ); - - await db.insert(usersOnUpdate).values([ - { name: 'John', alwaysNull: 'this will be null after updating' }, - { name: 'Jane' }, - { name: 'Jack' }, - { name: 'Jill' }, - ]); - - const { updatedAt, ...rest } = getTableColumns(usersOnUpdate); - // const initial = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - await db.update(usersOnUpdate).set({ name: 'Angel' }).where(eq(usersOnUpdate.id, 1)); - await db.update(usersOnUpdate).set({ updateCounter: null }).where(eq(usersOnUpdate.id, 2)); - - const justDates = await db.select({ updatedAt }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - const response = await db.select({ ...rest }).from(usersOnUpdate).orderBy(asc(usersOnUpdate.id)); - - t.deepEqual(response, [ - { name: 'Angel', id: 1, updateCounter: 2, alwaysNull: null }, - { name: 'Jane', id: 2, updateCounter: null, alwaysNull: null }, - { name: 'Jack', id: 3, updateCounter: 1, alwaysNull: null }, - { name: 'Jill', id: 4, updateCounter: 1, alwaysNull: null }, - ]); - const msDelay = 250; - - // t.assert(initial[0]?.updatedAt?.valueOf() !== justDates[0]?.updatedAt?.valueOf()); - - for (const eachUser of justDates) { - t.assert(eachUser.updatedAt!.valueOf() > Date.now() - msDelay); - } -}); diff --git a/integration-tests/tests/__old/postgres.js.test.ts b/integration-tests/tests/__old/postgres.js.test.ts deleted file mode 100644 index dec27afc1..000000000 --- a/integration-tests/tests/__old/postgres.js.test.ts +++ /dev/null @@ -1,2791 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - eq, - exists, - gt, - gte, - inArray, - lt, - Name, - name, - placeholder, - type SQL, - sql, - type SQLWrapper, - TransactionRollbackError, -} from 'drizzle-orm'; -import { - alias, - boolean, - date, - getMaterializedViewConfig, - getViewConfig, - integer, - interval, - jsonb, - type PgColumn, - pgEnum, - pgMaterializedView, - pgTable, - pgTableCreator, - pgView, - serial, - text, - time, - timestamp, - uuid as pgUuid, - varchar, -} from 'drizzle-orm/pg-core'; -import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'; -import { drizzle } from 'drizzle-orm/postgres-js'; -import { migrate } from 'drizzle-orm/postgres-js/migrator'; -import getPort from 'get-port'; -import postgres, { type Sql } from 'postgres'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, randomString } from '../utils.ts'; - -const QUERY_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull().$default(() => 'random_string'), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - docker: Docker; - pgContainer: Docker.Container; - db: PostgresJsDatabase; - client: Sql; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 5432 }); - const image = 'postgres:14'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - const pgContainer = (ctx.pgContainer = await docker.createContainer({ - Image: image, - Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '5432/tcp': [{ HostPort: `${port}` }], - }, - }, - })); - - await pgContainer.start(); - - return `postgres://postgres:postgres@localhost:${port}/postgres`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['PG_CONNECTION_STRING'] ?? await createDockerDB(ctx); - - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = postgres(connectionString, { - max: 1, - onnotice: () => { - // disable notices - }, - }); - await ctx.client`select 1`; - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to Postgres'); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: QUERY_LOGGING }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.db.execute( - sql` - create table cities ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.db.execute( - sql` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with exists', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const user = alias(usersTable, 'user'); - const result = await db.select({ name: usersTable.name }).from(usersTable).where( - exists(db.select({ one: sql`1` }).from(user).where(and(eq(usersTable.name, 'John'), eq(user.id, usersTable.id)))), - ); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -// TODO change tests to new structure -test.serial('migrator : default migration strategy', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom schema', async (t) => { - const { db } = t.context; - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); - - // test if the custom migrations table was created - const { count } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); - t.true(count > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { count } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); - t.true(count > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); -}); - -test.serial('migrator : migrate with custom table and custom schema', async (t) => { - const { db } = t.context; - const customTable = randomString(); - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { - migrationsFolder: './drizzle2/pg', - migrationsTable: customTable, - migrationsSchema: customSchema, - }); - - // test if the custom migrations table was created - const { count } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, - ); - t.true(count > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.execute<{ id: number; name: string }>(sql`select id, name from "users"`); - t.deepEqual(Array.prototype.slice.call(result), [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const result = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${new Name( - usersTable.name.name, - )}) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(Array.prototype.slice.call(result), [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const result = await db.execute>( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(Array.prototype.slice.call(result), [{ id: 1, name: 'John' }]); -}); - -test.serial('Query check: Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - const query = db - .insert(users) - .values({}) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default)', - params: [], - }); -}); - -test.serial('Query check: Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state').default('UA'), - }); - - const query = db - .insert(users) - .values([{}, {}]) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("id", "name", "state") values (default, default, default), (default, default, default)', - params: [], - }); -}); - -test.serial('$default function', async (t) => { - const { db } = t.context; - - const insertedOrder = await db.insert(orders).values({ id: 1, region: 'Ukraine', amount: 1, quantity: 1 }) - .returning(); - const selectedOrder = await db.select().from(orders); - - t.deepEqual(insertedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); - - t.deepEqual(selectedOrder, [{ - id: 1, - amount: 1, - quantity: 1, - region: 'Ukraine', - product: 'random_string', - }]); -}); - -test.serial('Insert all defaults in 1 row', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_single', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values({}); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }]); -}); - -test.serial('Insert all defaults in multiple rows', async (t) => { - const { db } = t.context; - - const users = pgTable('empty_insert_multiple', { - id: serial('id').primaryKey(), - name: text('name').default('Dan'), - state: text('state'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial primary key, name text default 'Dan', state text)`, - ); - - await db.insert(users).values([{}, {}]); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'Dan', state: null }, { id: 2, name: 'Dan', state: null }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db.insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing(); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable) - .values({ name: 'John' }); - - await db.insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).where( - eq(usersTable.id, 1), - ); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db.select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare('query')); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: '2' }]); -}); - -test.serial('select count w/ custom mapper', async (t) => { - const { db } = t.context; - - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db - .select() - .from(users2Table) - .for('update') - .toSQL(); - - t.regex( - query.sql, - / for update$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('update', { of: [users2Table, coursesTable] }) - .toSQL(); - - t.regex( - query.sql, - / for update of "users2", "courses"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table }) - .toSQL(); - - t.regex( - query.sql, - /for no key update of "users2"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table, skipLocked: true }) - .toSQL(); - - t.regex( - query.sql, - / for no key update of "users2" skip locked$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('share', { of: users2Table, noWait: true }) - .toSQL(); - - t.regex( - query.sql, - // eslint-disable-next-line unicorn/better-regex - /for share of "users2" no wait$/, - ); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('materialized view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgMaterializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from enum', async (t) => { - const { db } = t.context; - - const muscleEnum = pgEnum('muscle', [ - 'abdominals', - 'hamstrings', - 'adductors', - 'quadriceps', - 'biceps', - 'shoulders', - 'chest', - 'middle_back', - 'calves', - 'glutes', - 'lower_back', - 'lats', - 'triceps', - 'traps', - 'forearms', - 'neck', - 'abductors', - ]); - - const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - - const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - - const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - - const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - - const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - - const exercises = pgTable('exercises', { - id: serial('id').primaryKey(), - name: varchar('name').notNull(), - force: forceEnum('force'), - level: levelEnum('level'), - mechanic: mechanicEnum('mechanic'), - equipment: equipmentEnum('equipment'), - instructions: text('instructions'), - category: categoryEnum('category'), - primaryMuscles: muscleEnum('primary_muscles').array(), - secondaryMuscles: muscleEnum('secondary_muscles').array(), - createdAt: timestamp('created_at').notNull().default(sql`now()`), - updatedAt: timestamp('updated_at').notNull().default(sql`now()`), - }); - - await db.execute(sql`drop table if exists ${exercises}`); - await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - - await db.execute( - sql`create type ${ - name(muscleEnum.enumName) - } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, - ); - await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); - await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); - await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute( - sql`create type ${ - name(equipmentEnum.enumName) - } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, - ); - await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); - await db.execute(sql` - create table ${exercises} ( - id serial primary key, - name varchar not null, - force force, - level level, - mechanic mechanic, - equipment equipment, - instructions text, - category category, - primary_muscles muscle[], - secondary_muscles muscle[], - created_at timestamp not null default now(), - updated_at timestamp not null default now() - ) - `); - - await db.insert(exercises).values({ - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - }); - - const result = await db.select().from(exercises); - - t.deepEqual(result, [ - { - id: 1, - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - createdAt: result[0]!.createdAt, - updatedAt: result[0]!.updatedAt, - }, - ]); - - await db.execute(sql`drop table ${exercises}`); - await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('select from sql', async (t) => { - const { db } = t.context; - - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - await t.notThrowsAsync(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ); - // beta -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('all date and time columns', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - dateString: date('date_string', { mode: 'string' }).notNull(), - time: time('time', { precision: 3 }).notNull(), - datetime: timestamp('datetime').notNull(), - datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), - datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), - datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), - datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), - interval: interval('interval').notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - date_string date not null, - time time(3) not null, - datetime timestamp not null, - datetime_wtz timestamp with time zone not null, - datetime_string timestamp not null, - datetime_full_precision timestamp(6) not null, - datetime_wtz_string timestamp with time zone not null, - interval interval not null - ) - `); - - const someDatetime = new Date('2022-01-01T00:00:00.123Z'); - const fullPrecision = '2022-01-01T00:00:00.123456Z'; - const someTime = '23:23:12.432'; - - await db.insert(table).values({ - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01T00:00:00.123Z', - datetimeFullPrecision: fullPrecision, - datetimeWTZString: '2022-01-01T00:00:00.123Z', - interval: '1 day', - }); - - const result = await db.select().from(table); - - Expect< - Equal<{ - id: number; - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - }[], typeof result> - >; - - Expect< - Equal<{ - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - id?: number | undefined; - }, typeof table.$inferInsert> - >; - - t.deepEqual(result, [ - { - id: 1, - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01 00:00:00.123', - datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), - datetimeWTZString: '2022-01-01 00:00:00.123+00', - interval: '1 day', - }, - ]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone second case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date(); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as date and check that timezones are the same - // There is no way to check timezone in Date object, as it is always represented internally in UTC - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: insertedDate }]); - - // 3. Compare both dates - t.deepEqual(insertedDate.getTime(), result[0]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns with timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123-04'); // used different time zones, internally is still UTC - const insertedDate2 = new Date('2022-01-02 04:00:00.123+04'); // They are both the same date in different time zones - - // 1. Insert date as new dates with different time zones - await db.insert(table).values([ - { timestamp: insertedDate }, - { timestamp: insertedDate2 }, - ]); - - // 2, Select and compare both dates - const result = await db.select().from(table); - - t.deepEqual(result[0]?.timestamp.getTime(), result[1]?.timestamp.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone first case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format without timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01 02:00:00.123456' }, - ]); - - // 2, Select in string format and check that values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456' }]); - - // 3. Select as raw query and check that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone second case mode string', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null - ) - `); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: '2022-01-01T02:00:00.123456-02' }, - ]); - - // 2, Select as raw query and check that values are the same - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual([...result], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('all date and time columns without timezone third case mode date', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) not null - ) - `); - - const insertedDate = new Date('2022-01-01 20:00:00.123+04'); - - // 1. Insert date as new date - await db.insert(table).values([ - { timestamp: insertedDate }, - ]); - - // 2, Select as raw query as string - const result = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3. Compare both dates using orm mapping - Need to add 'Z' to tell JS that it is UTC - t.deepEqual(new Date(result[0]!.timestamp_string + 'Z').getTime(), insertedDate.getTime()); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode date for timestamp with timezone', async (t) => { - const { db } = t.context; - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'date', withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(3) with time zone not null - ) - `); - - const timestampString = new Date('2022-01-01 00:00:00.456-0200'); - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: timestampString }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.456+00' }]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone in UTC timezone', async (t) => { - const { db } = t.context; - - // get current timezone from db - const [timezone] = await db.execute<{ TimeZone: string }>(sql`show timezone`); - - // set timezone to UTC - await db.execute(sql`set time zone 'UTC'`); - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - // 2.1 Notice that postgres will return the date in UTC, but it is exactly the same - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 02:00:00.123456+00' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - // 3.1 Notice that postgres will return the date in UTC, but it is exactlt the same - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 02:00:00.123456+00' }]); - - await db.execute(sql`set time zone '${sql.raw(timezone!.TimeZone)}'`); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('test mode string for timestamp with timezone in different timezone', async (t) => { - const { db } = t.context; - - // get current timezone from db - const [timezone] = await db.execute<{ TimeZone: string }>(sql`show timezone`); - - // set timezone to HST (UTC - 10) - await db.execute(sql`set time zone 'HST'`); - - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-1000'; - - // 1. Insert date in string format with timezone in it - await db.insert(table).values([ - { timestamp: timestampString }, - ]); - - // 2. Select date in string format and check that the values are the same - const result = await db.select().from(table); - - t.deepEqual(result, [{ id: 1, timestamp: '2022-01-01 00:00:00.123456-10' }]); - - // 3. Select as raw query and checke that values are the same - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - }>(sql`select * from ${table}`); - - t.deepEqual([...result2], [{ id: 1, timestamp_string: '2022-01-01 00:00:00.123456-10' }]); - - await db.execute(sql`set time zone '${sql.raw(timezone!.TimeZone)}'`); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = pgTable('products_transactions', { - id: serial('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - await tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - t.deepEqual(result, []); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - await tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = pgTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('array operators', async (t) => { - const { db } = t.context; - - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - t.deepEqual(contains, [{ id: 3 }, { id: 5 }]); - t.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]); - t.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - t.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]); -}); - -test.serial('array mapping and parsing', async (t) => { - const { db } = t.context; - - const arrays = pgTable('arrays_tests', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - nested: text('nested').array().array(), - numbers: integer('numbers').notNull().array(), - }); - - await db.execute(sql`drop table if exists ${arrays}`); - await db.execute(sql` - create table ${arrays} ( - id serial primary key, - tags text[], - nested text[][], - numbers integer[] - ) - `); - - await db.insert(arrays).values({ - tags: ['', 'b', 'c'], - nested: [['1', ''], ['3', '\\a']], - numbers: [1, 2, 3], - }); - - const result = await db.select().from(arrays); - - t.deepEqual(result, [{ - id: 1, - tags: ['', 'b', 'c'], - nested: [['1', ''], ['3', '\\a']], - numbers: [1, 2, 3], - }]); - - await db.execute(sql`drop table ${arrays}`); -}); diff --git a/integration-tests/tests/__old/sql.js.test.ts b/integration-tests/tests/__old/sql.js.test.ts deleted file mode 100644 index 050483268..000000000 --- a/integration-tests/tests/__old/sql.js.test.ts +++ /dev/null @@ -1,1838 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import { asc, eq, type Equal, gt, inArray, Name, name, placeholder, sql, TransactionRollbackError } from 'drizzle-orm'; -import type { SQLJsDatabase } from 'drizzle-orm/sql-js'; -import { drizzle } from 'drizzle-orm/sql-js'; -import { migrate } from 'drizzle-orm/sql-js/migrator'; -import { - alias, - blob, - getViewConfig, - integer, - primaryKey, - sqliteTable, - sqliteTableCreator, - sqliteView, - text, -} from 'drizzle-orm/sqlite-core'; -import type { Database } from 'sql.js'; -import initSqlJs from 'sql.js'; -import { Expect, randomString } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - verified: integer('verified', { mode: 'boolean' }).notNull().default(false), - json: blob('json', { mode: 'json' }).$type(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`strftime('%s', 'now')`), -}); - -const users2Table = sqliteTable('users2', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const citiesTable = sqliteTable('cities', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const coursesTable = sqliteTable('courses', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = sqliteTable('course_categories', { - id: integer('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = sqliteTable('orders', { - id: integer('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const usersMigratorTable = sqliteTable('users12', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const anotherUsersMigratorTable = sqliteTable('another_users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const pkExampleTable = sqliteTable('pk_example', { - id: integer('id').notNull(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => ({ - compositePk: primaryKey(table.id, table.name), -})); - -const bigIntExample = sqliteTable('big_int_example', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - bigInt: blob('big_int', { mode: 'bigint' }).notNull(), -}); - -interface Context { - db: SQLJsDatabase; - client: Database; -} - -const test = anyTest as TestFn; - -test.before(async (t) => { - const ctx = t.context; - - const SQL = await initSqlJs(); - ctx.client = new SQL.Database(); - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always((t) => { - const ctx = t.context; - ctx.client?.close(); -}); - -test.beforeEach((t) => { - const ctx = t.context; - - ctx.db.run(sql`drop table if exists ${usersTable}`); - ctx.db.run(sql`drop table if exists ${users2Table}`); - ctx.db.run(sql`drop table if exists ${citiesTable}`); - ctx.db.run(sql`drop table if exists ${coursesTable}`); - ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`); - ctx.db.run(sql`drop table if exists ${orders}`); - ctx.db.run(sql`drop table if exists ${bigIntExample}`); - ctx.db.run(sql`drop table if exists ${pkExampleTable}`); - - ctx.db.run(sql` - create table ${usersTable} ( - id integer primary key, - name text not null, - verified integer not null default 0, - json blob, - created_at integer not null default (strftime('%s', 'now')) - ) - `); - ctx.db.run(sql` - create table ${users2Table} ( - id integer primary key, - name text not null, - city_id integer references ${citiesTable}(${name(citiesTable.id.name)}) - ) - `); - ctx.db.run(sql` - create table ${citiesTable} ( - id integer primary key, - name text not null - ) - `); - ctx.db.run(sql` - create table ${courseCategoriesTable} ( - id integer primary key, - name text not null - ) - `); - ctx.db.run(sql` - create table ${coursesTable} ( - id integer primary key, - name text not null, - category_id integer references ${courseCategoriesTable}(${name(courseCategoriesTable.id.name)}) - ) - `); - ctx.db.run(sql` - create table ${orders} ( - id integer primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `); - ctx.db.run(sql` - create table ${pkExampleTable} ( - id integer not null, - name text not null, - email text not null, - primary key (id, name) - ) - `); - ctx.db.run(sql` - create table ${bigIntExample} ( - id integer primary key, - name text not null, - big_int blob not null - ) - `); -}); - -test.serial('insert bigint values', (t) => { - const { db } = t.context; - - db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run(); - db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run(); - db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run(); - db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run(); - db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run(); - - const result = db.select().from(bigIntExample).all(); - t.deepEqual(result, [ - { id: 1, name: 'one', bigInt: BigInt('0') }, - { id: 2, name: 'two', bigInt: BigInt('127') }, - { id: 3, name: 'three', bigInt: BigInt('32767') }, - { id: 4, name: 'four', bigInt: BigInt('1234567890') }, - { id: 5, name: 'five', bigInt: BigInt('12345678900987654321') }, - ]); -}); - -test.serial('select all fields', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select().from(usersTable).all(); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select partial', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select({ name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', (t) => { - const { db } = t.context; - - const usersDistinctTable = sqliteTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${usersDistinctTable}`); - db.run(sql`create table ${usersDistinctTable} (id integer, name text)`); - - db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]).run(); - const users = db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ).all(); - - db.run(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', (t) => { - const { db } = t.context; - - const users = db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('insert with auto increment', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'George' }, - { name: 'Austin' }, - ]).run(); - const result = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'George' }, - { id: 4, name: 'Austin' }, - ]); -}); - -test.serial('insert with default values', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert with overridden default values', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John', verified: true }).run(); - const result = db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('update with returning all fields', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning partial', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', (t) => { - const { db } = t.context; - - const now = Date.now(); - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning partial', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const users = db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const result = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - db.insert(usersTable).values({ name: 'Jane' }).run(); - const result2 = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result2, [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]); -}); - -test.serial('json insert', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John', json: ['foo', 'bar'] }).run(); - const result = db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', json: ['foo', 'bar'] }]); -}); - -test.serial('insert many', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]).run(); - const result = db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', (t) => { - const { db } = t.context; - - const result = db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }) - .all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('partial join with alias', (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)) - .all(); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('select from alias', (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: sql`'Jo h n'` }).run(); - const result = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const statement = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).prepare(); - const result = statement.all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - stmt.run({ name: `John ${i}` }); - } - - const result = db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ name: 'John' }).run(); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = stmt.all({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('select with group by as field', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('migrator', (t) => { - const { db } = t.context; - - db.run(sql`drop table if exists another_users`); - db.run(sql`drop table if exists users12`); - db.run(sql`drop table if exists __drizzle_migrations`); - - migrate(db, { migrationsFolder: './drizzle2/sqlite' }); - - db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result = db.select().from(usersMigratorTable).all(); - - db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result2 = db.select().from(usersMigratorTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - t.deepEqual(result2, [{ id: 1, name: 'John', email: 'email' }]); - - db.run(sql`drop table another_users`); - db.run(sql`drop table users12`); - db.run(sql`drop table __drizzle_migrations`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - db.run(sql`drop table if exists another_users`); - db.run(sql`drop table if exists users12`); - db.run(sql`drop table if exists ${sql.identifier(customTable)}`); - - migrate(db, { migrationsFolder: './drizzle2/sqlite', migrationsTable: customTable }); - - // test if the custom migrations table was created - const res = db.all(sql`select * from ${sql.identifier(customTable)};`); - t.true(res.length > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - db.run(sql`drop table another_users`); - db.run(sql`drop table users12`); - db.run(sql`drop table ${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.run + select via db.all', (t) => { - const { db } = t.context; - - db.run(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = db.all<{ id: number; name: string }>(sql`select id, name from "users"`); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.get', (t) => { - const { db } = t.context; - - const inserted = db.get<{ id: number; name: string }>( - sql`insert into ${usersTable} (${new Name( - usersTable.name.name, - )}) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.run + select via db.get', (t) => { - const { db } = t.context; - - db.run(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = db.get<{ id: number; name: string }>( - sql`select ${usersTable.id}, ${usersTable.name} from ${usersTable}`, - ); - t.deepEqual(result, { id: 1, name: 'John' }); -}); - -test.serial('insert via db.get w/ query builder', (t) => { - const { db } = t.context; - - const inserted = db.get>( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted, { id: 1, name: 'John' }); -}); - -test.serial('left join (flat object fields)', (t) => { - const { db } = t.context; - - const { id: cityId } = db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all()[0]!; - - db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = db.select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', (t) => { - const { db } = t.context; - - const { id: cityId } = db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all()[0]!; - - db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = db.select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }).from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)) - .all(); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', (t) => { - const { db } = t.context; - - const { id: cityId } = db.insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }).all()[0]!; - - db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]).run(); - - const res = db.select().from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)).all(); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', (t) => { - const { db } = t.context; - - db.insert(courseCategoriesTable).values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]).run(); - - db.insert(coursesTable).values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]).run(); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name) - .all(); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', (t) => { - const { db } = t.context; - - db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]).run(); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`cast(sum(${orders.quantity}) as int)`, - productSales: sql`cast(sum(${orders.amount}) as int)`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product) - .all(); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', (t) => { - const { db } = t.context; - - db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = db.select({ name: sq.name }).from(sq).all(); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare()); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare()); -}); - -test.serial('select count()', (t) => { - const { db } = t.context; - - db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]).run(); - - const res = db.select({ count: sql`count(*)` }).from(usersTable).all(); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('having', (t) => { - const { db } = t.context; - - db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]).run(); - - db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]).run(); - - const result = db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name) - .all(); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', (t) => { - const { db } = t.context; - - const newYorkers1 = sqliteView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = sqliteView('new_yorkers', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = sqliteView('new_yorkers', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - db.run(sql`create view new_yorkers as ${getViewConfig(newYorkers1).query}`); - - db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]).run(); - - db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]).run(); - - { - const result = db.select().from(newYorkers1).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = db.select().from(newYorkers2).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = db.select().from(newYorkers3).all(); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = db.select({ name: newYorkers1.name }).from(newYorkers1).all(); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - db.run(sql`drop view ${newYorkers1}`); -}); - -test.serial('insert null timestamp', (t) => { - const { db } = t.context; - - const test = sqliteTable('test', { - t: integer('t', { mode: 'timestamp' }), - }); - - db.run(sql`create table ${test} (t timestamp)`); - - db.insert(test).values({ t: null }).run(); - const res = db.select().from(test).all(); - t.deepEqual(res, [{ t: null }]); - - db.run(sql`drop table ${test}`); -}); - -test.serial('select from raw sql', (t) => { - const { db } = t.context; - - const result = db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`).all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', (t) => { - const { db } = t.context; - - const result = db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`) - .all(); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', (t) => { - const { db } = t.context; - - const result = db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)) - .all(); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `myprefix_${name}`); - - const users = sqliteTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - db.insert(users).values({ id: 1, name: 'John' }).run(); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('transaction', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = sqliteTable('products_transactions', { - id: integer('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`drop table if exists ${products}`); - - db.run(sql`create table users_transactions (id integer not null primary key, balance integer not null)`); - db.run( - sql`create table products_transactions (id integer not null primary key, price integer not null, stock integer not null)`, - ); - - const user = db.insert(users).values({ balance: 100 }).returning().get(); - const product = db.insert(products).values({ price: 10, stock: 10 }).returning().get(); - - db.transaction((tx) => { - tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)).run(); - tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)).run(); - }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - db.run(sql`drop table ${users}`); - db.run(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table users_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - t.throws(() => - db.transaction((tx) => { - tx.insert(users).values({ balance: 100 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table users_nested_transactions (id integer not null primary key, balance integer not null)`, - ); - - db.transaction((tx) => { - tx.insert(users).values({ balance: 100 }).run(); - - tx.transaction((tx) => { - tx.update(users).set({ balance: 200 }).run(); - }); - }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_nested_transactions_rollback', { - id: integer('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table users_nested_transactions_rollback (id integer not null primary key, balance integer not null)`, - ); - - db.transaction((tx) => { - tx.insert(users).values({ balance: 100 }).run(); - - t.throws(() => - tx.transaction((tx) => { - tx.update(users).set({ balance: 200 }).run(); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = db.select().from(users).all(); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - db.run(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', (t) => { - const { db } = t.context; - - const internalStaff = sqliteTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = sqliteTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = sqliteTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - db.run(sql`drop table if exists ${internalStaff}`); - db.run(sql`drop table if exists ${customUser}`); - db.run(sql`drop table if exists ${ticket}`); - - db.run(sql`create table internal_staff (user_id integer not null)`); - db.run(sql`create table custom_user (id integer not null)`); - db.run(sql`create table ticket (staff_id integer not null)`); - - db.insert(internalStaff).values({ userId: 1 }).run(); - db.insert(customUser).values({ id: 1 }).run(); - db.insert(ticket).values({ staffId: 1 }).run(); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)) - .all(); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - db.run(sql`drop table ${internalStaff}`); - db.run(sql`drop table ${customUser}`); - db.run(sql`drop table ${ticket}`); -}); - -test.serial('join view as subquery', (t) => { - const { db } = t.context; - - const users = sqliteTable('users_join_view', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = sqliteView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - db.run(sql`drop table if exists ${users}`); - db.run(sql`drop view if exists ${newYorkers}`); - - db.run( - sql`create table ${users} (id integer not null primary key, name text not null, city_id integer not null)`, - ); - db.run(sql`create view ${newYorkers} as ${getViewConfig(newYorkers).query}`); - - db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]).run(); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = db.select().from(users).leftJoin(sq, eq(users.id, sq.id)).all(); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - db.run(sql`drop view ${newYorkers}`); - db.run(sql`drop table ${users}`); -}); - -test.serial('insert with onConflict do nothing', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing() - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk', (t) => { - const { db } = t.context; - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing() - .run(); - - const res = db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do nothing using target', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }) - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk as target', (t) => { - const { db } = t.context; - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing({ target: [pkExampleTable.id, pkExampleTable.name] }) - .run(); - - const res = db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do update', (t) => { - const { db } = t.context; - - db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .run(); - - const res = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do update using composite pk', (t) => { - const { db } = t.context; - - db.insert(pkExampleTable).values({ id: 1, name: 'John', email: 'john@example.com' }).run(); - - db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .onConflictDoUpdate({ target: [pkExampleTable.id, pkExampleTable.name], set: { email: 'john1@example.com' } }) - .run(); - - const res = db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john1@example.com' }]); -}); - -test.serial('insert undefined', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - t.notThrows(() => db.insert(users).values({ name: undefined }).run()); - - db.run(sql`drop table ${users}`); -}); - -test.serial('update undefined', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - t.throws(() => db.update(users).set({ name: undefined }).run()); - t.notThrows(() => db.update(users).set({ id: 1, name: undefined }).run()); - - db.run(sql`drop table ${users}`); -}); - -test.serial('async api - CRUD', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - await db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)); - - const res1 = await db.select().from(users); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - await db.delete(users).where(eq(users.id, 1)); - - const res2 = await db.select().from(users); - - t.deepEqual(res2, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + async execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + sync execute', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - insertStmt.execute().sync(); - - const selectStmt = db.select().from(users).prepare(); - const res = selectStmt.execute().sync(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - updateStmt.execute().sync(); - - const res1 = selectStmt.execute().sync(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - deleteStmt.execute().sync(); - - const res2 = selectStmt.execute().sync(); - - t.deepEqual(res2, []); - - db.run(sql`drop table ${users}`); -}); - -test.serial('select + .get() for empty result', (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const res = db.select().from(users).where(eq(users.id, 1)).get(); - - t.is(res, undefined); - - db.run(sql`drop table ${users}`); -}); diff --git a/integration-tests/tests/__old/sqlite-proxy-batch.test.ts b/integration-tests/tests/__old/sqlite-proxy-batch.test.ts deleted file mode 100644 index aa0c177bd..000000000 --- a/integration-tests/tests/__old/sqlite-proxy-batch.test.ts +++ /dev/null @@ -1,702 +0,0 @@ -import 'dotenv/config'; -import Database from 'better-sqlite3'; -import type BetterSqlite3 from 'better-sqlite3'; -import { eq, relations, sql } from 'drizzle-orm'; -import { type AnySQLiteColumn, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; -import type { SqliteRemoteDatabase, SqliteRemoteResult } from 'drizzle-orm/sqlite-proxy'; -import { drizzle as proxyDrizzle } from 'drizzle-orm/sqlite-proxy'; -import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; - -export const usersTable = sqliteTable('users', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - verified: integer('verified').notNull().default(0), - invitedBy: integer('invited_by').references((): AnySQLiteColumn => usersTable.id), -}); -export const usersConfig = relations(usersTable, ({ one, many }) => ({ - invitee: one(usersTable, { - fields: [usersTable.invitedBy], - references: [usersTable.id], - }), - usersToGroups: many(usersToGroupsTable), - posts: many(postsTable), -})); - -export const groupsTable = sqliteTable('groups', { - id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), - description: text('description'), -}); -export const groupsConfig = relations(groupsTable, ({ many }) => ({ - usersToGroups: many(usersToGroupsTable), -})); - -export const usersToGroupsTable = sqliteTable( - 'users_to_groups', - { - id: integer('id').primaryKey({ autoIncrement: true }), - userId: integer('user_id', { mode: 'number' }).notNull().references( - () => usersTable.id, - ), - groupId: integer('group_id', { mode: 'number' }).notNull().references( - () => groupsTable.id, - ), - }, - (t) => ({ - pk: primaryKey(t.userId, t.groupId), - }), -); -export const usersToGroupsConfig = relations(usersToGroupsTable, ({ one }) => ({ - group: one(groupsTable, { - fields: [usersToGroupsTable.groupId], - references: [groupsTable.id], - }), - user: one(usersTable, { - fields: [usersToGroupsTable.userId], - references: [usersTable.id], - }), -})); - -export const postsTable = sqliteTable('posts', { - id: integer('id').primaryKey({ autoIncrement: true }), - content: text('content').notNull(), - ownerId: integer('owner_id', { mode: 'number' }).references( - () => usersTable.id, - ), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const postsConfig = relations(postsTable, ({ one, many }) => ({ - author: one(usersTable, { - fields: [postsTable.ownerId], - references: [usersTable.id], - }), - comments: many(commentsTable), -})); - -export const commentsTable = sqliteTable('comments', { - id: integer('id').primaryKey({ autoIncrement: true }), - content: text('content').notNull(), - creator: integer('creator', { mode: 'number' }).references( - () => usersTable.id, - ), - postId: integer('post_id', { mode: 'number' }).references(() => postsTable.id), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const commentsConfig = relations(commentsTable, ({ one, many }) => ({ - post: one(postsTable, { - fields: [commentsTable.postId], - references: [postsTable.id], - }), - author: one(usersTable, { - fields: [commentsTable.creator], - references: [usersTable.id], - }), - likes: many(commentLikesTable), -})); - -export const commentLikesTable = sqliteTable('comment_likes', { - id: integer('id').primaryKey({ autoIncrement: true }), - creator: integer('creator', { mode: 'number' }).references( - () => usersTable.id, - ), - commentId: integer('comment_id', { mode: 'number' }).references( - () => commentsTable.id, - ), - createdAt: integer('created_at', { mode: 'timestamp_ms' }) - .notNull().default(sql`current_timestamp`), -}); -export const commentLikesConfig = relations(commentLikesTable, ({ one }) => ({ - comment: one(commentsTable, { - fields: [commentLikesTable.commentId], - references: [commentsTable.id], - }), - author: one(usersTable, { - fields: [commentLikesTable.creator], - references: [usersTable.id], - }), -})); - -const schema = { - usersTable, - postsTable, - commentsTable, - usersToGroupsTable, - groupsTable, - commentLikesConfig, - commentsConfig, - postsConfig, - usersToGroupsConfig, - groupsConfig, - usersConfig, -}; - -// eslint-disable-next-line drizzle-internal/require-entity-kind -class ServerSimulator { - constructor(private db: BetterSqlite3.Database) {} - - async batch(queries: { sql: string; params: any[]; method: string }[]) { - const results: { rows: any }[] = []; - for (const query of queries) { - const { method, sql, params } = query; - - if (method === 'run') { - try { - const result = this.db.prepare(sql).run(params); - results.push(result as any); - } catch (e: any) { - return { error: e.message }; - } - } else if (method === 'all' || method === 'values') { - try { - const rows = this.db.prepare(sql).raw().all(params); - results.push({ rows: rows }); - } catch (e: any) { - return { error: e.message }; - } - } else if (method === 'get') { - try { - const row = this.db.prepare(sql).raw().get(params); - results.push({ rows: row }); - } catch (e: any) { - return { error: e.message }; - } - } else { - return { error: 'Unknown method value' }; - } - } - return results; - } - - async query(sql: string, params: any[], method: string) { - if (method === 'run') { - try { - const result = this.db.prepare(sql).run(params); - return { data: result as any }; - } catch (e: any) { - return { error: e.message }; - } - } else if (method === 'all' || method === 'values') { - try { - const rows = this.db.prepare(sql).raw().all(params); - return { data: rows }; - } catch (e: any) { - return { error: e.message }; - } - } else if (method === 'get') { - try { - const row = this.db.prepare(sql).raw().get(params); - return { data: row }; - } catch (e: any) { - return { error: e.message }; - } - } else { - return { error: 'Unknown method value' }; - } - } - - migrations(queries: string[]) { - this.db.exec('BEGIN'); - try { - for (const query of queries) { - this.db.exec(query); - } - this.db.exec('COMMIT'); - } catch { - this.db.exec('ROLLBACK'); - } - - return {}; - } -} - -let db: SqliteRemoteDatabase; -let client: Database.Database; -let serverSimulator: ServerSimulator; - -beforeAll(async () => { - const dbPath = process.env['SQLITE_DB_PATH'] ?? ':memory:'; - - client = new Database(dbPath); - - serverSimulator = new ServerSimulator(client); - - db = proxyDrizzle(async (sql, params, method) => { - try { - // console.log(sql, params, method); - const rows = await serverSimulator.query(sql, params, method); - - // console.log('rowsTest', rows); - - if (rows.error !== undefined) { - throw new Error(rows.error); - } - - return { rows: rows.data }; - } catch (e: any) { - console.error('Error from sqlite proxy server:', e.response.data); - throw e; - } - }, async (queries) => { - try { - const result = await serverSimulator.batch(queries); - - if ((result as any).error !== undefined) { - throw new Error((result as any).error); - } - - return result as { rows: any }[]; - } catch (e: any) { - console.error('Error from sqlite proxy server:', e); - throw e; - } - }, { schema }); -}); - -beforeEach(async () => { - await db.run(sql`drop table if exists \`groups\``); - await db.run(sql`drop table if exists \`users\``); - await db.run(sql`drop table if exists \`users_to_groups\``); - await db.run(sql`drop table if exists \`posts\``); - await db.run(sql`drop table if exists \`comments\``); - await db.run(sql`drop table if exists \`comment_likes\``); - - await db.run( - sql` - CREATE TABLE \`users\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text NOT NULL, - \`verified\` integer DEFAULT 0 NOT NULL, - \`invited_by\` integer - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`groups\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text NOT NULL, - \`description\` text - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`users_to_groups\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`user_id\` integer NOT NULL, - \`group_id\` integer NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`posts\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`content\` text NOT NULL, - \`owner_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`comments\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`content\` text NOT NULL, - \`creator\` integer, - \`post_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); - await db.run( - sql` - CREATE TABLE \`comment_likes\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`creator\` integer, - \`comment_id\` integer, - \`created_at\` integer DEFAULT current_timestamp NOT NULL - ); - `, - ); -}); - -afterAll(async () => { - await db.run(sql`drop table if exists \`groups\``); - await db.run(sql`drop table if exists \`users\``); - await db.run(sql`drop table if exists \`users_to_groups\``); - await db.run(sql`drop table if exists \`posts\``); - await db.run(sql`drop table if exists \`comments\``); - await db.run(sql`drop table if exists \`comment_likes\``); - - client.close(); -}); - -test('findMany + findOne api example', async () => { - const user = await db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }); - const insertRes = await db.insert(usersTable).values({ id: 2, name: 'Dan' }); - const manyUsers = await db.query.usersTable.findMany({}); - const oneUser = await db.query.usersTable.findFirst({}); - - expectTypeOf(user).toEqualTypeOf< - { - id: number; - }[] - >; - - expectTypeOf(insertRes).toEqualTypeOf; - - expectTypeOf(manyUsers).toEqualTypeOf<{ - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[]>; - - expectTypeOf(oneUser).toEqualTypeOf< - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - } | undefined - >; - - expect(user).toEqual([{ - id: 1, - }]); - - expect(insertRes).toEqual({ rows: { changes: 1, lastInsertRowid: 2 } }); - - expect(manyUsers).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(oneUser).toEqual( - { id: 1, name: 'John', verified: 0, invitedBy: null }, - ); -}); - -test('batch api example', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ - id: usersTable.id, - invitedBy: usersTable.invitedBy, - }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.select().from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - invitedBy: number | null; - }[], - SqliteRemoteResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - invitedBy: null, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 2 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api only relational many -test('insert + findMany', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - SqliteRemoteResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(3); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 2 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); -}); - -// batch api relational many + one -test('insert + findMany + findFirst', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.query.usersTable.findFirst({}), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - SqliteRemoteResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 2 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 1, name: 'John', verified: 0, invitedBy: null }, - ); -}); - -test.skip('insert + db.all + db.get + db.values + db.run', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.run(sql`insert into users (id, name) values (2, 'Dan')`), - db.all(sql`select * from users`), - db.values(sql`select * from users`), - db.get(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - SqliteRemoteResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - unknown[][], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }, - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 2 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invited_by: null }, - { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ]); - - expect(batchResponse[3].map((row) => Array.prototype.slice.call(row))).toEqual([ - [1, 'John', 0, null], - [2, 'Dan', 0, null], - ]); - - expect(batchResponse[4]).toEqual( - { id: 1, name: 'John', verified: 0, invited_by: null }, - ); -}); - -// batch api combined rqb + raw call -test('insert + findManyWith + db.all', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.query.usersTable.findMany({}), - db.all(sql`select * from users`), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - SqliteRemoteResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 2 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'John', verified: 0, invitedBy: null }, - { id: 2, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - [1, 'John', 0, null], - [2, 'Dan', 0, null], - // { id: 1, name: 'John', verified: 0, invited_by: null }, - // { id: 2, name: 'Dan', verified: 0, invited_by: null }, - ]); -}); - -// batch api for insert + update + select -test('insert + update + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)), - db.query.usersTable.findMany({}), - db.select().from(usersTable).where(eq(usersTable.id, 1)), - db.select({ id: usersTable.id, invitedBy: usersTable.invitedBy }).from(usersTable), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - SqliteRemoteResult, - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - name: string; - verified: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - }[], - ]>(); - - expect(batchResponse.length).eq(5); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 1 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual([ - { id: 1, name: 'Dan', verified: 0, invitedBy: null }, - ]); - - expect(batchResponse[4]).toEqual([ - { id: 1, invitedBy: null }, - ]); -}); - -// batch api for insert + delete + select -test('insert + delete + select + select partial', async () => { - const batchResponse = await db.batch([ - db.insert(usersTable).values({ id: 1, name: 'John' }).returning({ id: usersTable.id }), - db.insert(usersTable).values({ id: 2, name: 'Dan' }), - db.delete(usersTable).where(eq(usersTable.id, 1)).returning({ id: usersTable.id, invitedBy: usersTable.invitedBy }), - db.query.usersTable.findFirst({ - columns: { - id: true, - invitedBy: true, - }, - }), - ]); - - expectTypeOf(batchResponse).toEqualTypeOf<[ - { - id: number; - }[], - SqliteRemoteResult, - { - id: number; - invitedBy: number | null; - }[], - { - id: number; - invitedBy: number | null; - } | undefined, - ]>(); - - expect(batchResponse.length).eq(4); - - expect(batchResponse[0]).toEqual([{ - id: 1, - }]); - - expect(batchResponse[1]).toEqual({ changes: 1, lastInsertRowid: 2 }); - - expect(batchResponse[2]).toEqual([ - { id: 1, invitedBy: null }, - ]); - - expect(batchResponse[3]).toEqual( - { id: 2, invitedBy: null }, - ); -}); diff --git a/integration-tests/tests/__old/sqlite-proxy.test.ts b/integration-tests/tests/__old/sqlite-proxy.test.ts deleted file mode 100644 index 7cfa413ac..000000000 --- a/integration-tests/tests/__old/sqlite-proxy.test.ts +++ /dev/null @@ -1,1114 +0,0 @@ -import 'dotenv/config'; - -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import type BetterSqlite3 from 'better-sqlite3'; -import Database from 'better-sqlite3'; -import { asc, eq, Name, placeholder, sql } from 'drizzle-orm'; -import { alias, blob, integer, primaryKey, sqliteTable, sqliteTableCreator, text } from 'drizzle-orm/sqlite-core'; -import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'; -import { drizzle as proxyDrizzle } from 'drizzle-orm/sqlite-proxy'; -import { migrate } from 'drizzle-orm/sqlite-proxy/migrator'; - -// eslint-disable-next-line drizzle-internal/require-entity-kind -class ServerSimulator { - constructor(private db: BetterSqlite3.Database) {} - - async query(sql: string, params: any[], method: string) { - if (method === 'run') { - try { - const result = this.db.prepare(sql).run(params); - return { data: result as any }; - } catch (e: any) { - return { error: e.message }; - } - } else if (method === 'all' || method === 'values') { - try { - const rows = this.db.prepare(sql).raw().all(params); - return { data: rows }; - } catch (e: any) { - return { error: e.message }; - } - } else if (method === 'get') { - try { - const row = this.db.prepare(sql).raw().get(params); - return { data: row }; - } catch (e: any) { - return { error: e.message }; - } - } else { - return { error: 'Unknown method value' }; - } - } - - migrations(queries: string[]) { - this.db.exec('BEGIN'); - try { - for (const query of queries) { - this.db.exec(query); - } - this.db.exec('COMMIT'); - } catch { - this.db.exec('ROLLBACK'); - } - - return {}; - } -} - -const usersTable = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - verified: integer('verified', { mode: 'boolean' }).notNull().default(false), - json: blob('json', { mode: 'json' }).$type(), - createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`strftime('%s', 'now')`), -}); - -const usersMigratorTable = sqliteTable('users12', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const anotherUsersMigratorTable = sqliteTable('another_users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -const pkExampleTable = sqliteTable('pk_example', { - id: integer('id').notNull(), - name: text('name').notNull(), - email: text('email').notNull(), -}, (table) => ({ - compositePk: primaryKey(table.id, table.name), -})); - -const bigIntExample = sqliteTable('big_int_example', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - bigInt: blob('big_int', { mode: 'bigint' }).notNull(), -}); - -interface Context { - db: SqliteRemoteDatabase; - client: Database.Database; - serverSimulator: ServerSimulator; -} - -const test = anyTest as TestFn; - -test.before((t) => { - const ctx = t.context; - const dbPath = process.env['SQLITE_DB_PATH'] ?? ':memory:'; - - ctx.client = new Database(dbPath); - - ctx.serverSimulator = new ServerSimulator(ctx.client); - - ctx.db = proxyDrizzle(async (sql, params, method) => { - try { - const rows = await ctx.serverSimulator.query(sql, params, method); - - if (rows.error !== undefined) { - throw new Error(rows.error); - } - - return { rows: rows.data }; - } catch (e: any) { - console.error('Error from sqlite proxy server:', e.response.data); - throw e; - } - }); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.run(sql`drop table if exists ${usersTable}`); - await ctx.db.run(sql`drop table if exists ${pkExampleTable}`); - await ctx.db.run(sql`drop table if exists ${bigIntExample}`); - - await ctx.db.run(sql` - create table ${usersTable} ( - id integer primary key, - name text not null, - verified integer not null default 0, - json blob, - created_at integer not null default (strftime('%s', 'now')) - ) - `); - await ctx.db.run(sql` - create table ${pkExampleTable} ( - id integer not null, - name text not null, - email text not null, - primary key (id, name) - ) - `); - await ctx.db.run(sql` - create table ${bigIntExample} ( - id integer primary key, - name text not null, - big_int blob not null - ) - `); -}); - -test.serial('insert bigint values', async (t) => { - const { db } = t.context; - - await db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run(); - await db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run(); - await db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run(); - await db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run(); - await db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run(); - - const result = await db.select().from(bigIntExample).all(); - t.deepEqual(result, [ - { id: 1, name: 'one', bigInt: BigInt('0') }, - { id: 2, name: 'two', bigInt: BigInt('127') }, - { id: 3, name: 'three', bigInt: BigInt('32767') }, - { id: 4, name: 'four', bigInt: BigInt('1234567890') }, - { id: 5, name: 'five', bigInt: BigInt('12345678900987654321') }, - ]); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select().from(usersTable).all(); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('select partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select({ name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ name: 'John' }]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = sqliteTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${usersDistinctTable}`); - await db.run(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]).run(); - const users = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ).all(); - - await db.run(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('insert returning sql + get()', async (t) => { - const { db } = t.context; - - const users = await db.insert(usersTable).values({ name: 'John' }).returning({ - name: sql`upper(${usersTable.name})`, - }).get(); - - t.deepEqual(users, { name: 'JOHN' }); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).all(); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update returning sql + get()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - name: sql`upper(${usersTable.name})`, - }).get(); - - t.deepEqual(users, { name: 'JANE' }); -}); - -test.serial('insert with auto increment', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Jane' }, - { name: 'George' }, - { name: 'Austin' }, - ]).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - { id: 2, name: 'Jane' }, - { id: 3, name: 'George' }, - { id: 4, name: 'Austin' }, - ]); -}); - -test.serial('insert with default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: false, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }).run(); - const result = await db.select().from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', verified: true, json: null, createdAt: result[0]!.createdAt }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'Jane', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('update with returning all fields + get()', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning().get(); - - t.assert(users.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users.createdAt.getTime() - now) < 5000); - t.deepEqual(users, { id: 1, name: 'Jane', verified: false, json: null, createdAt: users.createdAt }); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.update(usersTable).set({ name: 'Jane' }).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().all(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, [{ id: 1, name: 'John', verified: false, json: null, createdAt: users[0]!.createdAt }]); -}); - -test.serial('delete with returning all fields + get()', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning().get(); - - t.assert(users!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users!.createdAt.getTime() - now) < 5000); - t.deepEqual(users, { id: 1, name: 'John', verified: false, json: null, createdAt: users!.createdAt }); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).all(); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('delete with returning partial + get()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }).get(); - - t.deepEqual(users, { id: 1, name: 'John' }); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.insert(usersTable).values({ name: 'Jane' }).run(); - const result2 = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result2, [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', json: ['foo', 'bar'] }).run(); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', json: ['foo', 'bar'] }]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]).run(); - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db.insert(usersTable).values([ - { name: 'John' }, - { name: 'Bruce', json: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - json: usersTable.json, - verified: usersTable.verified, - }) - .all(); - - t.deepEqual(result, [ - { id: 1, name: 'John', json: null, verified: false }, - { id: 2, name: 'Bruce', json: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', json: null, verified: false }, - { id: 4, name: 'Austin', json: null, verified: true }, - ]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }).from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select().from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)) - .all(); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const sqliteTable = sqliteTableCreator((name) => `prefixed_${name}`); - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.run(sql`drop table if exists ${users}`); - await db.run(sql`create table ${users} (id integer primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]).run(); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)) - .all(); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }).run(); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const statement = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable).prepare(); - const result = await statement.all(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db.insert(usersTable).values({ - verified: true, - name: placeholder('name'), - }).prepare(); - - for (let i = 0; i < 10; i++) { - await stmt.run({ name: `John ${i}` }); - } - - const result = await db.select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }).from(usersTable).all(); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }).run(); - const stmt = db.select({ - id: usersTable.id, - name: usersTable.name, - }).from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare(); - const result = await stmt.all({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.name) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .all(); - - t.deepEqual(result, [{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]).run(); - - const result = await db.select({ name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1) - .all(); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('migrator', async (t) => { - const { db, serverSimulator } = t.context; - - await db.run(sql`drop table if exists another_users`); - await db.run(sql`drop table if exists users12`); - await db.run(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, async (queries) => { - try { - serverSimulator.migrations(queries); - } catch (e) { - console.error(e); - throw new Error('Proxy server cannot run migrations'); - } - }, { migrationsFolder: 'drizzle2/sqlite' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result = await db.select().from(usersMigratorTable).all(); - - await db.insert(anotherUsersMigratorTable).values({ name: 'John', email: 'email' }).run(); - const result2 = await db.select().from(usersMigratorTable).all(); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - t.deepEqual(result2, [{ id: 1, name: 'John', email: 'email' }]); - - await db.run(sql`drop table another_users`); - await db.run(sql`drop table users12`); - await db.run(sql`drop table __drizzle_migrations`); -}); - -test.serial('insert via db.run + select via db.all', async (t) => { - const { db } = t.context; - - await db.run(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.all(sql`select id, name from "users"`); - t.deepEqual(result, [[1, 'John']]); -}); - -test.serial('insert via db.get', async (t) => { - const { db } = t.context; - - const inserted = await db.get( - sql`insert into ${usersTable} (${new Name( - usersTable.name.name, - )}) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted, [1, 'John']); -}); - -test.serial('insert via db.run + select via db.get', async (t) => { - const { db } = t.context; - - await db.run(sql`insert into ${usersTable} (${new Name(usersTable.name.name)}) values (${'John'})`); - - const result = await db.get( - sql`select ${usersTable.id}, ${usersTable.name} from ${usersTable}`, - ); - t.deepEqual(result, [1, 'John']); -}); - -test.serial('insert via db.get w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.get( - db.insert(usersTable).values({ name: 'John' }).returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted, [1, 'John']); -}); - -test.after.always((t) => { - const ctx = t.context; - ctx.client?.close(); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing() - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk', async (t) => { - const { db } = t.context; - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing() - .run(); - - const res = await db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do nothing using target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing using composite pk as target', async (t) => { - const { db } = t.context; - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .run(); - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john1@example.com' }) - .onConflictDoNothing({ target: [pkExampleTable.id, pkExampleTable.name] }) - .run(); - - const res = await db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john@example.com' }]); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ id: 1, name: 'John' }).run(); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .run(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do update using composite pk', async (t) => { - const { db } = t.context; - - await db.insert(pkExampleTable).values({ id: 1, name: 'John', email: 'john@example.com' }).run(); - - await db - .insert(pkExampleTable) - .values({ id: 1, name: 'John', email: 'john@example.com' }) - .onConflictDoUpdate({ target: [pkExampleTable.id, pkExampleTable.name], set: { email: 'john1@example.com' } }) - .run(); - - const res = await db - .select({ id: pkExampleTable.id, name: pkExampleTable.name, email: pkExampleTable.email }) - .from(pkExampleTable) - .where(eq(pkExampleTable.id, 1)) - .all(); - - t.deepEqual(res, [{ id: 1, name: 'John', email: 'john1@example.com' }]); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined }).run()); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - await db.run(sql`drop table if exists ${users}`); - - await db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined }).run()); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined }).run()); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - CRUD', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const res = await db.select().from(users); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - await db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)); - - const res1 = await db.select().from(users); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - await db.delete(users).where(eq(users.id, 1)); - - const res2 = await db.select().from(users); - - t.deepEqual(res2, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + async execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('async api - insert + select w/ prepare + sync execute', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const insertStmt = db.insert(users).values({ id: 1, name: 'John' }).prepare(); - await insertStmt.execute(); - - const selectStmt = db.select().from(users).prepare(); - const res = await selectStmt.execute(); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); - - const updateStmt = db.update(users).set({ name: 'John1' }).where(eq(users.id, 1)).prepare(); - await updateStmt.execute(); - - const res1 = await selectStmt.execute(); - - t.deepEqual(res1, [{ id: 1, name: 'John1' }]); - - const deleteStmt = db.delete(users).where(eq(users.id, 1)).prepare(); - await deleteStmt.execute(); - - const res2 = await selectStmt.execute(); - - t.deepEqual(res2, []); - - await db.run(sql`drop table ${users}`); -}); - -test.serial('select + .get() for empty result', async (t) => { - const { db } = t.context; - - const users = sqliteTable('users', { - id: integer('id').primaryKey(), - name: text('name'), - }); - - db.run(sql`drop table if exists ${users}`); - - db.run( - sql`create table ${users} (id integer primary key, name text)`, - ); - - const res = await db.select().from(users).where(eq(users.id, 1)).get(); - - t.is(res, undefined); - - await db.run(sql`drop table ${users}`); -}); diff --git a/integration-tests/tests/__old/vercel-pg.test.ts b/integration-tests/tests/__old/vercel-pg.test.ts deleted file mode 100644 index 8d8eba517..000000000 --- a/integration-tests/tests/__old/vercel-pg.test.ts +++ /dev/null @@ -1,2470 +0,0 @@ -import 'dotenv/config'; - -import { createClient, type VercelClient } from '@vercel/postgres'; -import type { TestFn } from 'ava'; -import anyTest from 'ava'; -import Docker from 'dockerode'; -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - eq, - gt, - gte, - inArray, - lt, - name, - placeholder, - type SQL, - sql, - type SQLWrapper, - TransactionRollbackError, -} from 'drizzle-orm'; -import { - alias, - boolean, - char, - cidr, - getMaterializedViewConfig, - getViewConfig, - inet, - integer, - jsonb, - macaddr, - macaddr8, - type PgColumn, - pgEnum, - pgMaterializedView, - pgTable, - pgTableCreator, - pgView, - serial, - text, - timestamp, - uuid as pgUuid, - varchar, -} from 'drizzle-orm/pg-core'; -import { drizzle, type VercelPgDatabase } from 'drizzle-orm/vercel-postgres'; -import { migrate } from 'drizzle-orm/vercel-postgres/migrator'; -import getPort from 'get-port'; -import { v4 as uuid } from 'uuid'; -import { type Equal, Expect, randomString } from '../utils.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const network = pgTable('network_table', { - inet: inet('inet').notNull(), - cidr: cidr('cidr').notNull(), - macaddr: macaddr('macaddr').notNull(), - macaddr8: macaddr8('macaddr8').notNull(), -}); - -const salEmp = pgTable('sal_emp', { - name: text('name'), - payByQuarter: integer('pay_by_quarter').array(), - schedule: text('schedule').array().array(), -}); - -const _tictactoe = pgTable('tictactoe', { - squares: integer('squares').array(3).array(3), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -interface Context { - docker: Docker; - pgContainer: Docker.Container; - db: VercelPgDatabase; - client: VercelClient; -} - -const test = anyTest as TestFn; - -async function createDockerDB(ctx: Context): Promise { - const docker = (ctx.docker = new Docker()); - const port = await getPort({ port: 5432 }); - const image = 'postgres:14'; - - const pullStream = await docker.pull(image); - await new Promise((resolve, reject) => - docker.modem.followProgress(pullStream, (err) => (err ? reject(err) : resolve(err))) - ); - - ctx.pgContainer = await docker.createContainer({ - Image: image, - Env: ['POSTGRES_PASSWORD=postgres', 'POSTGRES_USER=postgres', 'POSTGRES_DB=postgres'], - name: `drizzle-integration-tests-${uuid()}`, - HostConfig: { - AutoRemove: true, - PortBindings: { - '5432/tcp': [{ HostPort: `${port}` }], - }, - }, - }); - - await ctx.pgContainer.start(); - - return `postgres://postgres:postgres@localhost:${port}/postgres`; -} - -test.before(async (t) => { - const ctx = t.context; - const connectionString = process.env['PG_CONNECTION_STRING'] ?? (await createDockerDB(ctx)); - - const sleep = 250; - let timeLeft = 5000; - let connected = false; - let lastError: unknown | undefined; - do { - try { - ctx.client = createClient({ connectionString }); - await ctx.client.connect(); - connected = true; - break; - } catch (e) { - lastError = e; - await new Promise((resolve) => setTimeout(resolve, sleep)); - timeLeft -= sleep; - } - } while (timeLeft > 0); - if (!connected) { - console.error('Cannot connect to Postgres'); - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); - throw lastError; - } - ctx.db = drizzle(ctx.client, { logger: ENABLE_LOGGING }); -}); - -test.after.always(async (t) => { - const ctx = t.context; - await ctx.client?.end().catch(console.error); - await ctx.pgContainer?.stop().catch(console.error); -}); - -test.beforeEach(async (t) => { - const ctx = t.context; - await ctx.db.execute(sql`drop schema public cascade`); - await ctx.db.execute(sql`create schema public`); - await ctx.db.execute( - sql` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - ); - await ctx.db.execute( - sql` - create table cities ( - id serial primary key, - name text not null, - state char(2) - ) - `, - ); - await ctx.db.execute( - sql` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - ); - await ctx.db.execute( - sql` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - ); - await ctx.db.execute( - sql` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - ); - await ctx.db.execute( - sql` - create table network_table ( - inet inet not null, - cidr cidr not null, - macaddr macaddr not null, - macaddr8 macaddr8 not null - ) - `, - ); - await ctx.db.execute( - sql` - create table sal_emp ( - name text not null, - pay_by_quarter integer[] not null, - schedule text[][] not null - ) - `, - ); - await ctx.db.execute( - sql` - create table tictactoe ( - squares integer[3][3] not null - ) - `, - ); -}); - -test.serial('select all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - t.assert(result[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(result[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('select sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select typed sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('select distinct', async (t) => { - const { db } = t.context; - - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - t.deepEqual(users1, [{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); - - t.deepEqual(users2.length, 2); - t.deepEqual(users2[0]?.id, 1); - t.deepEqual(users2[1]?.id, 2); - - t.deepEqual(users3.length, 2); - t.deepEqual(users3[0]?.name, 'Jane'); - t.deepEqual(users3[1]?.name, 'John'); -}); - -test.serial('insert returning sql', async (t) => { - const { db } = t.context; - - const users = await db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('delete returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .delete(usersTable) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JOHN' }]); -}); - -test.serial('update returning sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - t.deepEqual(users, [{ name: 'JANE' }]); -}); - -test.serial('update with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('update with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'Jane' }]); -}); - -test.serial('delete with returning all fields', async (t) => { - const { db } = t.context; - - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - t.assert(users[0]!.createdAt instanceof Date); // eslint-disable-line no-instanceof/no-instanceof - t.assert(Math.abs(users[0]!.createdAt.getTime() - now) < 100); - t.deepEqual(users, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test.serial('delete with returning partial', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - t.deepEqual(users, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert + select', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - t.deepEqual(result, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - t.deepEqual(result2, [ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test.serial('json insert', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }) - .from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test.serial('char insert', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Austin', state: 'TX' }]); -}); - -test.serial('char update', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, [{ id: 1, name: 'Atlanta', state: 'GA' }]); -}); - -test.serial('char delete', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - t.deepEqual(result, []); -}); - -test.serial('insert with overridden default values', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test.serial('insert many', async (t) => { - const { db } = t.context; - - await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('insert many with returning', async (t) => { - const { db } = t.context; - - const result = await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - t.deepEqual(result, [ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test.serial('select with group by as field', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.name); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as sql + column', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by as column + sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - t.deepEqual(result, [{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test.serial('select with group by complex query', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - t.deepEqual(result, [{ name: 'Jane' }]); -}); - -test.serial('build query', async (t) => { - const { db } = t.context; - - const query = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - t.deepEqual(query, { - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test.serial('insert sql', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('partial join with alias', async (t) => { - const { db } = t.context; - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }) - .from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - t.deepEqual(result, [ - { - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }, - ]); -}); - -test.serial('full join with alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - t.deepEqual(result, [{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from alias', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - t.deepEqual(result, [{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert with spaces', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - t.deepEqual(result, [{ id: 1, name: 'Jo h n' }]); -}); - -test.serial('prepared statement', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const statement = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement reuse', async (t) => { - const { db } = t.context; - - const stmt = db - .insert(usersTable) - .values({ - verified: true, - name: placeholder('name'), - }) - .prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }) - .from(usersTable); - - t.deepEqual(result, [ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test.serial('prepared statement with placeholder in .where', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); -}); - -test.serial('prepared statement with placeholder in .limit', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - t.is(result.length, 1); -}); - -test.serial('prepared statement with placeholder in .offset', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - t.deepEqual(result, [{ id: 2, name: 'John1' }]); -}); - -// TODO change tests to new structure -test.serial('migrator : default migration strategy', async (t) => { - const { db } = t.context; - - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle"."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom schema', async (t) => { - const { db } = t.context; - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsSchema: customSchema }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from ${sql.identifier(customSchema)}."__drizzle_migrations";`); - t.true(rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}."__drizzle_migrations"`); -}); - -test.serial('migrator : migrate with custom table', async (t) => { - const { db } = t.context; - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute(sql`select * from "drizzle".${sql.identifier(customTable)};`); - t.true(rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table "drizzle".${sql.identifier(customTable)}`); -}); - -test.serial('migrator : migrate with custom table and custom schema', async (t) => { - const { db } = t.context; - const customTable = randomString(); - const customSchema = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists "drizzle"."__drizzle_migrations"`); - - await migrate(db, { - migrationsFolder: './drizzle2/pg', - migrationsTable: customTable, - migrationsSchema: customSchema, - }); - - // test if the custom migrations table was created - const { rowCount } = await db.execute( - sql`select * from ${sql.identifier(customSchema)}.${sql.identifier(customTable)};`, - ); - t.true(rowCount > 0); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - t.deepEqual(result, [{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customSchema)}.${sql.identifier(customTable)}`); -}); - -test.serial('insert via db.execute + select via db.execute', async (t) => { - const { db } = t.context; - - await db.execute( - sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`, - ); - - const result = await db.execute<{ id: number; name: string }>( - sql`select id, name from "users"`, - ); - t.deepEqual(result.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute + returning', async (t) => { - const { db } = t.context; - - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert via db.execute w/ query builder', async (t) => { - const { db } = t.context; - - const inserted = await db.execute>( - db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ id: usersTable.id, name: usersTable.name }), - ); - t.deepEqual(inserted.rows, [{ id: 1, name: 'John' }]); -}); - -test.serial('build query insert with onConflict do update', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do update / multiple columns', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id","name") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test.serial('build query insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('build query insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - t.deepEqual(query, { - sql: 'insert into "users" ("name", "jsonb") values ($1, $2) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test.serial('insert with onConflict do update', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John1' }]); -}); - -test.serial('insert with onConflict do nothing', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('insert with onConflict do nothing + target', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - t.deepEqual(res, [{ id: 1, name: 'John' }]); -}); - -test.serial('left join (flat object fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test.serial('left join (grouped fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test.serial('left join (all fields)', async (t) => { - const { db } = t.context; - - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select() - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - t.deepEqual(res, [ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - state: null, - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test.serial('join subquery', async (t) => { - const { db } = t.context; - - await db - .insert(courseCategoriesTable) - .values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db - .insert(coursesTable) - .values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - t.deepEqual(res, [ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test.serial('with ... select', async (t) => { - const { db } = t.context; - - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - t.deepEqual(result, [ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test.serial('select from subquery sql', async (t) => { - const { db } = t.context; - - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - t.deepEqual(res, [{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test.serial('select a field without joining its table', (t) => { - const { db } = t.context; - - t.throws(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')); -}); - -test.serial('select all fields from subquery without alias', (t) => { - const { db } = t.context; - - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - t.throws(() => db.select().from(sq).prepare('query')); -}); - -test.serial('select count()', async (t) => { - const { db } = t.context; - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - t.deepEqual(res, [{ count: '2' }]); -}); - -test.serial('select count w/ custom mapper', async (t) => { - const { db } = t.context; - - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - t.deepEqual(res, [{ count: 2 }]); -}); - -test.serial('network types', async (t) => { - const { db } = t.context; - - const value: typeof network.$inferSelect = { - inet: '127.0.0.1', - cidr: '192.168.100.128/25', - macaddr: '08:00:2b:01:02:03', - macaddr8: '08:00:2b:01:02:03:04:05', - }; - - await db.insert(network).values(value); - - const res = await db.select().from(network); - - t.deepEqual(res, [value]); -}); - -test.serial('array types', async (t) => { - const { db } = t.context; - - const values: typeof salEmp.$inferSelect[] = [ - { - name: 'John', - payByQuarter: [10000, 10000, 10000, 10000], - schedule: [['meeting', 'lunch'], ['training', 'presentation']], - }, - { - name: 'Carol', - payByQuarter: [20000, 25000, 25000, 25000], - schedule: [['breakfast', 'consulting'], ['meeting', 'lunch']], - }, - ]; - - await db.insert(salEmp).values(values); - - const res = await db.select().from(salEmp); - - t.deepEqual(res, values); -}); - -test.serial('select for ...', (t) => { - const { db } = t.context; - - { - const query = db - .select() - .from(users2Table) - .for('update') - .toSQL(); - - t.regex( - query.sql, - / for update$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('update', { of: [users2Table, coursesTable] }) - .toSQL(); - - t.regex( - query.sql, - / for update of "users2", "courses"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table }) - .toSQL(); - - t.regex( - query.sql, - /for no key update of "users2"$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('no key update', { of: users2Table, skipLocked: true }) - .toSQL(); - - t.regex( - query.sql, - / for no key update of "users2" skip locked$/, - ); - } - - { - const query = db - .select() - .from(users2Table) - .for('share', { of: users2Table, noWait: true }) - .toSQL(); - - t.regex( - query.sql, - // eslint-disable-next-line unicorn/better-regex - /for share of "users2" no wait$/, - ); - } -}); - -test.serial('having', async (t) => { - const { db } = t.context; - - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - t.deepEqual(result, [ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -test.serial('view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop view ${newYorkers1}`); -}); - -test.serial('materialized view', async (t) => { - const { db } = t.context; - - const newYorkers1 = pgMaterializedView('new_yorkers') - .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - - const newYorkers2 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - - const newYorkers3 = pgMaterializedView('new_yorkers', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }).existing(); - - await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - - await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - - await db.insert(users2Table).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 1 }, - { name: 'Jack', cityId: 2 }, - ]); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, []); - } - - await db.refreshMaterializedView(newYorkers1); - - { - const result = await db.select().from(newYorkers1); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers2); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select().from(newYorkers3); - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 2, name: 'Jane', cityId: 1 }, - ]); - } - - { - const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); - t.deepEqual(result, [ - { name: 'John' }, - { name: 'Jane' }, - ]); - } - - await db.execute(sql`drop materialized view ${newYorkers1}`); -}); - -// TODO: copy to SQLite and MySQL, add to docs -test.serial('select from raw sql', async (t) => { - const { db } = t.context; - - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John' }, - ]); -}); - -test.serial('select from raw sql with joins', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - t.deepEqual(result, [ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from select', async (t) => { - const { db } = t.context; - - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('join on aliased sql from with clause', async (t) => { - const { db } = t.context; - - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect>; - - t.deepEqual(result, [ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test.serial('prefixed table', async (t) => { - const { db } = t.context; - - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('select from enum', async (t) => { - const { db } = t.context; - - const muscleEnum = pgEnum('muscle', [ - 'abdominals', - 'hamstrings', - 'adductors', - 'quadriceps', - 'biceps', - 'shoulders', - 'chest', - 'middle_back', - 'calves', - 'glutes', - 'lower_back', - 'lats', - 'triceps', - 'traps', - 'forearms', - 'neck', - 'abductors', - ]); - - const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - - const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - - const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - - const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - - const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - - const exercises = pgTable('exercises', { - id: serial('id').primaryKey(), - name: varchar('name').notNull(), - force: forceEnum('force'), - level: levelEnum('level'), - mechanic: mechanicEnum('mechanic'), - equipment: equipmentEnum('equipment'), - instructions: text('instructions'), - category: categoryEnum('category'), - primaryMuscles: muscleEnum('primary_muscles').array(), - secondaryMuscles: muscleEnum('secondary_muscles').array(), - createdAt: timestamp('created_at').notNull().default(sql`now()`), - updatedAt: timestamp('updated_at').notNull().default(sql`now()`), - }); - - await db.execute(sql`drop table if exists ${exercises}`); - await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - - await db.execute( - sql`create type ${ - name(muscleEnum.enumName) - } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, - ); - await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); - await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); - await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); - await db.execute( - sql`create type ${ - name(equipmentEnum.enumName) - } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, - ); - await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); - await db.execute(sql` - create table ${exercises} ( - id serial primary key, - name varchar not null, - force force, - level level, - mechanic mechanic, - equipment equipment, - instructions text, - category category, - primary_muscles muscle[], - secondary_muscles muscle[], - created_at timestamp not null default now(), - updated_at timestamp not null default now() - ) - `); - - await db.insert(exercises).values({ - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - }); - - const result = await db.select().from(exercises); - - t.deepEqual(result, [ - { - id: 1, - name: 'Bench Press', - force: 'isotonic', - level: 'beginner', - mechanic: 'compound', - equipment: 'barbell', - instructions: - '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.', - category: 'upper_body', - primaryMuscles: ['chest', 'triceps'], - secondaryMuscles: ['shoulders', 'traps'], - createdAt: result[0]!.createdAt, - updatedAt: result[0]!.updatedAt, - }, - ]); - - await db.execute(sql`drop table ${exercises}`); - await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); - await db.execute(sql`drop type ${name(forceEnum.enumName)}`); - await db.execute(sql`drop type ${name(levelEnum.enumName)}`); - await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); - await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); - await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -}); - -test.serial('orderBy with aliased column', (t) => { - const { db } = t.context; - - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - t.deepEqual(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test.serial('select from sql', async (t) => { - const { db } = t.context; - - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - await t.notThrowsAsync(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ); -}); - -test.serial('timestamp timezone', async (t) => { - const { db } = t.context; - - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - t.assert(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 2000); - t.assert(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 2000); - - // check that the timestamps are set correctly for non default times - t.assert(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 2000); - t.assert(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 2000); -}); - -test.serial('transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = pgTable('products_transactions', { - id: serial('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - - await db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 90 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test.serial('transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await t.throwsAsync(async () => - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - - const result = await db.select().from(users); - - t.deepEqual(result, []); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 200 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('nested transaction rollback', async (t) => { - const { db } = t.context; - - const users = pgTable('users_nested_transactions_rollback', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table users_nested_transactions_rollback (id serial not null primary key, balance integer not null)`, - ); - - await db.transaction(async (tx) => { - await tx.insert(users).values({ balance: 100 }); - - await t.throwsAsync(async () => - await tx.transaction(async (tx) => { - await tx.update(users).set({ balance: 200 }); - tx.rollback(); - }), { instanceOf: TransactionRollbackError }); - }); - - const result = await db.select().from(users); - - t.deepEqual(result, [{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join subquery with join', async (t) => { - const { db } = t.context; - - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - t.deepEqual(mainQuery, [{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -test.serial('subquery with view', async (t) => { - const { db } = t.context; - - const users = pgTable('users_subquery_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.$with('sq').as(db.select().from(newYorkers)); - const result = await db.with(sq).select().from(sq); - - t.deepEqual(result, [ - { id: 1, name: 'John', cityId: 1 }, - { id: 3, name: 'Jack', cityId: 1 }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('join view as subquery', async (t) => { - const { db } = t.context; - - const users = pgTable('users_join_view', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop view if exists ${newYorkers}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - - await db.insert(users).values([ - { name: 'John', cityId: 1 }, - { name: 'Jane', cityId: 2 }, - { name: 'Jack', cityId: 1 }, - { name: 'Jill', cityId: 2 }, - ]); - - const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - - const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - - t.deepEqual(result, [ - { - users_join_view: { id: 1, name: 'John', cityId: 1 }, - new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, - }, - { - users_join_view: { id: 2, name: 'Jane', cityId: 2 }, - new_yorkers_sq: null, - }, - { - users_join_view: { id: 3, name: 'Jack', cityId: 1 }, - new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, - }, - { - users_join_view: { id: 4, name: 'Jill', cityId: 2 }, - new_yorkers_sq: null, - }, - ]); - - await db.execute(sql`drop view ${newYorkers}`); - await db.execute(sql`drop table ${users}`); -}); - -test.serial('table selection with single table', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - - await db.insert(users).values({ name: 'John', cityId: 1 }); - - const result = await db.select({ users }).from(users); - - t.deepEqual(result, [{ users: { id: 1, name: 'John', cityId: 1 } }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('set null to jsonb field', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - jsonb: jsonb('jsonb'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, jsonb jsonb)`, - ); - - const result = await db.insert(users).values({ jsonb: null }).returning(); - - t.deepEqual(result, [{ id: 1, jsonb: null }]); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('insert undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.notThrowsAsync(async () => await db.insert(users).values({ name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('update undefined', async (t) => { - const { db } = t.context; - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await t.throwsAsync(async () => await db.update(users).set({ name: undefined })); - await t.notThrowsAsync(async () => await db.update(users).set({ id: 1, name: undefined })); - - await db.execute(sql`drop table ${users}`); -}); - -test.serial('array operators', async (t) => { - const { db } = t.context; - - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - t.deepEqual(contains, [{ id: 3 }, { id: 5 }]); - t.deepEqual(contained, [{ id: 1 }, { id: 2 }, { id: 3 }]); - t.deepEqual(overlaps, [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - t.deepEqual(withSubQuery, [{ id: 1 }, { id: 3 }, { id: 5 }]); -}); diff --git a/integration-tests/tests/__old/xata-http.test.ts b/integration-tests/tests/__old/xata-http.test.ts deleted file mode 100644 index cce78cdc6..000000000 --- a/integration-tests/tests/__old/xata-http.test.ts +++ /dev/null @@ -1,2391 +0,0 @@ -import 'dotenv/config'; - -import { - and, - arrayContained, - arrayContains, - arrayOverlaps, - asc, - eq, - gt, - gte, - inArray, - lt, - name, - placeholder, - type SQL, - sql, - type SQLWrapper, -} from 'drizzle-orm'; -import { - alias, - boolean, - char, - cidr, - date, - inet, - integer, - interval, - jsonb, - macaddr, - macaddr8, - type PgColumn, - pgTable, - pgTableCreator, - serial, - text, - time, - timestamp, - uuid as pgUuid, -} from 'drizzle-orm/pg-core'; -import { drizzle } from 'drizzle-orm/xata-http'; -import type { XataHttpClient, XataHttpDatabase } from 'drizzle-orm/xata-http'; -import { migrate } from 'drizzle-orm/xata-http/migrator'; -import { v4 as uuid } from 'uuid'; -import { beforeAll, beforeEach, expect, test } from 'vitest'; -import { type Equal, Expect, randomString } from '../utils.ts'; -import { getXataClient } from '../xata/xata.ts'; - -const ENABLE_LOGGING = false; - -const usersTable = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - verified: boolean('verified').notNull().default(false), - jsonb: jsonb('jsonb').$type(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), -}); - -const citiesTable = pgTable('cities', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - state: char('state', { length: 2 }), -}); - -const users2Table = pgTable('users2', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').references(() => citiesTable.id), -}); - -const coursesTable = pgTable('courses', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - categoryId: integer('category_id').references(() => courseCategoriesTable.id), -}); - -const courseCategoriesTable = pgTable('course_categories', { - id: serial('id').primaryKey(), - name: text('name').notNull(), -}); - -const orders = pgTable('orders', { - id: serial('id').primaryKey(), - region: text('region').notNull(), - product: text('product').notNull(), - amount: integer('amount').notNull(), - quantity: integer('quantity').notNull(), -}); - -const network = pgTable('network_table', { - inet: inet('inet').notNull(), - cidr: cidr('cidr').notNull(), - macaddr: macaddr('macaddr').notNull(), - macaddr8: macaddr8('macaddr8').notNull(), -}); - -const salEmp = pgTable('sal_emp', { - name: text('name'), - payByQuarter: integer('pay_by_quarter').array(), - schedule: text('schedule').array().array(), -}); - -const _tictactoe = pgTable('tictactoe', { - squares: integer('squares').array(3).array(3), -}); - -const usersMigratorTable = pgTable('users12', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - email: text('email').notNull(), -}); - -let db: XataHttpDatabase; -let client: XataHttpClient; - -beforeAll(async () => { - const apiKey = process.env['XATA_API_KEY']; - if (!apiKey) { - throw new Error('XATA_API_KEY is not defined'); - } - - client = getXataClient(); - db = drizzle(client, { logger: ENABLE_LOGGING }); -}); - -beforeEach(async () => { - await db.execute(sql`drop table if exists users cascade`); - await db.execute(sql`drop table if exists cities cascade`); - await db.execute(sql`drop table if exists users2 cascade`); - await db.execute(sql`drop table if exists course_categories cascade`); - await db.execute(sql`drop table if exists courses cascade`); - await db.execute(sql`drop table if exists orders cascade`); - await db.execute(sql`drop table if exists network_table cascade`); - await db.execute(sql`drop table if exists sal_emp cascade`); - await db.execute(sql`drop table if exists tictactoe cascade`); - - await client.sql({ - statement: ` - create table users ( - id serial primary key, - name text not null, - verified boolean not null default false, - jsonb jsonb, - created_at timestamptz not null default now() - ) - `, - }); - await client.sql({ - statement: ` - create table cities ( - id serial primary key, - name text not null, - state char(2) - ) - `, - }); - await client.sql({ - statement: ` - create table users2 ( - id serial primary key, - name text not null, - city_id integer references cities(id) - ) - `, - }); - await client.sql({ - statement: ` - create table course_categories ( - id serial primary key, - name text not null - ) - `, - }); - await client.sql({ - statement: ` - create table courses ( - id serial primary key, - name text not null, - category_id integer references course_categories(id) - ) - `, - }); - await client.sql({ - statement: ` - create table orders ( - id serial primary key, - region text not null, - product text not null, - amount integer not null, - quantity integer not null - ) - `, - }); - await client.sql({ - statement: ` - create table network_table ( - inet inet not null, - cidr cidr not null, - macaddr macaddr not null, - macaddr8 macaddr8 not null - ) - `, - }); - await client.sql({ - statement: ` - create table sal_emp ( - name text not null, - pay_by_quarter integer[] not null, - schedule text[][] not null - ) - `, - }); - await client.sql({ - statement: ` - create table tictactoe ( - squares integer[3][3] not null - ) - `, - }); -}); - -test('select all fields', async () => { - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - - expect(result[0]!.createdAt instanceof Date).toBeTruthy(); // eslint-disable-line no-instanceof/no-instanceof - expect(Math.abs(result[0]!.createdAt.getTime() - now) < 1000).toBeTruthy(); - expect(result).toEqual([ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test('select sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .select({ - name: sql`upper(${usersTable.name})`, - }) - .from(usersTable); - - expect(users).toEqual([{ name: 'JOHN' }]); -}); - -test('select typed sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - - const users = await db.select({ - name: sql`upper(${usersTable.name})`, - }).from(usersTable); - - expect(users).toEqual([{ name: 'JOHN' }]); -}); - -test('select distinct', async () => { - const usersDistinctTable = pgTable('users_distinct', { - id: integer('id').notNull(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${usersDistinctTable}`); - await db.execute(sql`create table ${usersDistinctTable} (id integer, name text)`); - - await db.insert(usersDistinctTable).values([ - { id: 1, name: 'John' }, - { id: 1, name: 'John' }, - { id: 2, name: 'John' }, - { id: 1, name: 'Jane' }, - ]); - const users1 = await db.selectDistinct().from(usersDistinctTable).orderBy( - usersDistinctTable.id, - usersDistinctTable.name, - ); - const users2 = await db.selectDistinctOn([usersDistinctTable.id]).from(usersDistinctTable).orderBy( - usersDistinctTable.id, - ); - const users3 = await db.selectDistinctOn([usersDistinctTable.name], { name: usersDistinctTable.name }).from( - usersDistinctTable, - ).orderBy(usersDistinctTable.name); - - await db.execute(sql`drop table ${usersDistinctTable}`); - - expect(users1).toEqual([{ id: 1, name: 'Jane' }, { id: 1, name: 'John' }, { id: 2, name: 'John' }]); - - expect(users2.length).toEqual(2); - expect(users2[0]?.id).toEqual(1); - expect(users2[1]?.id).toEqual(2); - - expect(users3.length).toEqual(2); - expect(users3[0]?.name, 'Jane'); - expect(users3[1]?.name, 'John'); -}); - -test('insert returning sql', async () => { - const users = await db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - expect(users).toEqual([{ name: 'JOHN' }]); -}); - -test('delete returning sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .delete(usersTable) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - expect(users).toEqual([{ name: 'JOHN' }]); -}); - -test('update returning sql', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - name: sql`upper(${usersTable.name})`, - }); - - expect(users).toEqual([{ name: 'JANE' }]); -}); - -test('update with returning all fields', async () => { - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning(); - - expect(users[0]!.createdAt instanceof Date).toBeTruthy(); // eslint-disable-line no-instanceof/no-instanceof - expect(Math.abs(users[0]!.createdAt.getTime() - now) < 1000).toBeTruthy(); - expect(users).toEqual([ - { id: 1, name: 'Jane', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test('update with returning partial', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db - .update(usersTable) - .set({ name: 'Jane' }) - .where(eq(usersTable.name, 'John')) - .returning({ - id: usersTable.id, - name: usersTable.name, - }); - - expect(users).toEqual([{ id: 1, name: 'Jane' }]); -}); - -test('delete with returning all fields', async () => { - const now = Date.now(); - - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning(); - - expect(users[0]!.createdAt instanceof Date).toBeTruthy(); // eslint-disable-line no-instanceof/no-instanceof - expect(Math.abs(users[0]!.createdAt.getTime() - now) < 1000).toBeTruthy(); - expect(users).toEqual([ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: users[0]!.createdAt }, - ]); -}); - -test('delete with returning partial', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const users = await db.delete(usersTable).where(eq(usersTable.name, 'John')).returning({ - id: usersTable.id, - name: usersTable.name, - }); - - expect(users).toEqual([{ id: 1, name: 'John' }]); -}); - -test('insert + select', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const result = await db.select().from(usersTable); - expect(result).toEqual([ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result[0]!.createdAt }, - ]); - - await db.insert(usersTable).values({ name: 'Jane' }); - const result2 = await db.select().from(usersTable); - expect(result2).toEqual([ - { id: 1, name: 'John', verified: false, jsonb: null, createdAt: result2[0]!.createdAt }, - { id: 2, name: 'Jane', verified: false, jsonb: null, createdAt: result2[1]!.createdAt }, - ]); -}); - -test('json insert', async () => { - await db.insert(usersTable).values({ name: 'John', jsonb: ['foo', 'bar'] }); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - }) - .from(usersTable); - - expect(result).toEqual([{ id: 1, name: 'John', jsonb: ['foo', 'bar'] }]); -}); - -test('char insert', async () => { - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - expect(result).toEqual([{ id: 1, name: 'Austin', state: 'TX' }]); -}); - -test('char update', async () => { - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.update(citiesTable).set({ name: 'Atlanta', state: 'GA' }).where(eq(citiesTable.id, 1)); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - expect(result).toEqual([{ id: 1, name: 'Atlanta', state: 'GA' }]); -}); - -test('char delete', async () => { - await db.insert(citiesTable).values({ name: 'Austin', state: 'TX' }); - await db.delete(citiesTable).where(eq(citiesTable.state, 'TX')); - const result = await db - .select({ id: citiesTable.id, name: citiesTable.name, state: citiesTable.state }) - .from(citiesTable); - - expect(result).toEqual([]); -}); - -test('insert with overridden default values', async () => { - await db.insert(usersTable).values({ name: 'John', verified: true }); - const result = await db.select().from(usersTable); - - expect(result).toEqual([ - { id: 1, name: 'John', verified: true, jsonb: null, createdAt: result[0]!.createdAt }, - ]); -}); - -test('insert many', async () => { - await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]); - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }) - .from(usersTable); - - expect(result).toEqual([ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test('insert many with returning', async () => { - const result = await db - .insert(usersTable) - .values([ - { name: 'John' }, - { name: 'Bruce', jsonb: ['foo', 'bar'] }, - { name: 'Jane' }, - { name: 'Austin', verified: true }, - ]) - .returning({ - id: usersTable.id, - name: usersTable.name, - jsonb: usersTable.jsonb, - verified: usersTable.verified, - }); - - expect(result).toEqual([ - { id: 1, name: 'John', jsonb: null, verified: false }, - { id: 2, name: 'Bruce', jsonb: ['foo', 'bar'], verified: false }, - { id: 3, name: 'Jane', jsonb: null, verified: false }, - { id: 4, name: 'Austin', jsonb: null, verified: true }, - ]); -}); - -test('select with group by as field', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.name); - - expect(result).toEqual([{ name: 'Jane' }, { name: 'John' }]); -}); - -test('select with group by as sql', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`); - - expect(result).toEqual([{ name: 'Jane' }, { name: 'John' }]); -}); - -test('select with group by as sql + column', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(sql`${usersTable.name}`, usersTable.id); - - expect(result).toEqual([{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test('select with group by as column + sql', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`); - - expect(result).toEqual([{ name: 'Jane' }, { name: 'Jane' }, { name: 'John' }]); -}); - -test('select with group by complex query', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }, { name: 'Jane' }]); - - const result = await db - .select({ name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, sql`${usersTable.name}`) - .orderBy(asc(usersTable.name)) - .limit(1); - - expect(result).toEqual([{ name: 'Jane' }]); -}); - -test('build query', async () => { - const query = db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .groupBy(usersTable.id, usersTable.name) - .toSQL(); - - expect(query).toEqual({ - sql: 'select "id", "name" from "users" group by "users"."id", "users"."name"', - params: [], - }); -}); - -test('insert sql', async () => { - await db.insert(usersTable).values({ name: sql`${'John'}` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - expect(result).toEqual([{ id: 1, name: 'John' }]); -}); - -test('partial join with alias', async () => { - const customerAlias = alias(usersTable, 'customer'); - - await db.insert(usersTable).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select({ - user: { - id: usersTable.id, - name: usersTable.name, - }, - customer: { - id: customerAlias.id, - name: customerAlias.name, - }, - }) - .from(usersTable) - .leftJoin(customerAlias, eq(customerAlias.id, 11)) - .where(eq(usersTable.id, 10)); - - expect(result).toEqual([ - { - user: { id: 10, name: 'Ivan' }, - customer: { id: 11, name: 'Hans' }, - }, - ]); -}); - -test('full join with alias', async () => { - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(users) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(users.id, 10)); - - expect(result).toEqual([{ - users: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('select from alias', async () => { - const pgTable = pgTableCreator((name) => `prefixed_${name}`); - - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`create table ${users} (id serial primary key, name text not null)`); - - const user = alias(users, 'user'); - const customers = alias(users, 'customer'); - - await db.insert(users).values([{ id: 10, name: 'Ivan' }, { id: 11, name: 'Hans' }]); - const result = await db - .select() - .from(user) - .leftJoin(customers, eq(customers.id, 11)) - .where(eq(user.id, 10)); - - expect(result).toEqual([{ - user: { - id: 10, - name: 'Ivan', - }, - customer: { - id: 11, - name: 'Hans', - }, - }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('insert with spaces', async () => { - await db.insert(usersTable).values({ name: sql`'Jo h n'` }); - const result = await db.select({ id: usersTable.id, name: usersTable.name }).from(usersTable); - - expect(result).toEqual([{ id: 1, name: 'Jo h n' }]); -}); - -test('prepared statement', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const statement = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .prepare('statement1'); - const result = await statement.execute(); - - expect(result).toEqual([{ id: 1, name: 'John' }]); -}); - -test('prepared statement reuse', async () => { - const stmt = db - .insert(usersTable) - .values({ - verified: true, - name: placeholder('name'), - }) - .prepare('stmt2'); - - for (let i = 0; i < 10; i++) { - await stmt.execute({ name: `John ${i}` }); - } - - const result = await db - .select({ - id: usersTable.id, - name: usersTable.name, - verified: usersTable.verified, - }) - .from(usersTable); - - expect(result).toEqual([ - { id: 1, name: 'John 0', verified: true }, - { id: 2, name: 'John 1', verified: true }, - { id: 3, name: 'John 2', verified: true }, - { id: 4, name: 'John 3', verified: true }, - { id: 5, name: 'John 4', verified: true }, - { id: 6, name: 'John 5', verified: true }, - { id: 7, name: 'John 6', verified: true }, - { id: 8, name: 'John 7', verified: true }, - { id: 9, name: 'John 8', verified: true }, - { id: 10, name: 'John 9', verified: true }, - ]); -}); - -test('prepared statement with placeholder in .where', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .prepare('stmt3'); - const result = await stmt.execute({ id: 1 }); - - expect(result).toEqual([{ id: 1, name: 'John' }]); -}); - -test('prepared statement with placeholder in .limit', async () => { - await db.insert(usersTable).values({ name: 'John' }); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .where(eq(usersTable.id, placeholder('id'))) - .limit(placeholder('limit')) - .prepare('stmt_limit'); - - const result = await stmt.execute({ id: 1, limit: 1 }); - - expect(result).toEqual([{ id: 1, name: 'John' }]); - expect(result.length).toEqual(1); -}); - -test('prepared statement with placeholder in .offset', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'John1' }]); - const stmt = db - .select({ - id: usersTable.id, - name: usersTable.name, - }) - .from(usersTable) - .offset(placeholder('offset')) - .prepare('stmt_offset'); - - const result = await stmt.execute({ offset: 1 }); - - expect(result).toEqual([{ id: 2, name: 'John1' }]); -}); - -test('migrator : default migration strategy', async () => { - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/pg' }); - - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - - const result = await db.select().from(usersMigratorTable); - - expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table __drizzle_migrations`); -}); - -test('migrator : migrate with custom table', async () => { - const customTable = randomString(); - await db.execute(sql`drop table if exists all_columns`); - await db.execute(sql`drop table if exists users12`); - await db.execute(sql`drop table if exists __drizzle_migrations`); - - await migrate(db, { migrationsFolder: './drizzle2/pg', migrationsTable: customTable }); - - // test if the custom migrations table was created - const { records } = await db.execute(sql`select * from ${sql.identifier(customTable)};`); - expect(records.length > 0).toBeTruthy(); - - // test if the migrated table are working as expected - await db.insert(usersMigratorTable).values({ name: 'John', email: 'email' }); - const result = await db.select().from(usersMigratorTable); - expect(result).toEqual([{ id: 1, name: 'John', email: 'email' }]); - - await db.execute(sql`drop table all_columns`); - await db.execute(sql`drop table users12`); - await db.execute(sql`drop table ${sql.identifier(customTable)}`); -}); - -test('insert via db.execute + select via db.execute', async () => { - await db.execute( - sql`insert into ${usersTable} (${name(usersTable.name.name)}) values (${'John'})`, - ); - - const result = await db.execute<{ id: number; name: string }>( - sql`select id, name from "users"`, - ); - - expect(result.records).toEqual([{ id: 1, name: 'John' }]); -}); - -test('insert via db.execute + returning', async () => { - const inserted = await db.execute<{ id: number; name: string }>( - sql`insert into ${usersTable} (${ - name( - usersTable.name.name, - ) - }) values (${'John'}) returning ${usersTable.id}, ${usersTable.name}`, - ); - expect(inserted.records).toEqual([{ id: 1, name: 'John' }]); -}); - -test('insert via db.execute w/ query builder', async () => { - const inserted = await db.execute>( - db - .insert(usersTable) - .values({ name: 'John' }) - .returning({ id: usersTable.id, name: usersTable.name }), - ); - expect(inserted.records).toEqual([{ id: 1, name: 'John' }]); -}); - -test('build query insert with onConflict do update', async () => { - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }) - .toSQL(); - - expect(query).toEqual({ - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do update set "name" = $3', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test('build query insert with onConflict do update / multiple columns', async () => { - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoUpdate({ target: [usersTable.id, usersTable.name], set: { name: 'John1' } }) - .toSQL(); - - expect(query).toEqual({ - 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', - params: ['John', '["foo","bar"]', 'John1'], - }); -}); - -test('build query insert with onConflict do nothing', async () => { - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing() - .toSQL(); - - expect(query).toEqual({ - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test('build query insert with onConflict do nothing + target', async () => { - const query = db - .insert(usersTable) - .values({ name: 'John', jsonb: ['foo', 'bar'] }) - .onConflictDoNothing({ target: usersTable.id }) - .toSQL(); - - expect(query).toEqual({ - sql: - 'insert into "users" ("id", "name", "verified", "jsonb", "created_at") values (default, $1, default, $2, default) on conflict ("id") do nothing', - params: ['John', '["foo","bar"]'], - }); -}); - -test('insert with onConflict do update', async () => { - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoUpdate({ target: usersTable.id, set: { name: 'John1' } }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - expect(res).toEqual([{ id: 1, name: 'John1' }]); -}); - -test('insert with onConflict do nothing', async () => { - await db.insert(usersTable).values({ name: 'John' }); - - await db.insert(usersTable).values({ id: 1, name: 'John' }).onConflictDoNothing(); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - expect(res).toEqual([{ id: 1, name: 'John' }]); -}); - -test('insert with onConflict do nothing + target', async () => { - await db.insert(usersTable).values({ name: 'John' }); - - await db - .insert(usersTable) - .values({ id: 1, name: 'John' }) - .onConflictDoNothing({ target: usersTable.id }); - - const res = await db - .select({ id: usersTable.id, name: usersTable.name }) - .from(usersTable) - .where(eq(usersTable.id, 1)); - - expect(res).toEqual([{ id: 1, name: 'John' }]); -}); - -test('left join (flat object fields)', async () => { - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - userId: users2Table.id, - userName: users2Table.name, - cityId: citiesTable.id, - cityName: citiesTable.name, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - expect(res).toEqual([ - { userId: 1, userName: 'John', cityId, cityName: 'Paris' }, - { userId: 2, userName: 'Jane', cityId: null, cityName: null }, - ]); -}); - -test('left join (grouped fields)', async () => { - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select({ - id: users2Table.id, - user: { - name: users2Table.name, - nameUpper: sql`upper(${users2Table.name})`, - }, - city: { - id: citiesTable.id, - name: citiesTable.name, - nameUpper: sql`upper(${citiesTable.name})`, - }, - }) - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - expect(res).toEqual([ - { - id: 1, - user: { name: 'John', nameUpper: 'JOHN' }, - city: { id: cityId, name: 'Paris', nameUpper: 'PARIS' }, - }, - { - id: 2, - user: { name: 'Jane', nameUpper: 'JANE' }, - city: null, - }, - ]); -}); - -test('left join (all fields)', async () => { - const { id: cityId } = await db - .insert(citiesTable) - .values([{ name: 'Paris' }, { name: 'London' }]) - .returning({ id: citiesTable.id }) - .then((rows) => rows[0]!); - - await db.insert(users2Table).values([{ name: 'John', cityId }, { name: 'Jane' }]); - - const res = await db - .select() - .from(users2Table) - .leftJoin(citiesTable, eq(users2Table.cityId, citiesTable.id)); - - expect(res).toEqual([ - { - users2: { - id: 1, - name: 'John', - cityId, - }, - cities: { - id: cityId, - name: 'Paris', - state: null, - }, - }, - { - users2: { - id: 2, - name: 'Jane', - cityId: null, - }, - cities: null, - }, - ]); -}); - -test('join subquery', async () => { - await db - .insert(courseCategoriesTable) - .values([ - { name: 'Category 1' }, - { name: 'Category 2' }, - { name: 'Category 3' }, - { name: 'Category 4' }, - ]); - - await db - .insert(coursesTable) - .values([ - { name: 'Development', categoryId: 2 }, - { name: 'IT & Software', categoryId: 3 }, - { name: 'Marketing', categoryId: 4 }, - { name: 'Design', categoryId: 1 }, - ]); - - const sq2 = db - .select({ - categoryId: courseCategoriesTable.id, - category: courseCategoriesTable.name, - total: sql`count(${courseCategoriesTable.id})`, - }) - .from(courseCategoriesTable) - .groupBy(courseCategoriesTable.id, courseCategoriesTable.name) - .as('sq2'); - - const res = await db - .select({ - courseName: coursesTable.name, - categoryId: sq2.categoryId, - }) - .from(coursesTable) - .leftJoin(sq2, eq(coursesTable.categoryId, sq2.categoryId)) - .orderBy(coursesTable.name); - - expect(res).toEqual([ - { courseName: 'Design', categoryId: 1 }, - { courseName: 'Development', categoryId: 2 }, - { courseName: 'IT & Software', categoryId: 3 }, - { courseName: 'Marketing', categoryId: 4 }, - ]); -}); - -test('with ... select', async () => { - await db.insert(orders).values([ - { region: 'Europe', product: 'A', amount: 10, quantity: 1 }, - { region: 'Europe', product: 'A', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 20, quantity: 2 }, - { region: 'Europe', product: 'B', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 30, quantity: 3 }, - { region: 'US', product: 'A', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 40, quantity: 4 }, - { region: 'US', product: 'B', amount: 50, quantity: 5 }, - ]); - - const regionalSales = db - .$with('regional_sales') - .as( - db - .select({ - region: orders.region, - totalSales: sql`sum(${orders.amount})`.as('total_sales'), - }) - .from(orders) - .groupBy(orders.region), - ); - - const topRegions = db - .$with('top_regions') - .as( - db - .select({ - region: regionalSales.region, - }) - .from(regionalSales) - .where( - gt( - regionalSales.totalSales, - db.select({ sales: sql`sum(${regionalSales.totalSales})/10` }).from(regionalSales), - ), - ), - ); - - const result = await db - .with(regionalSales, topRegions) - .select({ - region: orders.region, - product: orders.product, - productUnits: sql`sum(${orders.quantity})::int`, - productSales: sql`sum(${orders.amount})::int`, - }) - .from(orders) - .where(inArray(orders.region, db.select({ region: topRegions.region }).from(topRegions))) - .groupBy(orders.region, orders.product) - .orderBy(orders.region, orders.product); - - expect(result).toEqual([ - { - region: 'Europe', - product: 'A', - productUnits: 3, - productSales: 30, - }, - { - region: 'Europe', - product: 'B', - productUnits: 5, - productSales: 50, - }, - { - region: 'US', - product: 'A', - productUnits: 7, - productSales: 70, - }, - { - region: 'US', - product: 'B', - productUnits: 9, - productSales: 90, - }, - ]); -}); - -test('select from subquery sql', async () => { - await db.insert(users2Table).values([{ name: 'John' }, { name: 'Jane' }]); - - const sq = db - .select({ name: sql`${users2Table.name} || ' modified'`.as('name') }) - .from(users2Table) - .as('sq'); - - const res = await db.select({ name: sq.name }).from(sq); - - expect(res).toEqual([{ name: 'John modified' }, { name: 'Jane modified' }]); -}); - -test('select a field without joining its table', () => { - expect(() => db.select({ name: users2Table.name }).from(usersTable).prepare('query')).toThrowError(); -}); - -test('select all fields from subquery without alias', () => { - const sq = db.$with('sq').as(db.select({ name: sql`upper(${users2Table.name})` }).from(users2Table)); - - expect(() => db.select().from(sq).prepare('query')).toThrowError; -}); - -test('select count()', async () => { - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: sql`count(*)` }).from(usersTable); - - expect(res).toEqual([{ count: 2 }]); -}); - -test('select count w/ custom mapper', async () => { - function count(value: PgColumn | SQLWrapper): SQL; - function count(value: PgColumn | SQLWrapper, alias: string): SQL.Aliased; - function count(value: PgColumn | SQLWrapper, alias?: string): SQL | SQL.Aliased { - const result = sql`count(${value})`.mapWith(Number); - if (!alias) { - return result; - } - return result.as(alias); - } - - await db.insert(usersTable).values([{ name: 'John' }, { name: 'Jane' }]); - - const res = await db.select({ count: count(sql`*`) }).from(usersTable); - - expect(res).toEqual([{ count: 2 }]); -}); - -test.skip('network types', async () => { - const value: typeof network.$inferSelect = { - inet: '127.0.0.1', - cidr: '192.168.100.128/25', - macaddr: '08:00:2b:01:02:03', - macaddr8: '08:00:2b:01:02:03:04:05', - }; - - await db.insert(network).values(value); - - const res = await db.select().from(network); - - expect(res).toEqual([value]); -}); - -test.skip('array types', async () => { - const values: typeof salEmp.$inferSelect[] = [ - { - name: 'John', - payByQuarter: [10000, 10000, 10000, 10000], - schedule: [['meeting', 'lunch'], ['training', 'presentation']], - }, - { - name: 'Carol', - payByQuarter: [20000, 25000, 25000, 25000], - schedule: [['breakfast', 'consulting'], ['meeting', 'lunch']], - }, - ]; - - await db.insert(salEmp).values(values); - - const res = await db.select().from(salEmp); - - expect(res).toEqual(values); -}); - -// test('select for ...', (t) => { -// { -// const query = db -// .select() -// .from(users2Table) -// .for('update') -// .toSQL(); - -// t.regex( -// query.sql, -// / for update$/, -// ); -// } - -// { -// const query = db -// .select() -// .from(users2Table) -// .for('update', { of: [users2Table, coursesTable] }) -// .toSQL(); - -// t.regex( -// query.sql, -// / for update of "users2", "courses"$/, -// ); -// } - -// { -// const query = db -// .select() -// .from(users2Table) -// .for('no key update', { of: users2Table }) -// .toSQL(); - -// t.regex( -// query.sql, -// /for no key update of "users2"$/, -// ); -// } - -// { -// const query = db -// .select() -// .from(users2Table) -// .for('no key update', { of: users2Table, skipLocked: true }) -// .toSQL(); - -// t.regex( -// query.sql, -// / for no key update of "users2" skip locked$/, -// ); -// } - -// { -// const query = db -// .select() -// .from(users2Table) -// .for('share', { of: users2Table, noWait: true }) -// .toSQL(); - -// t.regex( -// query.sql, -// // eslint-disable-next-line unicorn/better-regex -// /for share of "users2" no wait$/, -// ); -// } -// }); - -test('having', async () => { - await db.insert(citiesTable).values([{ name: 'London' }, { name: 'Paris' }, { name: 'New York' }]); - - await db.insert(users2Table).values([{ name: 'John', cityId: 1 }, { name: 'Jane', cityId: 1 }, { - name: 'Jack', - cityId: 2, - }]); - - const result = await db - .select({ - id: citiesTable.id, - name: sql`upper(${citiesTable.name})`.as('upper_name'), - usersCount: sql`count(${users2Table.id})::int`.as('users_count'), - }) - .from(citiesTable) - .leftJoin(users2Table, eq(users2Table.cityId, citiesTable.id)) - .where(({ name }) => sql`length(${name}) >= 3`) - .groupBy(citiesTable.id) - .having(({ usersCount }) => sql`${usersCount} > 0`) - .orderBy(({ name }) => name); - - expect(result).toEqual([ - { - id: 1, - name: 'LONDON', - usersCount: 2, - }, - { - id: 2, - name: 'PARIS', - usersCount: 1, - }, - ]); -}); - -// Not supported in Xata HTTP -// test('view', async () => { -// - -// const newYorkers1 = pgView('new_yorkers') -// .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - -// const newYorkers2 = pgView('new_yorkers', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// cityId: integer('city_id').notNull(), -// }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - -// const newYorkers3 = pgView('new_yorkers', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// cityId: integer('city_id').notNull(), -// }).existing(); - -// await db.execute(sql`create view ${newYorkers1} as ${getViewConfig(newYorkers1).query}`); - -// await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - -// await db.insert(users2Table).values([ -// { name: 'John', cityId: 1 }, -// { name: 'Jane', cityId: 1 }, -// { name: 'Jack', cityId: 2 }, -// ]); - -// { -// const result = await db.select().from(newYorkers1); -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 2, name: 'Jane', cityId: 1 }, -// ]); -// } - -// { -// const result = await db.select().from(newYorkers2); -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 2, name: 'Jane', cityId: 1 }, -// ]); -// } - -// { -// const result = await db.select().from(newYorkers3); -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 2, name: 'Jane', cityId: 1 }, -// ]); -// } - -// { -// const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); -// expect(result, [ -// { name: 'John' }, -// { name: 'Jane' }, -// ]); -// } - -// await db.execute(sql`drop view ${newYorkers1}`); -// }); - -// test('materialized view', async () => { -// - -// const newYorkers1 = pgMaterializedView('new_yorkers') -// .as((qb) => qb.select().from(users2Table).where(eq(users2Table.cityId, 1))); - -// const newYorkers2 = pgMaterializedView('new_yorkers', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// cityId: integer('city_id').notNull(), -// }).as(sql`select * from ${users2Table} where ${eq(users2Table.cityId, 1)}`); - -// const newYorkers3 = pgMaterializedView('new_yorkers', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// cityId: integer('city_id').notNull(), -// }).existing(); - -// await db.execute(sql`create materialized view ${newYorkers1} as ${getMaterializedViewConfig(newYorkers1).query}`); - -// await db.insert(citiesTable).values([{ name: 'New York' }, { name: 'Paris' }]); - -// await db.insert(users2Table).values([ -// { name: 'John', cityId: 1 }, -// { name: 'Jane', cityId: 1 }, -// { name: 'Jack', cityId: 2 }, -// ]); - -// { -// const result = await db.select().from(newYorkers1); -// expect(result, []); -// } - -// await db.refreshMaterializedView(newYorkers1); - -// { -// const result = await db.select().from(newYorkers1); -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 2, name: 'Jane', cityId: 1 }, -// ]); -// } - -// { -// const result = await db.select().from(newYorkers2); -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 2, name: 'Jane', cityId: 1 }, -// ]); -// } - -// { -// const result = await db.select().from(newYorkers3); -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 2, name: 'Jane', cityId: 1 }, -// ]); -// } - -// { -// const result = await db.select({ name: newYorkers1.name }).from(newYorkers1); -// expect(result, [ -// { name: 'John' }, -// { name: 'Jane' }, -// ]); -// } - -// await db.execute(sql`drop materialized view ${newYorkers1}`); -// }); - -// TODO: copy to SQLite and MySQL, add to docs -test('select from raw sql', async () => { - const result = await db.select({ - id: sql`id`, - name: sql`name`, - }).from(sql`(select 1 as id, 'John' as name) as users`); - - Expect>; - - expect(result).toEqual([ - { id: 1, name: 'John' }, - ]); -}); - -test('select from raw sql with joins', async () => { - const result = await db - .select({ - id: sql`users.id`, - name: sql`users.name`, - userCity: sql`users.city`, - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, sql`cities.id = users.id`); - - Expect>; - - expect(result).toEqual([ - { id: 1, name: 'John', userCity: 'New York', cityName: 'Paris' }, - ]); -}); - -test('join on aliased sql from select', async () => { - const result = await db - .select({ - userId: sql`users.id`.as('userId'), - name: sql`users.name`, - userCity: sql`users.city`, - cityId: sql`cities.id`.as('cityId'), - cityName: sql`cities.name`, - }) - .from(sql`(select 1 as id, 'John' as name, 'New York' as city) as users`) - .leftJoin(sql`(select 1 as id, 'Paris' as name) as cities`, (cols) => eq(cols.cityId, cols.userId)); - - Expect< - Equal<{ userId: number; name: string; userCity: string; cityId: number; cityName: string }[], typeof result> - >; - - expect(result).toEqual([ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test('join on aliased sql from with clause', async () => { - const users = db.$with('users').as( - db.select({ - id: sql`id`.as('userId'), - name: sql`name`.as('userName'), - city: sql`city`.as('city'), - }).from( - sql`(select 1 as id, 'John' as name, 'New York' as city) as users`, - ), - ); - - const cities = db.$with('cities').as( - db.select({ - id: sql`id`.as('cityId'), - name: sql`name`.as('cityName'), - }).from( - sql`(select 1 as id, 'Paris' as name) as cities`, - ), - ); - - const result = await db - .with(users, cities) - .select({ - userId: users.id, - name: users.name, - userCity: users.city, - cityId: cities.id, - cityName: cities.name, - }) - .from(users) - .leftJoin(cities, (cols) => eq(cols.cityId, cols.userId)); - - Expect< - Equal<{ userId: number; name: string; userCity: string; cityId: number; cityName: string }[], typeof result> - >; - - expect(result).toEqual([ - { userId: 1, name: 'John', userCity: 'New York', cityId: 1, cityName: 'Paris' }, - ]); -}); - -test('prefixed table', async () => { - const pgTable = pgTableCreator((name) => `myprefix_${name}`); - - const users = pgTable('test_prefixed_table_with_unique_name', { - id: integer('id').primaryKey(), - name: text('name').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table myprefix_test_prefixed_table_with_unique_name (id integer not null primary key, name text not null)`, - ); - - await db.insert(users).values({ id: 1, name: 'John' }); - - const result = await db.select().from(users); - - expect(result).toEqual([{ id: 1, name: 'John' }]); - - await db.execute(sql`drop table ${users}`); -}); - -// Not supported in Xata -// test('select from enum', async () => { -// - -// const muscleEnum = pgEnum('muscle', [ -// 'abdominals', -// 'hamstrings', -// 'adductors', -// 'quadriceps', -// 'biceps', -// 'shoulders', -// 'chest', -// 'middle_back', -// 'calves', -// 'glutes', -// 'lower_back', -// 'lats', -// 'triceps', -// 'traps', -// 'forearms', -// 'neck', -// 'abductors', -// ]); - -// const forceEnum = pgEnum('force', ['isometric', 'isotonic', 'isokinetic']); - -// const levelEnum = pgEnum('level', ['beginner', 'intermediate', 'advanced']); - -// const mechanicEnum = pgEnum('mechanic', ['compound', 'isolation']); - -// const equipmentEnum = pgEnum('equipment', ['barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell']); - -// const categoryEnum = pgEnum('category', ['upper_body', 'lower_body', 'full_body']); - -// const exercises = pgTable('exercises', { -// id: serial('id').primaryKey(), -// name: varchar('name').notNull(), -// force: forceEnum('force'), -// level: levelEnum('level'), -// mechanic: mechanicEnum('mechanic'), -// equipment: equipmentEnum('equipment'), -// instructions: text('instructions'), -// category: categoryEnum('category'), -// primaryMuscles: muscleEnum('primary_muscles').array(), -// secondaryMuscles: muscleEnum('secondary_muscles').array(), -// createdAt: timestamp('created_at').notNull().default(sql`now()`), -// updatedAt: timestamp('updated_at').notNull().default(sql`now()`), -// }); - -// await db.execute(sql`drop table if exists ${exercises}`); -// await db.execute(sql`drop type if exists ${name(muscleEnum.enumName)}`); -// await db.execute(sql`drop type if exists ${name(forceEnum.enumName)}`); -// await db.execute(sql`drop type if exists ${name(levelEnum.enumName)}`); -// await db.execute(sql`drop type if exists ${name(mechanicEnum.enumName)}`); -// await db.execute(sql`drop type if exists ${name(equipmentEnum.enumName)}`); -// await db.execute(sql`drop type if exists ${name(categoryEnum.enumName)}`); - -// await db.execute( -// sql`create type ${ -// name(muscleEnum.enumName) -// } as enum ('abdominals', 'hamstrings', 'adductors', 'quadriceps', 'biceps', 'shoulders', 'chest', 'middle_back', 'calves', 'glutes', 'lower_back', 'lats', 'triceps', 'traps', 'forearms', 'neck', 'abductors')`, -// ); -// await db.execute(sql`create type ${name(forceEnum.enumName)} as enum ('isometric', 'isotonic', 'isokinetic')`); -// await db.execute(sql`create type ${name(levelEnum.enumName)} as enum ('beginner', 'intermediate', 'advanced')`); -// await db.execute(sql`create type ${name(mechanicEnum.enumName)} as enum ('compound', 'isolation')`); -// await db.execute( -// sql`create type ${ -// name(equipmentEnum.enumName) -// } as enum ('barbell', 'dumbbell', 'bodyweight', 'machine', 'cable', 'kettlebell')`, -// ); -// await db.execute(sql`create type ${name(categoryEnum.enumName)} as enum ('upper_body', 'lower_body', 'full_body')`); -// await db.execute(sql` -// create table ${exercises} ( -// id serial primary key, -// name varchar not null, -// force force, -// level level, -// mechanic mechanic, -// equipment equipment, -// instructions text, -// category category, -// primary_muscles muscle[], -// secondary_muscles muscle[], -// created_at timestamp not null default now(), -// updated_at timestamp not null default now() -// ) -// `); - -// await db.insert(exercises).values({ -// name: 'Bench Press', -// force: 'isotonic', -// level: 'beginner', -// mechanic: 'compound', -// equipment: 'barbell', -// instructions: -// '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.', -// category: 'upper_body', -// primaryMuscles: ['chest', 'triceps'], -// secondaryMuscles: ['shoulders', 'traps'], -// }); - -// const result = await db.select().from(exercises); - -// expect(result, [ -// { -// id: 1, -// name: 'Bench Press', -// force: 'isotonic', -// level: 'beginner', -// mechanic: 'compound', -// equipment: 'barbell', -// instructions: -// '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.', -// category: 'upper_body', -// primaryMuscles: ['chest', 'triceps'], -// secondaryMuscles: ['shoulders', 'traps'], -// createdAt: result[0]!.createdAt, -// updatedAt: result[0]!.updatedAt, -// }, -// ]); - -// await db.execute(sql`drop table ${exercises}`); -// await db.execute(sql`drop type ${name(muscleEnum.enumName)}`); -// await db.execute(sql`drop type ${name(forceEnum.enumName)}`); -// await db.execute(sql`drop type ${name(levelEnum.enumName)}`); -// await db.execute(sql`drop type ${name(mechanicEnum.enumName)}`); -// await db.execute(sql`drop type ${name(equipmentEnum.enumName)}`); -// await db.execute(sql`drop type ${name(categoryEnum.enumName)}`); -// }); - -test('orderBy with aliased column', () => { - const query = db.select({ - test: sql`something`.as('test'), - }).from(users2Table).orderBy((fields) => fields.test).toSQL(); - - expect(query.sql, 'select something as "test" from "users2" order by "test"'); -}); - -test('select from sql', async () => { - const metricEntry = pgTable('metric_entry', { - id: pgUuid('id').notNull(), - createdAt: timestamp('created_at').notNull(), - }); - - await db.execute(sql`drop table if exists ${metricEntry}`); - await db.execute(sql`create table ${metricEntry} (id uuid not null, created_at timestamp not null)`); - - const metricId = uuid(); - - const intervals = db.$with('intervals').as( - db - .select({ - startTime: sql`(date'2023-03-01'+ x * '1 day'::interval)`.as('start_time'), - endTime: sql`(date'2023-03-01'+ (x+1) *'1 day'::interval)`.as('end_time'), - }) - .from(sql`generate_series(0, 29, 1) as t(x)`), - ); - - expect(() => - db - .with(intervals) - .select({ - startTime: intervals.startTime, - endTime: intervals.endTime, - count: sql`count(${metricEntry})`, - }) - .from(metricEntry) - .rightJoin( - intervals, - and( - eq(metricEntry.id, metricId), - gte(metricEntry.createdAt, intervals.startTime), - lt(metricEntry.createdAt, intervals.endTime), - ), - ) - .groupBy(intervals.startTime, intervals.endTime) - .orderBy(asc(intervals.startTime)) - ).not.toThrowError(); -}); - -test.skip('timestamp timezone', async () => { - const usersTableWithAndWithoutTimezone = pgTable('users_test_with_and_without_timezone', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), - updatedAt: timestamp('updated_at', { withTimezone: false }).notNull().defaultNow(), - }); - - await db.execute(sql`drop table if exists ${usersTableWithAndWithoutTimezone}`); - - await db.execute( - sql` - create table users_test_with_and_without_timezone ( - id serial not null primary key, - name text not null, - created_at timestamptz not null default now(), - updated_at timestamp not null default now() - ) - `, - ); - - const date = new Date(Date.parse('2020-01-01T00:00:00+04:00')); - - await db.insert(usersTableWithAndWithoutTimezone).values({ name: 'With default times' }); - await db.insert(usersTableWithAndWithoutTimezone).values({ - name: 'Without default times', - createdAt: date, - updatedAt: date, - }); - const users = await db.select().from(usersTableWithAndWithoutTimezone); - - // check that the timestamps are set correctly for default times - expect(Math.abs(users[0]!.updatedAt.getTime() - Date.now()) < 3000).toBeTruthy(); - expect(Math.abs(users[0]!.createdAt.getTime() - Date.now()) < 3000).toBeTruthy(); - - // check that the timestamps are set correctly for non default times - expect(Math.abs(users[1]!.updatedAt.getTime() - date.getTime()) < 3000).toBeTruthy(); - expect(Math.abs(users[1]!.createdAt.getTime() - date.getTime()) < 3000).toBeTruthy(); -}); - -test.skip('all date and time columns', async () => { - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - dateString: date('date_string', { mode: 'string' }).notNull(), - time: time('time', { precision: 3 }).notNull(), - datetime: timestamp('datetime').notNull(), - datetimeWTZ: timestamp('datetime_wtz', { withTimezone: true }).notNull(), - datetimeString: timestamp('datetime_string', { mode: 'string' }).notNull(), - datetimeFullPrecision: timestamp('datetime_full_precision', { precision: 6, mode: 'string' }).notNull(), - datetimeWTZString: timestamp('datetime_wtz_string', { withTimezone: true, mode: 'string' }).notNull(), - interval: interval('interval').notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - date_string date not null, - time time(3) not null, - datetime timestamp not null, - datetime_wtz timestamp with time zone not null, - datetime_string timestamp not null, - datetime_full_precision timestamp(6) not null, - datetime_wtz_string timestamp with time zone not null, - interval interval not null - ) - `); - - const someDatetime = new Date('2022-01-01T00:00:00.123Z'); - const fullPrecision = '2022-01-01T00:00:00.123456'; - const someTime = '23:23:12.432'; - - await db.insert(table).values({ - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01T00:00:00.123Z', - datetimeFullPrecision: fullPrecision.replace('T', ' ').replace('Z', ''), - datetimeWTZString: '2022-01-01T00:00:00.123Z', - interval: '1 day', - }); - - const result = await db.select().from(table); - - Expect< - Equal<{ - id: number; - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - }[], typeof result> - >; - - Expect< - Equal<{ - dateString: string; - time: string; - datetime: Date; - datetimeWTZ: Date; - datetimeString: string; - datetimeFullPrecision: string; - datetimeWTZString: string; - interval: string; - id?: number | undefined; - }, typeof table.$inferInsert> - >; - - expect(result).toEqual([ - { - id: 1, - dateString: '2022-01-01', - time: someTime, - datetime: someDatetime, - datetimeWTZ: someDatetime, - datetimeString: '2022-01-01 00:00:00.123', - datetimeFullPrecision: fullPrecision.replace('T', ' '), - datetimeWTZString: '2022-01-01 00:00:00.123+00', - interval: '1 day', - }, - ]); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.skip('all date and time columns with timezone', async () => { - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestamp: timestamp('timestamp_string', { mode: 'string', withTimezone: true, precision: 6 }).notNull(), - timestampAsDate: timestamp('timestamp_date', { withTimezone: true, precision: 3 }).notNull(), - timestampTimeZones: timestamp('timestamp_date_2', { withTimezone: true, precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) with time zone not null, - timestamp_date timestamp(3) with time zone not null, - timestamp_date_2 timestamp(3) with time zone not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456-0200'; - const timestampDate = new Date(); - const timestampDateWTZ = new Date('2022-01-01 00:00:00.123 +0500'); - - const timestampString2 = '2022-01-01 00:00:00.123456-0400'; - const timestampDate2 = new Date(); - const timestampDateWTZ2 = new Date('2022-01-01 00:00:00.123 +0200'); - - await db.insert(table).values([ - { timestamp: timestampString, timestampAsDate: timestampDate, timestampTimeZones: timestampDateWTZ }, - { timestamp: timestampString2, timestampAsDate: timestampDate2, timestampTimeZones: timestampDateWTZ2 }, - ]); - - const result = await db.select().from(table); - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - timestamp_date: string; - timestamp_date_2: string; - }>(sql`select * from ${table}`); - - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - expect(result).toEqual([ - { - id: 1, - timestamp: '2022-01-01T02:00:00.123456Z', - timestampAsDate: timestampDate, - timestampTimeZones: timestampDateWTZ, - }, - { - id: 2, - timestamp: '2022-01-01T04:00:00.123456Z', - timestampAsDate: timestampDate2, - timestampTimeZones: timestampDateWTZ2, - }, - ]); - - expect(result2.records).toEqual([ - { - id: 1, - timestamp_string: '2022-01-01 02:00:00.123456+00', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - { - id: 2, - timestamp_string: '2022-01-01 04:00:00.123456+00', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - timestamp_date_2: timestampDateWTZ2.toISOString().replace('T', ' ').replace('Z', '') + '+00', - }, - ]); - - expect( - result[0]?.timestampTimeZones.getTime(), - ).toEqual( - new Date((result2.records[0] as any).timestamp_date_2 as any).getTime(), - ); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test.skip('all date and time columns without timezone', async () => { - const table = pgTable('all_columns', { - id: serial('id').primaryKey(), - timestampString: timestamp('timestamp_string', { mode: 'string', precision: 6 }).notNull(), - timestampString2: timestamp('timestamp_string2', { precision: 3, mode: 'string' }).notNull(), - timestampDate: timestamp('timestamp_date', { precision: 3 }).notNull(), - }); - - await db.execute(sql`drop table if exists ${table}`); - - await db.execute(sql` - create table ${table} ( - id serial primary key, - timestamp_string timestamp(6) not null, - timestamp_string2 timestamp(3) not null, - timestamp_date timestamp(3) not null - ) - `); - - const timestampString = '2022-01-01 00:00:00.123456'; - const timestampString2 = '2022-01-02 00:00:00.123 -0300'; - const timestampDate = new Date('2022-01-01 00:00:00.123Z'); - - const timestampString_2 = '2022-01-01 00:00:00.123456'; - const timestampString2_2 = '2022-01-01 00:00:00.123 -0300'; - const timestampDate2 = new Date('2022-01-01 00:00:00.123 +0200'); - - await db.insert(table).values([ - { timestampString, timestampString2, timestampDate }, - { timestampString: timestampString_2, timestampString2: timestampString2_2, timestampDate: timestampDate2 }, - ]); - - const result = await db.select().from(table); - const result2 = await db.execute<{ - id: number; - timestamp_string: string; - timestamp_string2: string; - timestamp_date: string; - }>(sql`select * from ${table}`); - - // Whatever you put in, you get back when you're using the date mode - // But when using the string mode, postgres returns a string transformed into UTC - expect(result).toEqual([ - { - id: 1, - timestampString: timestampString, - timestampString2: '2022-01-02 00:00:00.123', - timestampDate: timestampDate, - }, - { - id: 2, - timestampString: timestampString_2, - timestampString2: '2022-01-01 00:00:00.123', - timestampDate: timestampDate2, - }, - ]); - - expect(result2.records).toEqual([ - { - id: 1, - timestamp_string: timestampString, - timestamp_string2: '2022-01-02 00:00:00.123', - timestamp_date: timestampDate.toISOString().replace('T', ' ').replace('Z', ''), - }, - { - id: 2, - timestamp_string: timestampString_2, - timestamp_string2: '2022-01-01 00:00:00.123', - timestamp_date: timestampDate2.toISOString().replace('T', ' ').replace('Z', ''), - }, - ]); - - expect((result2.records[0] as any).timestamp_string).toEqual('2022-01-01 00:00:00.123456'); - // need to add the 'Z', otherwise javascript assumes it's in local time - expect(new Date((result2.records[0] as any).timestamp_date + 'Z' as any).getTime()).toEqual( - timestampDate.getTime(), - ); - - await db.execute(sql`drop table if exists ${table}`); -}); - -test('transaction', async () => { - const users = pgTable('users_transactions', { - id: serial('id').primaryKey(), - balance: integer('balance').notNull(), - }); - const products = pgTable('products_transactions', { - id: serial('id').primaryKey(), - price: integer('price').notNull(), - stock: integer('stock').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - await db.execute(sql`drop table if exists ${products}`); - - await db.execute(sql`create table users_transactions (id serial not null primary key, balance integer not null)`); - await db.execute( - sql`create table products_transactions (id serial not null primary key, price integer not null, stock integer not null)`, - ); - - const user = await db.insert(users).values({ balance: 100 }).returning().then((rows) => rows[0]!); - const product = await db.insert(products).values({ price: 10, stock: 10 }).returning().then((rows) => rows[0]!); - - await expect( - db.transaction(async (tx) => { - await tx.update(users).set({ balance: user.balance - product.price }).where(eq(users.id, user.id)); - await tx.update(products).set({ stock: product.stock - 1 }).where(eq(products.id, product.id)); - }), - ).rejects.toThrowError('No transactions support in Xata Http driver'); - - // t.is(error!.message, 'No transactions support in Xata Http driver'); - - const result = await db.select().from(users); - - expect(result).toEqual([{ id: 1, balance: 100 }]); - - await db.execute(sql`drop table ${users}`); - await db.execute(sql`drop table ${products}`); -}); - -test('join subquery with join', async () => { - const internalStaff = pgTable('internal_staff', { - userId: integer('user_id').notNull(), - }); - - const customUser = pgTable('custom_user', { - id: integer('id').notNull(), - }); - - const ticket = pgTable('ticket', { - staffId: integer('staff_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${internalStaff}`); - await db.execute(sql`drop table if exists ${customUser}`); - await db.execute(sql`drop table if exists ${ticket}`); - - await db.execute(sql`create table internal_staff (user_id integer not null)`); - await db.execute(sql`create table custom_user (id integer not null)`); - await db.execute(sql`create table ticket (staff_id integer not null)`); - - await db.insert(internalStaff).values({ userId: 1 }); - await db.insert(customUser).values({ id: 1 }); - await db.insert(ticket).values({ staffId: 1 }); - - const subq = db - .select() - .from(internalStaff) - .leftJoin(customUser, eq(internalStaff.userId, customUser.id)) - .as('internal_staff'); - - const mainQuery = await db - .select() - .from(ticket) - .leftJoin(subq, eq(subq.internal_staff.userId, ticket.staffId)); - - expect(mainQuery).toEqual([{ - ticket: { staffId: 1 }, - internal_staff: { - internal_staff: { userId: 1 }, - custom_user: { id: 1 }, - }, - }]); - - await db.execute(sql`drop table ${internalStaff}`); - await db.execute(sql`drop table ${customUser}`); - await db.execute(sql`drop table ${ticket}`); -}); - -// Not supported in Xata -// test('subquery with view', async () => { -// - -// const users = pgTable('users_subquery_view', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// cityId: integer('city_id').notNull(), -// }); - -// const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - -// await db.execute(sql`drop table if exists ${users}`); -// await db.execute(sql`drop view if exists ${newYorkers}`); - -// await db.execute( -// sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, -// ); -// await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - -// await db.insert(users).values([ -// { name: 'John', cityId: 1 }, -// { name: 'Jane', cityId: 2 }, -// { name: 'Jack', cityId: 1 }, -// { name: 'Jill', cityId: 2 }, -// ]); - -// const sq = db.$with('sq').as(db.select().from(newYorkers)); -// const result = await db.with(sq).select().from(sq); - -// expect(result, [ -// { id: 1, name: 'John', cityId: 1 }, -// { id: 3, name: 'Jack', cityId: 1 }, -// ]); - -// await db.execute(sql`drop view ${newYorkers}`); -// await db.execute(sql`drop table ${users}`); -// }); - -// test('join view as subquery', async () => { -// - -// const users = pgTable('users_join_view', { -// id: serial('id').primaryKey(), -// name: text('name').notNull(), -// cityId: integer('city_id').notNull(), -// }); - -// const newYorkers = pgView('new_yorkers').as((qb) => qb.select().from(users).where(eq(users.cityId, 1))); - -// await db.execute(sql`drop table if exists ${users}`); -// await db.execute(sql`drop view if exists ${newYorkers}`); - -// await db.execute( -// sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, -// ); -// await db.execute(sql`create view ${newYorkers} as select * from ${users} where city_id = 1`); - -// await db.insert(users).values([ -// { name: 'John', cityId: 1 }, -// { name: 'Jane', cityId: 2 }, -// { name: 'Jack', cityId: 1 }, -// { name: 'Jill', cityId: 2 }, -// ]); - -// const sq = db.select().from(newYorkers).as('new_yorkers_sq'); - -// const result = await db.select().from(users).leftJoin(sq, eq(users.id, sq.id)); - -// expect(result, [ -// { -// users_join_view: { id: 1, name: 'John', cityId: 1 }, -// new_yorkers_sq: { id: 1, name: 'John', cityId: 1 }, -// }, -// { -// users_join_view: { id: 2, name: 'Jane', cityId: 2 }, -// new_yorkers_sq: null, -// }, -// { -// users_join_view: { id: 3, name: 'Jack', cityId: 1 }, -// new_yorkers_sq: { id: 3, name: 'Jack', cityId: 1 }, -// }, -// { -// users_join_view: { id: 4, name: 'Jill', cityId: 2 }, -// new_yorkers_sq: null, -// }, -// ]); - -// await db.execute(sql`drop view ${newYorkers}`); -// await db.execute(sql`drop table ${users}`); -// }); - -test('table selection with single table', async () => { - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name').notNull(), - cityId: integer('city_id').notNull(), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text not null, city_id integer not null)`, - ); - - await db.insert(users).values({ name: 'John', cityId: 1 }); - - const result = await db.select({ users }).from(users); - - expect(result).toEqual([{ users: { id: 1, name: 'John', cityId: 1 } }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('set null to jsonb field', async () => { - const users = pgTable('users', { - id: serial('id').primaryKey(), - jsonb: jsonb('jsonb'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, jsonb jsonb)`, - ); - - const result = await db.insert(users).values({ jsonb: null }).returning(); - - expect(result).toEqual([{ id: 1, jsonb: null }]); - - await db.execute(sql`drop table ${users}`); -}); - -test('insert undefined', async () => { - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - await expect(db.insert(users).values({ name: undefined })).resolves.not.toThrowError(); - - await db.execute(sql`drop table ${users}`); -}); - -test('update undefined', async () => { - const users = pgTable('users', { - id: serial('id').primaryKey(), - name: text('name'), - }); - - await db.execute(sql`drop table if exists ${users}`); - - await db.execute( - sql`create table ${users} (id serial not null primary key, name text)`, - ); - - expect(() => db.update(users).set({ name: undefined })).toThrowError(); - await expect(db.update(users).set({ id: 1, name: undefined })).resolves.not.toThrowError(); - - await db.execute(sql`drop table ${users}`); -}); - -test('array operators', async () => { - const posts = pgTable('posts', { - id: serial('id').primaryKey(), - tags: text('tags').array(), - }); - - await db.execute(sql`drop table if exists ${posts}`); - - await db.execute( - sql`create table ${posts} (id serial primary key, tags text[])`, - ); - - await db.insert(posts).values([{ - tags: ['ORM'], - }, { - tags: ['Typescript'], - }, { - tags: ['Typescript', 'ORM'], - }, { - tags: ['Typescript', 'Frontend', 'React'], - }, { - tags: ['Typescript', 'ORM', 'Database', 'Postgres'], - }, { - tags: ['Java', 'Spring', 'OOP'], - }]); - - const contains = await db.select({ id: posts.id }).from(posts) - .where(arrayContains(posts.tags, ['Typescript', 'ORM'])); - const contained = await db.select({ id: posts.id }).from(posts) - .where(arrayContained(posts.tags, ['Typescript', 'ORM'])); - const overlaps = await db.select({ id: posts.id }).from(posts) - .where(arrayOverlaps(posts.tags, ['Typescript', 'ORM'])); - const withSubQuery = await db.select({ id: posts.id }).from(posts) - .where(arrayContains( - posts.tags, - db.select({ tags: posts.tags }).from(posts).where(eq(posts.id, 1)), - )); - - expect(contains).toEqual([{ id: 3 }, { id: 5 }]); - expect(contained).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); - expect(overlaps).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]); - expect(withSubQuery).toEqual([{ id: 1 }, { id: 3 }, { id: 5 }]); -}); diff --git a/integration-tests/tests/__old/awsdatapi.alltypes.test.ts b/integration-tests/tests/awsdatapi.alltypes.test.ts similarity index 99% rename from integration-tests/tests/__old/awsdatapi.alltypes.test.ts rename to integration-tests/tests/awsdatapi.alltypes.test.ts index ef25b892a..16c49c05f 100644 --- a/integration-tests/tests/__old/awsdatapi.alltypes.test.ts +++ b/integration-tests/tests/awsdatapi.alltypes.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/no-empty-file */ // import 'dotenv/config'; // import { RDSDataClient } from '@aws-sdk/client-rds-data'; diff --git a/integration-tests/tests/imports.test.cjs b/integration-tests/tests/imports.test.cjs deleted file mode 100644 index a7b11ff80..000000000 --- a/integration-tests/tests/imports.test.cjs +++ /dev/null @@ -1,55 +0,0 @@ -require('drizzle-orm'); -require('drizzle-orm/aws-data-api/pg'); -require('drizzle-orm/aws-data-api/pg/migrator'); -require('drizzle-orm/better-sqlite3'); -require('drizzle-orm/better-sqlite3/migrator'); -require('drizzle-orm/bun-sqlite'); -require('drizzle-orm/bun-sqlite/migrator'); -require('drizzle-orm/d1'); -require('drizzle-orm/d1/migrator'); -require('drizzle-orm/knex'); -require('drizzle-orm/kysely'); -require('drizzle-orm/libsql'); -require('drizzle-orm/libsql/migrator'); -require('drizzle-orm/mysql-core'); -require('drizzle-orm/mysql2'); -require('drizzle-orm/mysql2/migrator'); -require('drizzle-orm/neon-serverless'); -require('drizzle-orm/neon-serverless/migrator'); -require('drizzle-orm/node-postgres'); -require('drizzle-orm/node-postgres/migrator'); -const { pgTable, serial } = require('drizzle-orm/pg-core'); -require('drizzle-orm/planetscale-serverless'); -require('drizzle-orm/planetscale-serverless/migrator'); -require('drizzle-orm/postgres-js'); -require('drizzle-orm/postgres-js/migrator'); -require('drizzle-orm/sql-js'); -require('drizzle-orm/sql-js/migrator'); -require('drizzle-orm/sqlite-core'); -require('drizzle-orm/sqlite-proxy'); -require('drizzle-orm/sqlite-proxy/migrator'); -require('drizzle-orm/pg-proxy'); -require('drizzle-orm/pg-proxy/migrator'); -require('drizzle-orm/mysql-proxy'); -require('drizzle-orm/mysql-proxy/migrator'); -require('drizzle-orm/migrator'); -const { createInsertSchema: createZodInsertSchema } = require('drizzle-zod'); -const { - createInsertSchema: createTypeboxInsertSchema, -} = require('drizzle-typebox'); -const { - createInsertSchema: createValibotInsertSchema, -} = require('drizzle-valibot'); -const { compatibilityVersion, npmVersion } = require('drizzle-orm/version'); -const { strict: assert } = require('node:assert'); - -assert.equal(typeof compatibilityVersion, 'number'); -assert.equal(typeof npmVersion, 'string'); - -const test = pgTable('test', { - id: serial('id').primaryKey(), -}); - -const zodInsertSchema = createZodInsertSchema(test); -const typeboxInsertSchema = createTypeboxInsertSchema(test); -const valibotInsertSchema = createValibotInsertSchema(test); diff --git a/integration-tests/tests/imports.test.mjs b/integration-tests/tests/imports.test.mjs deleted file mode 100644 index 07e06b331..000000000 --- a/integration-tests/tests/imports.test.mjs +++ /dev/null @@ -1,47 +0,0 @@ -import 'drizzle-orm'; -import 'drizzle-orm/aws-data-api/pg'; -import 'drizzle-orm/aws-data-api/pg/migrator'; -import 'drizzle-orm/better-sqlite3'; -import 'drizzle-orm/better-sqlite3/migrator'; -import 'drizzle-orm/bun-sqlite'; -import 'drizzle-orm/bun-sqlite/migrator'; -import 'drizzle-orm/d1'; -import 'drizzle-orm/d1/migrator'; -import 'drizzle-orm/knex'; -import 'drizzle-orm/kysely'; -import 'drizzle-orm/libsql'; -import 'drizzle-orm/libsql/migrator'; -import 'drizzle-orm/mysql-core'; -import 'drizzle-orm/mysql2'; -import 'drizzle-orm/mysql2/migrator'; -import 'drizzle-orm/neon-serverless'; -import 'drizzle-orm/neon-serverless/migrator'; -import 'drizzle-orm/node-postgres'; -import 'drizzle-orm/node-postgres/migrator'; -import { pgTable, serial } from 'drizzle-orm/pg-core'; -import 'drizzle-orm/planetscale-serverless'; -import 'drizzle-orm/planetscale-serverless/migrator'; -import 'drizzle-orm/postgres-js'; -import 'drizzle-orm/postgres-js/migrator'; -import 'drizzle-orm/sql-js'; -import 'drizzle-orm/sql-js/migrator'; -import 'drizzle-orm/sqlite-core'; -import 'drizzle-orm/sqlite-proxy'; -import 'drizzle-orm/sqlite-proxy/migrator'; -import 'drizzle-orm/pg-proxy'; -import 'drizzle-orm/pg-proxy/migrator'; -import 'drizzle-orm/mysql-proxy'; -import 'drizzle-orm/mysql-proxy/migrator'; -import 'drizzle-orm/migrator'; -import { compatibilityVersion, npmVersion } from 'drizzle-orm/version'; -import { createInsertSchema } from 'drizzle-zod'; -import { strict as assert } from 'node:assert'; - -assert.equal(typeof compatibilityVersion, 'number'); -assert.equal(typeof npmVersion, 'string'); - -const test = pgTable('test', { - id: serial('id').primaryKey(), -}); - -const insertSchema = createInsertSchema(test); diff --git a/integration-tests/tests/mysql/mysql-common.ts b/integration-tests/tests/mysql/mysql-common.ts index cec75d00d..e67e706fb 100644 --- a/integration-tests/tests/mysql/mysql-common.ts +++ b/integration-tests/tests/mysql/mysql-common.ts @@ -66,8 +66,8 @@ import { migrate } from 'drizzle-orm/mysql2/migrator'; import getPort from 'get-port'; import { v4 as uuid } from 'uuid'; import { afterAll, beforeEach, describe, expect, test } from 'vitest'; -import type { Equal } from '~/__old/utils.ts'; -import { Expect, toLocalDate } from '~/__old/utils.ts'; +import { Expect, toLocalDate } from '~/utils.ts'; +import type { Equal } from '~/utils.ts'; type TestMySQLDB = MySqlDatabase; diff --git a/integration-tests/tests/mysql/mysql-custom.test.ts b/integration-tests/tests/mysql/mysql-custom.test.ts index 16961a7de..c8a761665 100644 --- a/integration-tests/tests/mysql/mysql-custom.test.ts +++ b/integration-tests/tests/mysql/mysql-custom.test.ts @@ -21,7 +21,7 @@ import { migrate } from 'drizzle-orm/mysql2/migrator'; import * as mysql from 'mysql2/promise'; import { v4 as uuid } from 'uuid'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { toLocalDate } from '~/__old/utils'; +import { toLocalDate } from '~/utils'; import { createDockerDB } from './mysql-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/mysql/mysql-prefixed.test.ts b/integration-tests/tests/mysql/mysql-prefixed.test.ts index 5903bc9cd..2f313ec00 100644 --- a/integration-tests/tests/mysql/mysql-prefixed.test.ts +++ b/integration-tests/tests/mysql/mysql-prefixed.test.ts @@ -25,7 +25,7 @@ import { drizzle } from 'drizzle-orm/mysql2'; import { migrate } from 'drizzle-orm/mysql2/migrator'; import * as mysql from 'mysql2/promise'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { Expect, toLocalDate } from '~/__old/utils'; +import { Expect, toLocalDate } from '~/utils'; import { createDockerDB } from './mysql-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/pg/neon-http.test.ts b/integration-tests/tests/pg/neon-http.test.ts index e3fe0823b..69b891812 100644 --- a/integration-tests/tests/pg/neon-http.test.ts +++ b/integration-tests/tests/pg/neon-http.test.ts @@ -6,8 +6,8 @@ import { migrate } from 'drizzle-orm/neon-http/migrator'; import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core'; import { Client } from 'pg'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; +import { randomString } from '~/utils'; import { tests, usersMigratorTable, usersTable } from './pg-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/pg/node-postgres.test.ts b/integration-tests/tests/pg/node-postgres.test.ts index 63d73e687..1c898e6a6 100644 --- a/integration-tests/tests/pg/node-postgres.test.ts +++ b/integration-tests/tests/pg/node-postgres.test.ts @@ -6,8 +6,8 @@ import { migrate } from 'drizzle-orm/node-postgres/migrator'; import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core'; import { Client } from 'pg'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; +import { randomString } from '~/utils'; import { createDockerDB, tests, usersMigratorTable, usersTable } from './pg-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/pg/pg-custom.test.ts b/integration-tests/tests/pg/pg-custom.test.ts index 933a34ad7..0d21261a6 100644 --- a/integration-tests/tests/pg/pg-custom.test.ts +++ b/integration-tests/tests/pg/pg-custom.test.ts @@ -6,7 +6,7 @@ import { migrate } from 'drizzle-orm/node-postgres/migrator'; import { alias, customType, pgTable, pgTableCreator, serial, text } from 'drizzle-orm/pg-core'; import { Client } from 'pg'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; +import { randomString } from '~/utils'; import { createDockerDB } from './pg-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/pg/postgres-js.test.ts b/integration-tests/tests/pg/postgres-js.test.ts index 7c29396b8..7becec7eb 100644 --- a/integration-tests/tests/pg/postgres-js.test.ts +++ b/integration-tests/tests/pg/postgres-js.test.ts @@ -7,8 +7,8 @@ import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; import { Name, sql } from 'drizzle-orm'; import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core'; import { migrate } from 'drizzle-orm/postgres-js/migrator'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; +import { randomString } from '~/utils'; import { createDockerDB, tests, usersMigratorTable, usersTable } from './pg-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/pg/vercel-pg.test.ts b/integration-tests/tests/pg/vercel-pg.test.ts index ca68e598f..5f3062eff 100644 --- a/integration-tests/tests/pg/vercel-pg.test.ts +++ b/integration-tests/tests/pg/vercel-pg.test.ts @@ -4,8 +4,8 @@ import { pgTable, serial, timestamp } from 'drizzle-orm/pg-core'; import { drizzle, type VercelPgDatabase } from 'drizzle-orm/vercel-postgres'; import { migrate } from 'drizzle-orm/vercel-postgres/migrator'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; +import { randomString } from '~/utils'; import { createDockerDB, tests, usersMigratorTable, usersTable } from './pg-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/pg/xata-http.test.ts b/integration-tests/tests/pg/xata-http.test.ts index 8f49dd6da..80c97e765 100644 --- a/integration-tests/tests/pg/xata-http.test.ts +++ b/integration-tests/tests/pg/xata-http.test.ts @@ -5,8 +5,8 @@ import { drizzle } from 'drizzle-orm/xata-http'; import type { XataHttpClient, XataHttpDatabase } from 'drizzle-orm/xata-http'; import { migrate } from 'drizzle-orm/xata-http/migrator'; import { beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; +import { randomString } from '~/utils'; import { getXataClient } from '../xata/xata.ts'; import { tests, usersMigratorTable, usersTable } from './pg-common'; diff --git a/integration-tests/tests/sqlite/better-sqlite.test.ts b/integration-tests/tests/sqlite/better-sqlite.test.ts index de37e0b0c..53feee15f 100644 --- a/integration-tests/tests/sqlite/better-sqlite.test.ts +++ b/integration-tests/tests/sqlite/better-sqlite.test.ts @@ -3,7 +3,6 @@ import { sql } from 'drizzle-orm'; import { type BetterSQLite3Database, drizzle } from 'drizzle-orm/better-sqlite3'; import { migrate } from 'drizzle-orm/better-sqlite3/migrator'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; import { anotherUsersMigratorTable, tests, usersMigratorTable } from './sqlite-common'; diff --git a/integration-tests/tests/sqlite/d1.test.ts b/integration-tests/tests/sqlite/d1.test.ts index 4f7465954..20e9e9d14 100644 --- a/integration-tests/tests/sqlite/d1.test.ts +++ b/integration-tests/tests/sqlite/d1.test.ts @@ -1,13 +1,13 @@ import { D1Database, D1DatabaseAPI } from '@miniflare/d1'; import { createSQLiteDB } from '@miniflare/shared'; -import { eq, sql } from 'drizzle-orm'; +import { sql } from 'drizzle-orm'; import type { DrizzleD1Database } from 'drizzle-orm/d1'; import { drizzle } from 'drizzle-orm/d1'; import { migrate } from 'drizzle-orm/d1/migrator'; import { beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; -import { anotherUsersMigratorTable, citiesTable, tests, users2Table, usersMigratorTable } from './sqlite-common'; +import { randomString } from '~/utils'; +import { anotherUsersMigratorTable, tests, usersMigratorTable } from './sqlite-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/sqlite/libsql-batch.test.ts b/integration-tests/tests/sqlite/libsql-batch.test.ts index edde765f9..693845f30 100644 --- a/integration-tests/tests/sqlite/libsql-batch.test.ts +++ b/integration-tests/tests/sqlite/libsql-batch.test.ts @@ -1,8 +1,10 @@ -import { type Client, createClient, ResultSet } from '@libsql/client'; +import { createClient } from '@libsql/client'; +import type { Client, ResultSet } from '@libsql/client'; import retry from 'async-retry'; import { eq, relations, sql } from 'drizzle-orm'; import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'; -import { AnySQLiteColumn, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core'; +import { integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; const ENABLE_LOGGING = false; @@ -158,7 +160,7 @@ beforeAll(async () => { }); afterAll(async () => { - client?.close(); + // client?.close(); }); beforeEach(async () => { diff --git a/integration-tests/tests/sqlite/libsql.test.ts b/integration-tests/tests/sqlite/libsql.test.ts index 7020f6f9e..71d3b289e 100644 --- a/integration-tests/tests/sqlite/libsql.test.ts +++ b/integration-tests/tests/sqlite/libsql.test.ts @@ -1,11 +1,11 @@ import { type Client, createClient } from '@libsql/client'; import retry from 'async-retry'; -import { eq, sql } from 'drizzle-orm'; +import { sql } from 'drizzle-orm'; import { drizzle, type LibSQLDatabase } from 'drizzle-orm/libsql'; import { migrate } from 'drizzle-orm/libsql/migrator'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; -import { anotherUsersMigratorTable, citiesTable, tests, users2Table, usersMigratorTable } from './sqlite-common'; +import { randomString } from '~/utils'; +import { anotherUsersMigratorTable, tests, usersMigratorTable } from './sqlite-common'; const ENABLE_LOGGING = false; diff --git a/integration-tests/tests/sqlite/sql-js.test.ts b/integration-tests/tests/sqlite/sql-js.test.ts index 1fed5445c..ec3d7b583 100644 --- a/integration-tests/tests/sqlite/sql-js.test.ts +++ b/integration-tests/tests/sqlite/sql-js.test.ts @@ -5,7 +5,6 @@ import { migrate } from 'drizzle-orm/sql-js/migrator'; import type { Database } from 'sql.js'; import initSqlJs from 'sql.js'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; import { anotherUsersMigratorTable, tests, usersMigratorTable } from './sqlite-common'; diff --git a/integration-tests/tests/sqlite/sqlite-common.ts b/integration-tests/tests/sqlite/sqlite-common.ts index 79be644a0..a816d8cca 100644 --- a/integration-tests/tests/sqlite/sqlite-common.ts +++ b/integration-tests/tests/sqlite/sqlite-common.ts @@ -43,7 +43,8 @@ import { uniqueKeyName, } from 'drizzle-orm/sqlite-core'; import { beforeEach, describe, expect, test } from 'vitest'; -import { Equal, Expect } from '~/__old/utils'; +import type { Equal } from '~/utils'; +import { Expect } from '~/utils'; declare module 'vitest' { interface TestContext { @@ -276,7 +277,7 @@ export function tests() { ]); } - test('table config: foreign keys name', async (ctx) => { + test('table config: foreign keys name', async () => { const table = sqliteTable('cities', { id: int('id').primaryKey(), name: text('name').notNull(), @@ -293,7 +294,7 @@ export function tests() { expect(tableConfig.foreignKeys[1]!.getName()).toBe('custom_fk_deprecated'); }); - test('table config: primary keys name', async (ctx) => { + test('table config: primary keys name', async () => { const table = sqliteTable('cities', { id: int('id').primaryKey(), name: text('name').notNull(), diff --git a/integration-tests/tests/sqlite/sqlite-proxy-batch.test.ts b/integration-tests/tests/sqlite/sqlite-proxy-batch.test.ts index 331a8d9b3..1137faaf4 100644 --- a/integration-tests/tests/sqlite/sqlite-proxy-batch.test.ts +++ b/integration-tests/tests/sqlite/sqlite-proxy-batch.test.ts @@ -1,7 +1,9 @@ +/* eslint-disable drizzle-internal/require-entity-kind */ import type BetterSqlite3 from 'better-sqlite3'; import Database from 'better-sqlite3'; import { eq, relations, sql } from 'drizzle-orm'; -import { AnySQLiteColumn, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core'; +import { integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import type { SqliteRemoteDatabase, SqliteRemoteResult } from 'drizzle-orm/sqlite-proxy'; import { drizzle as proxyDrizzle } from 'drizzle-orm/sqlite-proxy'; import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest'; diff --git a/integration-tests/tests/sqlite/sqlite-proxy.test.ts b/integration-tests/tests/sqlite/sqlite-proxy.test.ts index e4cc98d4b..9066b2bec 100644 --- a/integration-tests/tests/sqlite/sqlite-proxy.test.ts +++ b/integration-tests/tests/sqlite/sqlite-proxy.test.ts @@ -1,20 +1,12 @@ +/* eslint-disable drizzle-internal/require-entity-kind */ import type BetterSqlite3 from 'better-sqlite3'; import Database from 'better-sqlite3'; -import { eq, Name, sql } from 'drizzle-orm'; +import { Name, sql } from 'drizzle-orm'; import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy'; import { drizzle as proxyDrizzle } from 'drizzle-orm/sqlite-proxy'; -import { migrate } from 'drizzle-orm/sqlite-proxy/migrator'; import { afterAll, beforeAll, beforeEach, expect, test } from 'vitest'; -import { randomString } from '~/__old/utils'; import { skipTests } from '~/common'; -import { - anotherUsersMigratorTable, - citiesTable, - tests, - users2Table, - usersMigratorTable, - usersTable, -} from './sqlite-common'; +import { tests, usersTable } from './sqlite-common'; class ServerSimulator { constructor(private db: BetterSqlite3.Database) {} @@ -109,13 +101,13 @@ beforeEach(async () => { await db.run(sql`drop table if exists ${usersTable}`); await db.run(sql` - create table ${usersTable} ( - id integer primary key, - name text not null, - verified integer not null default 0, - json blob, - created_at integer not null default (strftime('%s', 'now')) - ) + create table ${usersTable} ( + id integer primary key, + name text not null, + verified integer not null default 0, + json blob, + created_at integer not null default (strftime('%s', 'now')) + ) `); }); diff --git a/integration-tests/tests/version.test.ts b/integration-tests/tests/version.test.ts index 0a2a605b8..f12ae9e71 100644 --- a/integration-tests/tests/version.test.ts +++ b/integration-tests/tests/version.test.ts @@ -9,5 +9,5 @@ test('shape', () => { compatibilityVersion: z.number(), npmVersion: z.string(), }); - expect(shape.parse(version)).not.toThrowError(); + expect(() => shape.parse(version)).not.toThrowError(); }); diff --git a/integration-tests/vitest.config.ts b/integration-tests/vitest.config.ts index 1ca48e472..42ee138ad 100644 --- a/integration-tests/vitest.config.ts +++ b/integration-tests/vitest.config.ts @@ -5,30 +5,29 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { include: [ - // 'tests/pg/node-postgres.test.ts', - // 'tests/pg/postgres-js.test.ts', - // 'tests/pg/pglite.test.ts', - // 'tests/pg/pg-custom.test.ts', - // 'tests/pg/pg-proxy.test.ts', - // 'tests/pg/neon-http.test.ts', - 'tests/mysql/mysql.test.ts', - 'tests/mysql/mysql-proxy.test.ts', - 'tests/mysql/mysql-prefixed.test.ts', - 'tests/mysql/mysql-planetscale.test.ts', - 'tests/mysql/mysql-custom.test.ts', + 'tests/extensions/postgis/**/*', + 'tests/relational/**/*.test.ts', + 'tests/pg/**/*.test.ts', + 'tests/mysql/**/*.test.ts', + 'tests/sqlite/**/*.test.ts', + 'tests/replicas/**/*', + 'tests/imports/**/*', + 'tests/extensions/vectors/**/*', + 'tests/version.test.ts', ], exclude: [ ...(process.env.SKIP_EXTERNAL_DB_TESTS ? [ 'tests/relational/mysql.planetscale.test.ts', 'tests/neon-http-batch.test.ts', - 'tests/xata-http.test.ts', + 'tests/pg/xata-http.test.ts', + 'tests/mysql/tidb-serverless.test.ts', ] : []), - 'tests/awsdatapi.test.ts', + 'tests/pg/awsdatapi.test.ts', + 'tests/awsdatapi.alltypes.test.ts', 'tests/pg/vercel-pg.test.ts', 'tests/relational/vercel.test.ts', - 'tests/__old/*', ], typecheck: { tsconfig: 'tsconfig.json',