chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:10:05 +08:00
commit d37d8d293b
1388 changed files with 484182 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
import 'dotenv/config';
import Database from 'better-sqlite3';
import { sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import util from 'node:util';
import * as schema from './tables.ts';
async function main() {
const bdb = new Database(process.env['SQLITE_DB_PATH']!);
const db = drizzle(bdb, { schema, logger: true });
const result = db.query.users.findMany({
columns: {
id: true,
name: true,
},
with: {
posts: {
columns: {
authorId: true,
},
with: {
comments: true,
},
extras: {
lower: sql<string>`lower(${schema.posts.title})`.as('lower_name'),
},
},
},
});
console.log(util.inspect(result, false, null, true));
bdb.close();
}
main();
@@ -0,0 +1,274 @@
import 'dotenv/config';
import Docker from 'dockerode';
import { sql } from 'drizzle-orm';
import { drizzle, type MySql2Database } from 'drizzle-orm/mysql2';
import getPort from 'get-port';
import * as mysql from 'mysql2/promise';
import { v4 as uuid } from 'uuid';
import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest';
import * as schema from './mysql.duplicates.ts';
const ENABLE_LOGGING = false;
/*
Test cases:
- querying nested relation without PK with additional fields
*/
let mysqlContainer: Docker.Container;
let db: MySql2Database<typeof schema>;
let client: mysql.Connection;
async function createDockerDB(): Promise<string> {
const 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)))
);
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 mysqlContainer.start();
return `mysql://root:mysql@127.0.0.1:${port}/drizzle`;
}
beforeAll(async () => {
const connectionString = process.env['MYSQL_CONNECTION_STRING'] ?? await createDockerDB();
const sleep = 1000;
let timeLeft = 30000;
let connected = false;
let lastError: unknown | undefined;
do {
try {
client = await mysql.createConnection(connectionString);
await 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 client?.end().catch(console.error);
await mysqlContainer?.stop().catch(console.error);
throw lastError;
}
db = drizzle(client, { schema, logger: ENABLE_LOGGING, mode: 'default' });
});
afterAll(async () => {
await client?.end().catch(console.error);
await mysqlContainer?.stop().catch(console.error);
});
beforeEach(async () => {
await db.execute(sql`drop table if exists \`members\``);
await db.execute(sql`drop table if exists \`artist_to_member\``);
await db.execute(sql`drop table if exists \`artists\``);
await db.execute(sql`drop table if exists \`albums\``);
await db.execute(
sql`
CREATE TABLE \`members\` (
\`id\` serial AUTO_INCREMENT PRIMARY KEY NOT NULL,
\`created_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`updated_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`name_en\` varchar(50) NOT NULL,
\`name_kr\` varchar(50) NOT NULL,
\`stage_name_en\` varchar(50) NOT NULL,
\`stage_name_kr\` varchar(50) NOT NULL,
\`image\` varchar(255) NOT NULL,
\`instagram\` varchar(255) NOT NULL);
`,
);
await db.execute(
sql`
CREATE TABLE \`artist_to_member\` (
\`id\` serial AUTO_INCREMENT PRIMARY KEY NOT NULL,
\`member_id\` int NOT NULL,
\`artist_id\` int NOT NULL);
`,
);
await db.execute(
sql`
CREATE TABLE \`artists\` (
\`id\` serial AUTO_INCREMENT PRIMARY KEY NOT NULL,
\`created_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`updated_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`name_en\` varchar(50) NOT NULL,
\`name_kr\` varchar(50) NOT NULL,
\`debut\` date NOT NULL,
\`company_id\` int NOT NULL,
\`is_group\` boolean NOT NULL DEFAULT true,
\`image\` varchar(255) NOT NULL,
\`twitter\` varchar(255) NOT NULL,
\`instagram\` varchar(255) NOT NULL,
\`youtube\` varchar(255) NOT NULL,
\`website\` varchar(255) NOT NULL,
\`spotify_id\` varchar(32));
`,
);
await db.execute(
sql`
CREATE TABLE \`albums\` (
\`id\` serial AUTO_INCREMENT PRIMARY KEY NOT NULL,
\`created_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`updated_at\` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
\`artist_id\` int NOT NULL,
\`name\` varchar(50) NOT NULL,
\`region\` enum('en','kr','jp','other') NOT NULL,
\`release_date\` date NOT NULL,
\`image\` varchar(255) NOT NULL,
\`spotify_id\` varchar(32));
`,
);
});
test('Simple case from GH', async () => {
await db.insert(schema.artists).values([
{
id: 1,
nameEn: 'Dan',
nameKr: '',
debut: new Date(),
companyId: 1,
image: '',
twitter: '',
instagram: '',
youtube: '',
website: '',
},
{
id: 2,
nameEn: 'Andrew',
nameKr: '',
debut: new Date(),
companyId: 1,
image: '',
twitter: '',
instagram: '',
youtube: '',
website: '',
},
{
id: 3,
nameEn: 'Alex',
nameKr: '',
debut: new Date(),
companyId: 1,
image: '',
twitter: '',
instagram: '',
youtube: '',
website: '',
},
]);
await db.insert(schema.albums).values([
{ id: 1, artistId: 1, name: 'Album1', region: 'en', releaseDate: new Date(), image: '' },
{ id: 2, artistId: 2, name: 'Album2', region: 'en', releaseDate: new Date(), image: '' },
{ id: 3, artistId: 3, name: 'Album3', region: 'en', releaseDate: new Date(), image: '' },
]);
await db.insert(schema.members).values([
{ id: 1, nameEn: 'MemberA', nameKr: '', stageNameEn: '', stageNameKr: '', image: '', instagram: '' },
{ id: 2, nameEn: 'MemberB', nameKr: '', stageNameEn: '', stageNameKr: '', image: '', instagram: '' },
{ id: 3, nameEn: 'MemberC', nameKr: '', stageNameEn: '', stageNameKr: '', image: '', instagram: '' },
]);
await db.insert(schema.artistsToMembers).values([
{ memberId: 1, artistId: 1 },
{ memberId: 2, artistId: 1 },
{ memberId: 2, artistId: 2 },
{ memberId: 3, artistId: 3 },
]);
const response = await db.query.artists.findFirst({
where: (artists, { eq }) => eq(artists.id, 1),
with: {
albums: true,
members: {
columns: {},
with: {
member: true,
},
},
},
});
expectTypeOf(response).toEqualTypeOf<
{
id: number;
createdAt: Date;
updatedAt: Date;
nameEn: string;
nameKr: string;
debut: Date;
companyId: number;
isGroup: boolean;
image: string;
twitter: string;
instagram: string;
youtube: string;
website: string;
spotifyId: string | null;
members: {
member: {
id: number;
createdAt: Date;
updatedAt: Date;
nameEn: string;
nameKr: string;
image: string;
instagram: string;
stageNameEn: string;
stageNameKr: string;
};
}[];
albums: {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
image: string;
spotifyId: string | null;
artistId: number;
region: 'en' | 'kr' | 'jp' | 'other';
releaseDate: Date;
}[];
} | undefined
>();
expect(response?.members.length).eq(2);
expect(response?.albums.length).eq(1);
expect(response?.albums[0]).toEqual({
id: 1,
createdAt: response?.albums[0]?.createdAt,
updatedAt: response?.albums[0]?.updatedAt,
artistId: 1,
name: 'Album1',
region: 'en',
releaseDate: response?.albums[0]?.releaseDate,
image: '',
spotifyId: null,
});
});
@@ -0,0 +1,111 @@
import { relations, sql } from 'drizzle-orm';
import { boolean, date, index, int, mysqlEnum, mysqlTable, serial, timestamp, varchar } from 'drizzle-orm/mysql-core';
export const artists = mysqlTable(
'artists',
{
id: serial('id').primaryKey(),
createdAt: timestamp('created_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: timestamp('updated_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
nameEn: varchar('name_en', { length: 50 }).notNull(),
nameKr: varchar('name_kr', { length: 50 }).notNull(),
debut: date('debut').notNull(),
companyId: int('company_id').notNull(),
isGroup: boolean('is_group').notNull().default(true),
image: varchar('image', { length: 255 }).notNull(),
twitter: varchar('twitter', { length: 255 }).notNull(),
instagram: varchar('instagram', { length: 255 }).notNull(),
youtube: varchar('youtube', { length: 255 }).notNull(),
website: varchar('website', { length: 255 }).notNull(),
spotifyId: varchar('spotify_id', { length: 32 }),
},
(table) => ({
nameEnIndex: index('artists__name_en__idx').on(table.nameEn),
}),
);
export const members = mysqlTable('members', {
id: serial('id').primaryKey(),
createdAt: timestamp('created_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: timestamp('updated_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
nameEn: varchar('name_en', { length: 50 }).notNull(),
nameKr: varchar('name_kr', { length: 50 }).notNull(),
stageNameEn: varchar('stage_name_en', { length: 50 }).notNull(),
stageNameKr: varchar('stage_name_kr', { length: 50 }).notNull(),
image: varchar('image', { length: 255 }).notNull(),
instagram: varchar('instagram', { length: 255 }).notNull(),
});
export const artistsToMembers = mysqlTable(
'artist_to_member',
{
id: serial('id').primaryKey(),
memberId: int('member_id').notNull(),
artistId: int('artist_id').notNull(),
},
(table) => ({
memberArtistIndex: index('artist_to_member__artist_id__member_id__idx').on(
table.memberId,
table.artistId,
),
}),
);
export const albums = mysqlTable(
'albums',
{
id: serial('id').primaryKey(),
createdAt: timestamp('created_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: timestamp('updated_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
artistId: int('artist_id').notNull(),
name: varchar('name', { length: 50 }).notNull(),
region: mysqlEnum('region', ['en', 'kr', 'jp', 'other']).notNull(),
releaseDate: date('release_date').notNull(),
image: varchar('image', { length: 255 }).notNull(),
spotifyId: varchar('spotify_id', { length: 32 }),
},
(table) => ({
artistIndex: index('albums__artist_id__idx').on(table.artistId),
nameIndex: index('albums__name__idx').on(table.name),
}),
);
// relations
export const artistRelations = relations(artists, ({ many }) => ({
albums: many(albums),
members: many(artistsToMembers),
}));
export const albumRelations = relations(albums, ({ one }) => ({
artist: one(artists, {
fields: [albums.artistId],
references: [artists.id],
}),
}));
export const memberRelations = relations(members, ({ many }) => ({
artists: many(artistsToMembers),
}));
export const artistsToMembersRelations = relations(artistsToMembers, ({ one }) => ({
artist: one(artists, {
fields: [artistsToMembers.artistId],
references: [artists.id],
}),
member: one(members, {
fields: [artistsToMembers.memberId],
references: [members.id],
}),
}));
@@ -0,0 +1,210 @@
import 'dotenv/config';
import Docker from 'dockerode';
import { sql } from 'drizzle-orm';
import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres';
import getPort from 'get-port';
import pg from 'pg';
import { v4 as uuid } from 'uuid';
import { afterAll, beforeAll, beforeEach, expect, expectTypeOf, test } from 'vitest';
import * as schema from './pg.duplicates.ts';
const { Client } = pg;
const ENABLE_LOGGING = false;
/*
Test cases:
- querying nested relation without PK with additional fields
*/
let pgContainer: Docker.Container;
let db: NodePgDatabase<typeof schema>;
let client: pg.Client;
async function createDockerDB(): Promise<string> {
const 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))
);
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`;
}
beforeAll(async () => {
const connectionString = process.env['PG_CONNECTION_STRING'] ?? (await createDockerDB());
const sleep = 250;
let timeLeft = 5000;
let connected = false;
let lastError: unknown | undefined;
do {
try {
client = new Client(connectionString);
await 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 client?.end().catch(console.error);
await pgContainer?.stop().catch(console.error);
throw lastError;
}
db = drizzle(client, { schema, logger: ENABLE_LOGGING });
});
afterAll(async () => {
await client?.end().catch(console.error);
await pgContainer?.stop().catch(console.error);
});
beforeEach(async () => {
await db.execute(sql`drop table if exists "members"`);
await db.execute(sql`drop table if exists "artist_to_member"`);
await db.execute(sql`drop table if exists "artists"`);
await db.execute(sql`drop table if exists "albums"`);
await db.execute(
sql`
CREATE TABLE "members" (
"id" serial PRIMARY KEY NOT NULL,
"created_at" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`,
);
await db.execute(
sql`
CREATE TABLE "artist_to_member" (
"id" serial PRIMARY KEY NOT NULL,
"member_id" int NOT NULL,
"artist_id" int NOT NULL);
`,
);
await db.execute(
sql`
CREATE TABLE "artists" (
"id" serial PRIMARY KEY NOT NULL,
"created_at" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"company_id" int NOT NULL);
`,
);
await db.execute(
sql`
CREATE TABLE "albums" (
"id" serial PRIMARY KEY NOT NULL,
"created_at" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"artist_id" int NOT NULL);
`,
);
});
test('Simple case from GH', async () => {
await db.insert(schema.artists).values([
{
id: 1,
companyId: 1,
},
{
id: 2,
companyId: 1,
},
{
id: 3,
companyId: 1,
},
]);
await db.insert(schema.albums).values([
{ id: 1, artistId: 1 },
{ id: 2, artistId: 2 },
{ id: 3, artistId: 3 },
]);
await db.insert(schema.members).values([
{ id: 1 },
{ id: 2 },
{ id: 3 },
]);
await db.insert(schema.artistsToMembers).values([
{ memberId: 1, artistId: 1 },
{ memberId: 2, artistId: 1 },
{ memberId: 2, artistId: 2 },
{ memberId: 3, artistId: 3 },
]);
const response = await db.query.artists.findFirst({
where: (artists, { eq }) => eq(artists.id, 1),
with: {
albums: true,
members: {
columns: {},
with: {
member: true,
},
},
},
});
expectTypeOf(response).toEqualTypeOf<
{
id: number;
createdAt: Date;
updatedAt: Date;
companyId: number;
albums: {
id: number;
createdAt: Date;
updatedAt: Date;
artistId: number;
}[];
members: {
member: {
id: number;
createdAt: Date;
updatedAt: Date;
};
}[];
} | undefined
>();
expect(response?.members.length).eq(2);
expect(response?.albums.length).eq(1);
expect(response?.albums[0]).toEqual({
id: 1,
createdAt: response?.albums[0]?.createdAt,
updatedAt: response?.albums[0]?.updatedAt,
artistId: 1,
});
});
@@ -0,0 +1,86 @@
import { relations, sql } from 'drizzle-orm';
import { index, integer, pgTable, serial, timestamp } from 'drizzle-orm/pg-core';
export const artists = pgTable(
'artists',
{
id: serial('id').primaryKey(),
createdAt: timestamp('created_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: timestamp('updated_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
companyId: integer('company_id').notNull(),
},
);
export const members = pgTable('members', {
id: serial('id').primaryKey(),
createdAt: timestamp('created_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: timestamp('updated_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
});
export const artistsToMembers = pgTable(
'artist_to_member',
{
id: serial('id').primaryKey(),
memberId: integer('member_id').notNull(),
artistId: integer('artist_id').notNull(),
},
(table) => ({
memberArtistIndex: index('artist_to_member__artist_id__member_id__idx').on(
table.memberId,
table.artistId,
),
}),
);
export const albums = pgTable(
'albums',
{
id: serial('id').primaryKey(),
createdAt: timestamp('created_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: timestamp('updated_at')
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
artistId: integer('artist_id').notNull(),
},
(table) => ({
artistIndex: index('albums__artist_id__idx').on(table.artistId),
}),
);
// relations
export const artistRelations = relations(artists, ({ many }) => ({
albums: many(albums),
members: many(artistsToMembers),
}));
export const albumRelations = relations(albums, ({ one }) => ({
artist: one(artists, {
fields: [albums.artistId],
references: [artists.id],
}),
}));
export const memberRelations = relations(members, ({ many }) => ({
artists: many(artistsToMembers),
}));
export const artistsToMembersRelations = relations(artistsToMembers, ({ one }) => ({
artist: one(artists, {
fields: [artistsToMembers.artistId],
references: [artists.id],
}),
member: one(members, {
fields: [artistsToMembers.memberId],
references: [members.id],
}),
}));
@@ -0,0 +1,156 @@
import { relations } from 'drizzle-orm';
import { boolean, integer, pgTable, primaryKey, text, uuid } from 'drizzle-orm/pg-core';
export const menuItems = pgTable('menu_items', {
id: uuid('id').defaultRandom().primaryKey(),
});
export const modifierGroups = pgTable('modifier_groups', {
id: uuid('id').defaultRandom().primaryKey(),
});
export const menuItemModifierGroups = pgTable(
'menu_item_modifier_groups',
{
menuItemId: uuid('menu_item_id')
.notNull()
.references(() => menuItems.id),
modifierGroupId: uuid('modifier_group_id')
.notNull()
.references(() => modifierGroups.id),
order: integer('order').default(0),
},
(table) => ({
menuItemIdModifierGroupIdOrderPk: primaryKey(
table.menuItemId,
table.modifierGroupId,
table.order,
),
}),
);
export const ingredients = pgTable('ingredients', {
id: uuid('id').defaultRandom().primaryKey(),
name: text('name').notNull(),
description: text('description'),
imageUrl: text('image_url'),
inStock: boolean('in_stock').default(true),
});
export const modifiers = pgTable('modifiers', {
id: uuid('id').defaultRandom().primaryKey(),
ingredientId: uuid('ingredient_id').references(() => ingredients.id),
itemId: uuid('item_id').references(() => menuItems.id),
});
export const menuItemIngredients = pgTable(
'menu_item_ingredients',
{
menuItemId: uuid('menu_item_id')
.notNull()
.references(() => menuItems.id),
ingredientId: uuid('ingredient_id')
.notNull()
.references(() => ingredients.id),
order: integer('order').default(0),
},
(table) => ({
menuItemIdIngredientIdOrderPk: primaryKey(
table.menuItemId,
table.ingredientId,
table.order,
),
}),
);
export const modifierGroupModifiers = pgTable(
'modifier_group_modifiers',
{
modifierGroupId: uuid('modifier_group_id')
.notNull()
.references(() => modifierGroups.id),
modifierId: uuid('modifier_id')
.notNull()
.references(() => modifiers.id),
order: integer('order').default(0),
},
(table) => ({
modifierGroupIdModifierIdOrderPk: primaryKey(
table.modifierGroupId,
table.modifierId,
table.order,
),
}),
);
export const menuItemRelations = relations(menuItems, ({ many }) => ({
ingredients: many(menuItemIngredients),
modifierGroups: many(menuItemModifierGroups),
// category: one(menuCategories, {
// fields: [menuItems.categoryId],
// references: [menuCategories.id],
// }),
}));
export const menuItemIngredientRelations = relations(
menuItemIngredients,
({ one }) => ({
menuItem: one(menuItems, {
fields: [menuItemIngredients.menuItemId],
references: [menuItems.id],
}),
ingredient: one(ingredients, {
fields: [menuItemIngredients.ingredientId],
references: [ingredients.id],
}),
}),
);
export const ingredientRelations = relations(ingredients, ({ many }) => ({
menuItems: many(menuItemIngredients),
}));
export const modifierGroupRelations = relations(modifierGroups, ({ many }) => ({
menuItems: many(menuItemModifierGroups),
modifiers: many(modifierGroupModifiers),
}));
export const modifierRelations = relations(modifiers, ({ one, many }) => ({
modifierGroups: many(modifierGroupModifiers),
ingredient: one(ingredients, {
fields: [modifiers.ingredientId],
references: [ingredients.id],
}),
item: one(menuItems, {
fields: [modifiers.itemId],
references: [menuItems.id],
}),
}));
export const menuItemModifierGroupRelations = relations(
menuItemModifierGroups,
({ one }) => ({
menuItem: one(menuItems, {
fields: [menuItemModifierGroups.menuItemId],
references: [menuItems.id],
}),
modifierGroup: one(modifierGroups, {
fields: [menuItemModifierGroups.modifierGroupId],
references: [modifierGroups.id],
}),
}),
);
export const modifierGroupModifierRelations = relations(
modifierGroupModifiers,
({ one }) => ({
modifierGroup: one(modifierGroups, {
fields: [modifierGroupModifiers.modifierGroupId],
references: [modifierGroups.id],
}),
modifier: one(modifiers, {
fields: [modifierGroupModifiers.modifierId],
references: [modifiers.id],
}),
}),
);
@@ -0,0 +1,318 @@
import 'dotenv/config';
import Docker from 'dockerode';
import { desc, sql } from 'drizzle-orm';
import { drizzle, type NodePgDatabase } from 'drizzle-orm/node-postgres';
import getPort from 'get-port';
import pg from 'pg';
import { v4 as uuid } from 'uuid';
import { afterAll, beforeAll, beforeEach, expectTypeOf, test } from 'vitest';
import * as schema from './pg.schema.ts';
const { Client } = pg;
const ENABLE_LOGGING = false;
/*
Test cases:
- querying nested relation without PK with additional fields
*/
let pgContainer: Docker.Container;
let db: NodePgDatabase<typeof schema>;
let client: pg.Client;
async function createDockerDB(): Promise<string> {
const 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))
);
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`;
}
beforeAll(async () => {
const connectionString = process.env['PG_CONNECTION_STRING'] ?? (await createDockerDB());
const sleep = 250;
let timeLeft = 5000;
let connected = false;
let lastError: unknown | undefined;
do {
try {
client = new Client(connectionString);
await 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 client?.end().catch(console.error);
await pgContainer?.stop().catch(console.error);
throw lastError;
}
db = drizzle(client, { schema, logger: ENABLE_LOGGING });
});
afterAll(async () => {
await client?.end().catch(console.error);
await pgContainer?.stop().catch(console.error);
});
beforeEach(async () => {
await db.execute(sql`drop schema public cascade`);
await db.execute(sql`create schema public`);
await db.execute(
sql`
CREATE TABLE IF NOT EXISTS "ingredients" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"description" text,
"image_url" text,
"in_stock" boolean DEFAULT true
);
CREATE TABLE IF NOT EXISTS "menu_item_ingredients" (
"menu_item_id" uuid NOT NULL,
"ingredient_id" uuid NOT NULL,
"order" integer DEFAULT 0
);
ALTER TABLE "menu_item_ingredients" ADD CONSTRAINT "menu_item_ingredients_menu_item_id_ingredient_id_order" PRIMARY KEY("menu_item_id","ingredient_id","order");
CREATE TABLE IF NOT EXISTS "menu_item_modifier_groups" (
"menu_item_id" uuid NOT NULL,
"modifier_group_id" uuid NOT NULL,
"order" integer DEFAULT 0
);
ALTER TABLE "menu_item_modifier_groups" ADD CONSTRAINT "menu_item_modifier_groups_menu_item_id_modifier_group_id_order" PRIMARY KEY("menu_item_id","modifier_group_id","order");
CREATE TABLE IF NOT EXISTS "menu_items" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL
);
CREATE TABLE IF NOT EXISTS "modifier_group_modifiers" (
"modifier_group_id" uuid NOT NULL,
"modifier_id" uuid NOT NULL,
"order" integer DEFAULT 0
);
ALTER TABLE "modifier_group_modifiers" ADD CONSTRAINT "modifier_group_modifiers_modifier_group_id_modifier_id_order" PRIMARY KEY("modifier_group_id","modifier_id","order");
CREATE TABLE IF NOT EXISTS "modifier_groups" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL
);
CREATE TABLE IF NOT EXISTS "modifiers" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"ingredient_id" uuid,
"item_id" uuid
);
DO $$ BEGIN
ALTER TABLE "menu_item_ingredients" ADD CONSTRAINT "menu_item_ingredients_menu_item_id_menu_items_id_fk" FOREIGN KEY ("menu_item_id") REFERENCES "menu_items"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "menu_item_ingredients" ADD CONSTRAINT "menu_item_ingredients_ingredient_id_ingredients_id_fk" FOREIGN KEY ("ingredient_id") REFERENCES "ingredients"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "menu_item_modifier_groups" ADD CONSTRAINT "menu_item_modifier_groups_menu_item_id_menu_items_id_fk" FOREIGN KEY ("menu_item_id") REFERENCES "menu_items"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "menu_item_modifier_groups" ADD CONSTRAINT "menu_item_modifier_groups_modifier_group_id_modifier_groups_id_fk" FOREIGN KEY ("modifier_group_id") REFERENCES "modifier_groups"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "modifier_group_modifiers" ADD CONSTRAINT "modifier_group_modifiers_modifier_group_id_modifier_groups_id_fk" FOREIGN KEY ("modifier_group_id") REFERENCES "modifier_groups"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "modifier_group_modifiers" ADD CONSTRAINT "modifier_group_modifiers_modifier_id_modifiers_id_fk" FOREIGN KEY ("modifier_id") REFERENCES "modifiers"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "modifiers" ADD CONSTRAINT "modifiers_ingredient_id_ingredients_id_fk" FOREIGN KEY ("ingredient_id") REFERENCES "ingredients"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "modifiers" ADD CONSTRAINT "modifiers_item_id_menu_items_id_fk" FOREIGN KEY ("item_id") REFERENCES "menu_items"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`,
);
});
test('Simple case from GH', async () => {
const firstMenuItemId = uuid();
const secondMenuItemId = uuid();
const firstModGroupsId = uuid();
const secondModGroupsId = uuid();
await db.insert(schema.menuItems).values([{ id: firstMenuItemId }, { id: secondMenuItemId }]);
await db.insert(schema.modifierGroups).values([{ id: firstModGroupsId }, { id: secondModGroupsId }]);
await db.insert(schema.menuItemModifierGroups).values([{
modifierGroupId: firstModGroupsId,
menuItemId: firstMenuItemId,
}, {
modifierGroupId: firstModGroupsId,
menuItemId: secondMenuItemId,
}, {
modifierGroupId: secondModGroupsId,
menuItemId: firstMenuItemId,
}]);
const firstIngredientId = uuid();
const secondIngredientId = uuid();
await db.insert(schema.ingredients).values([{
id: firstIngredientId,
name: 'first',
}, {
id: secondIngredientId,
name: 'second',
}]);
const firstModifierId = uuid();
const secondModifierId = uuid();
await db.insert(schema.modifiers).values([{
id: firstModifierId,
ingredientId: firstIngredientId,
itemId: firstMenuItemId,
}, {
id: secondModifierId,
ingredientId: secondIngredientId,
itemId: secondMenuItemId,
}]);
await db.insert(schema.modifierGroupModifiers).values([
{
modifierGroupId: firstModGroupsId,
modifierId: firstModifierId,
},
{
modifierGroupId: secondModGroupsId,
modifierId: secondModifierId,
},
]);
const response = await db.query.menuItems
.findMany({
with: {
modifierGroups: {
with: {
modifierGroup: {
with: {
modifiers: {
with: {
modifier: {
with: {
ingredient: true,
item: true,
},
},
},
orderBy: desc(schema.modifierGroupModifiers.order),
},
},
},
},
orderBy: schema.menuItemModifierGroups.order,
},
},
});
expectTypeOf(response).toEqualTypeOf<
{
id: string;
modifierGroups: {
menuItemId: string;
modifierGroupId: string;
order: number | null;
modifierGroup: {
id: string;
modifiers: {
modifierGroupId: string;
order: number | null;
modifierId: string;
modifier: {
id: string;
ingredientId: string | null;
itemId: string | null;
ingredient: {
id: string;
name: string;
description: string | null;
imageUrl: string | null;
inStock: boolean | null;
} | null;
item: {
id: string;
} | null;
};
}[];
};
}[];
}[]
>();
// TODO: don't rely on items order
// expect(response.length).eq(2);
// expect(response[0]?.modifierGroups.length).eq(1);
// expect(response[0]?.modifierGroups[0]?.modifierGroup.modifiers.length).eq(1);
// TODO: add correct IDs
// expect(response[0]?.modifierGroups[0]?.modifierGroup.modifiers[0]?.modifier.ingredient?.id).eq(
// '0b2b9abc-5975-4a1d-ba3d-6fc3b3149902',
// );
// expect(response[0]?.modifierGroups[0]?.modifierGroup.modifiers[0]?.modifier.item?.id).eq(
// 'a867133e-60b7-4003-aaa0-deeefad7e518',
// );
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,147 @@
import {
type AnyMySqlColumn,
bigint,
boolean,
mysqlSchema,
mysqlTable,
primaryKey,
serial,
text,
timestamp,
} from 'drizzle-orm/mysql-core';
import { relations } from 'drizzle-orm';
export const usersTable = mysqlTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: bigint('invited_by', { mode: 'number' }).references(
(): AnyMySqlColumn => usersTable.id,
),
});
const schemaV1 = mysqlSchema('schemaV1');
export const usersV1 = schemaV1.table('usersV1', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: bigint('invited_by', { mode: 'number' }),
});
export const usersTableV1 = schemaV1.table('users_table_V1', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: bigint('invited_by', { mode: 'number' }),
});
export const usersConfig = relations(usersTable, ({ one, many }) => ({
invitee: one(usersTable, {
fields: [usersTable.invitedBy],
references: [usersTable.id],
}),
usersToGroups: many(usersToGroupsTable),
posts: many(postsTable),
comments: many(commentsTable),
}));
export const groupsTable = mysqlTable('groups', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
description: text('description'),
});
export const groupsConfig = relations(groupsTable, ({ many }) => ({
usersToGroups: many(usersToGroupsTable),
}));
export const usersToGroupsTable = mysqlTable(
'users_to_groups',
{
id: serial('id').primaryKey(),
userId: bigint('user_id', { mode: 'number' }).notNull().references(
() => usersTable.id,
),
groupId: bigint('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 = mysqlTable('posts', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
ownerId: bigint('owner_id', { mode: 'number' }).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 = mysqlTable('comments', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
creator: bigint('creator', { mode: 'number' }).references(
() => usersTable.id,
),
postId: bigint('post_id', { mode: 'number' }).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 = mysqlTable('comment_likes', {
id: serial('id').primaryKey(),
creator: bigint('creator', { mode: 'number' }).references(
() => usersTable.id,
),
commentId: bigint('comment_id', { mode: 'number' }).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],
}),
}));
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,103 @@
import {
boolean,
integer,
type PgColumn,
pgSchema,
pgTable,
primaryKey,
serial,
text,
timestamp,
} from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
export const usersTable = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: integer('invited_by').references((): PgColumn => usersTable.id),
});
export const schemaV1 = pgSchema('schemaV1');
export const usersV1 = schemaV1.table('usersV1', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: integer('invited_by'),
});
export const usersTableV1 = schemaV1.table('users_table_V1', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: integer('invited_by'),
});
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').primaryKey(),
userId: integer('user_id').notNull().references(() => usersTable.id),
groupId: integer('group_id').notNull().references(() => groupsTable.id),
}, (t) => ({
pk: primaryKey(t.groupId, t.userId),
}));
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', { withTimezone: true }).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', { withTimezone: true }).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', { withTimezone: true }).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] }),
}));
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,106 @@
import { bigint, boolean, primaryKey, serial, singlestoreTable, text, timestamp } from 'drizzle-orm/singlestore-core';
import { relations } from 'drizzle-orm';
export const usersTable = singlestoreTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
verified: boolean('verified').notNull().default(false),
invitedBy: bigint('invited_by', { mode: 'number' }),
});
export const usersConfig = relations(usersTable, ({ one, many }) => ({
invitee: one(usersTable, {
fields: [usersTable.invitedBy],
references: [usersTable.id],
}),
usersToGroups: many(usersToGroupsTable),
posts: many(postsTable),
comments: many(commentsTable),
}));
export const groupsTable = singlestoreTable('groups', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
description: text('description'),
});
export const groupsConfig = relations(groupsTable, ({ many }) => ({
usersToGroups: many(usersToGroupsTable),
}));
export const usersToGroupsTable = singlestoreTable(
'users_to_groups',
{
id: serial('id').primaryKey(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
groupId: bigint('group_id', { mode: 'number' }).notNull(),
},
(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 = singlestoreTable('posts', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
ownerId: bigint('owner_id', { mode: 'number' }),
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 = singlestoreTable('comments', {
id: serial('id').primaryKey(),
content: text('content').notNull(),
creator: bigint('creator', { mode: 'number' }),
postId: bigint('post_id', { mode: 'number' }),
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 = singlestoreTable('comment_likes', {
id: serial('id').primaryKey(),
creator: bigint('creator', { mode: 'number' }),
commentId: bigint('comment_id', { mode: 'number' }),
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],
}),
}));
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,114 @@
import { type AnySQLiteColumn, integer, primaryKey, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { relations, sql } from 'drizzle-orm';
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],
}),
}));
@@ -0,0 +1,79 @@
import { relations } from 'drizzle-orm';
import { foreignKey, int, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
cityId: int('city_id').references(() => cities.id).notNull(),
homeCityId: int('home_city_id').references(() => cities.id),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
});
export const usersConfig = relations(users, ({ one, many }) => ({
city: one(cities, { relationName: 'UsersInCity', fields: [users.cityId], references: [cities.id] }),
homeCity: one(cities, { fields: [users.homeCityId], references: [cities.id] }),
posts: many(posts),
comments: many(comments),
}));
export const cities = sqliteTable('cities', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
});
export const citiesConfig = relations(cities, ({ many }) => ({
users: many(users, { relationName: 'UsersInCity' }),
}));
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey(),
title: text('title').notNull(),
authorId: int('author_id').references(() => users.id),
});
export const postsConfig = relations(posts, ({ one, many }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
comments: many(comments),
}));
export const comments = sqliteTable('comments', {
id: integer('id').primaryKey(),
postId: int('post_id').references(() => posts.id).notNull(),
authorId: int('author_id').references(() => users.id),
text: text('text').notNull(),
});
export const commentsConfig = relations(comments, ({ one }) => ({
post: one(posts, { fields: [comments.postId], references: [posts.id] }),
author: one(users, { fields: [comments.authorId], references: [users.id] }),
}));
export const books = sqliteTable('books', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
});
export const booksConfig = relations(books, ({ many }) => ({
authors: many(bookAuthors),
}));
export const bookAuthors = sqliteTable('book_authors', {
bookId: int('book_id').references(() => books.id).notNull(),
authorId: int('author_id').references(() => users.id).notNull(),
role: text('role').notNull(),
});
export const bookAuthorsConfig = relations(bookAuthors, ({ one }) => ({
book: one(books, { fields: [bookAuthors.bookId], references: [books.id] }),
author: one(users, { fields: [bookAuthors.authorId], references: [users.id] }),
}));
export const node = sqliteTable('node', {
id: integer('id').primaryKey(),
parentId: int('parent_id'),
leftId: int('left_id'),
rightId: int('right_id'),
}, (node) => ({
fk1: foreignKey(() => ({ columns: [node.parentId], foreignColumns: [node.id] })),
fk2: foreignKey(() => ({ columns: [node.leftId], foreignColumns: [node.id] })),
fk3: foreignKey(() => ({ columns: [node.rightId], foreignColumns: [node.id] })),
}));
export const nodeRelations = relations(node, ({ one }) => ({
parent: one(node, { fields: [node.parentId], references: [node.id] }),
left: one(node, { fields: [node.leftId], references: [node.id] }),
right: one(node, { fields: [node.rightId], references: [node.id] }),
}));
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff