From 99c5a58eb7f0500362f510e5de4a4695a3eaf37d Mon Sep 17 00:00:00 2001 From: Sasha <64744993+r1tsuu@users.noreply.github.com> Date: Tue, 17 Dec 2024 05:44:51 +0200 Subject: [PATCH] feat(db-sqlite): add `idType: 'uuid'` support --- .github/workflows/main.yml | 1 + docs/database/sqlite.mdx | 1 + packages/db-sqlite/src/index.ts | 6 +++--- .../db-sqlite/src/schema/buildDrizzleTable.ts | 3 +-- packages/db-sqlite/src/schema/setColumnID.ts | 13 ++++++++++++- packages/db-sqlite/src/types.ts | 3 ++- packages/drizzle/src/utilities/pushDevSchema.ts | 15 +++++++++------ test/custom-graphql/int.spec.ts | 2 +- test/database/int.spec.ts | 5 +++-- test/generateDatabaseAdapter.ts | 9 +++++++++ 10 files changed, 42 insertions(+), 16 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cab09983e14..4ac06a82def 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -180,6 +180,7 @@ jobs: - postgres-uuid - supabase - sqlite + - sqlite-uuid env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres diff --git a/docs/database/sqlite.mdx b/docs/database/sqlite.mdx index eea85e8e870..7d2df382275 100644 --- a/docs/database/sqlite.mdx +++ b/docs/database/sqlite.mdx @@ -40,6 +40,7 @@ export default buildConfig({ | `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. | | `migrationDir` | Customize the directory that migrations are stored. | | `logger` | The instance of the logger to be passed to drizzle. By default Payload's will be used. | +| `idType` | A string of 'number', or 'uuid' that is used for the data type given to id columns. | | `transactionOptions` | A SQLiteTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) | | `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. | | `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. | diff --git a/packages/db-sqlite/src/index.ts b/packages/db-sqlite/src/index.ts index 7cbe3953191..5d7c24aade5 100644 --- a/packages/db-sqlite/src/index.ts +++ b/packages/db-sqlite/src/index.ts @@ -61,8 +61,8 @@ export { sql } from 'drizzle-orm' const filename = fileURLToPath(import.meta.url) export function sqliteAdapter(args: Args): DatabaseAdapterObj { - const postgresIDType = args.idType || 'serial' - const payloadIDType = postgresIDType === 'serial' ? 'number' : 'text' + const sqliteIDType = args.idType || 'number' + const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number' function adapter({ payload }: { payload: Payload }) { const migrationDir = findMigrationDir(args.migrationDir) @@ -93,7 +93,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj { json: true, }, fieldConstraints: {}, - idType: postgresIDType, + idType: sqliteIDType, initializing, localesSuffix: args.localesSuffix || '_locales', logger: args.logger, diff --git a/packages/db-sqlite/src/schema/buildDrizzleTable.ts b/packages/db-sqlite/src/schema/buildDrizzleTable.ts index d5e273c875c..3b013864d92 100644 --- a/packages/db-sqlite/src/schema/buildDrizzleTable.ts +++ b/packages/db-sqlite/src/schema/buildDrizzleTable.ts @@ -59,9 +59,8 @@ export const buildDrizzleTable: BuildDrizzleTable = ({ adapter, locales, rawTabl break } - // Not used yet in SQLite but ready here. case 'uuid': { - let builder = text(column.name) + let builder = text(column.name, { length: 36 }) if (column.defaultRandom) { builder = builder.$defaultFn(() => uuidv4()) diff --git a/packages/db-sqlite/src/schema/setColumnID.ts b/packages/db-sqlite/src/schema/setColumnID.ts index cdcf6eb2220..164203d6f6e 100644 --- a/packages/db-sqlite/src/schema/setColumnID.ts +++ b/packages/db-sqlite/src/schema/setColumnID.ts @@ -1,6 +1,6 @@ import type { SetColumnID } from '@payloadcms/drizzle/types' -export const setColumnID: SetColumnID = ({ columns, fields }) => { +export const setColumnID: SetColumnID = ({ adapter, columns, fields }) => { const idField = fields.find((field) => field.name === 'id') if (idField) { if (idField.type === 'number') { @@ -22,6 +22,17 @@ export const setColumnID: SetColumnID = ({ columns, fields }) => { } } + if (adapter.idType === 'uuid') { + columns.id = { + name: 'id', + type: 'uuid', + defaultRandom: true, + primaryKey: true, + } + + return 'uuid' + } + columns.id = { name: 'id', type: 'integer', diff --git a/packages/db-sqlite/src/types.ts b/packages/db-sqlite/src/types.ts index 358cb764993..0bbac86fa6a 100644 --- a/packages/db-sqlite/src/types.ts +++ b/packages/db-sqlite/src/types.ts @@ -40,7 +40,7 @@ export type Args = { client: Config /** Generated schema from payload generate:db-schema file path */ generateSchemaOutputFile?: string - idType?: 'serial' | 'uuid' + idType?: 'number' | 'uuid' localesSuffix?: string logger?: DrizzleConfig['logger'] migrationDir?: string @@ -106,6 +106,7 @@ type SQLiteDrizzleAdapter = Omit< | 'drizzle' | 'dropDatabase' | 'execute' + | 'idType' | 'insert' | 'operators' | 'relations' diff --git a/packages/drizzle/src/utilities/pushDevSchema.ts b/packages/drizzle/src/utilities/pushDevSchema.ts index 71fbe315e95..71bf7d5242d 100644 --- a/packages/drizzle/src/utilities/pushDevSchema.ts +++ b/packages/drizzle/src/utilities/pushDevSchema.ts @@ -1,7 +1,7 @@ import prompts from 'prompts' import type { BasePostgresAdapter } from '../postgres/types.js' -import type { DrizzleAdapter } from '../types.js' +import type { DrizzleAdapter, PostgresDB } from '../types.js' /** * Pushes the development schema to the database using Drizzle. @@ -60,21 +60,24 @@ export const pushDevSchema = async (adapter: DrizzleAdapter) => { ? `"${adapter.schemaName}"."payload_migrations"` : '"payload_migrations"' + const drizzle = adapter.drizzle as PostgresDB + const result = await adapter.execute({ - drizzle: adapter.drizzle, + drizzle, raw: `SELECT * FROM ${migrationsTable} WHERE batch = '-1'`, }) const devPush = result.rows if (!devPush.length) { - await adapter.execute({ - drizzle: adapter.drizzle, - raw: `INSERT INTO ${migrationsTable} (name, batch) VALUES ('dev', '-1')`, + // Use drizzle for insert so $defaultFn's are called + drizzle.insert(adapter.tables.payload_migrations).values({ + name: 'dev', + batch: -1, }) } else { await adapter.execute({ - drizzle: adapter.drizzle, + drizzle, raw: `UPDATE ${migrationsTable} SET updated_at = CURRENT_TIMESTAMP WHERE batch = '-1'`, }) } diff --git a/test/custom-graphql/int.spec.ts b/test/custom-graphql/int.spec.ts index 016cb2107d8..9c18cab1855 100644 --- a/test/custom-graphql/int.spec.ts +++ b/test/custom-graphql/int.spec.ts @@ -24,7 +24,7 @@ describe('Custom GraphQL', () => { } }) - if (!['sqlite'].includes(process.env.PAYLOAD_DATABASE || '')) { + if (!['sqlite', 'sqlite-uuid'].includes(process.env.PAYLOAD_DATABASE || '')) { describe('Isolated Transaction ID', () => { it('should isolate transaction IDs between queries in the same request', async () => { const query = `query { diff --git a/test/database/int.spec.ts b/test/database/int.spec.ts index e743ab60f28..e39a2224224 100644 --- a/test/database/int.spec.ts +++ b/test/database/int.spec.ts @@ -530,7 +530,7 @@ describe('database', () => { describe('transactions', () => { describe('local api', () => { // sqlite cannot handle concurrent write transactions - if (!['sqlite'].includes(process.env.PAYLOAD_DATABASE)) { + if (!['sqlite', 'sqlite-uuid'].includes(process.env.PAYLOAD_DATABASE)) { it('should commit multiple operations in isolation', async () => { const req = { payload, @@ -1074,7 +1074,8 @@ describe('database', () => { data: { title: 'invalid', relationship: 'not-real-id' }, }) } catch (error) { - expect(error).toBeInstanceOf(Error) + // instanceof checks don't work with libsql + expect(error).toBeTruthy() } expect(invalidDoc).toBeUndefined() diff --git a/test/generateDatabaseAdapter.ts b/test/generateDatabaseAdapter.ts index 07254be6d1a..5cab958403a 100644 --- a/test/generateDatabaseAdapter.ts +++ b/test/generateDatabaseAdapter.ts @@ -53,6 +53,15 @@ export const allDatabaseAdapters = { url: process.env.SQLITE_URL || 'file:./payloadtests.db', }, })`, + 'sqlite-uuid': ` + import { sqliteAdapter } from '@payloadcms/db-sqlite' + + export const databaseAdapter = sqliteAdapter({ + idType: 'uuid', + client: { + url: process.env.SQLITE_URL || 'file:./payloadtests.db', + }, + })`, supabase: ` import { postgresAdapter } from '@payloadcms/db-postgres'