feat(db-sqlite): add idType: 'uuid' support (#10016)
Adds `idType: 'uuid'` to the SQLite adapter support:
```ts
sqliteAdapter({
idType: 'uuid',
})
```
Achieved through Drizzle's `$defaultFn()`
https://orm.drizzle.team/docs/latest-releases/drizzle-orm-v0283#-added-defaultfn--default-methods-to-column-builders
as SQLite doesn't have native UUID support. Added `sqlite-uuid` to CI.
This commit is contained in:
1
.github/workflows/main.yml
vendored
1
.github/workflows/main.yml
vendored
@@ -180,6 +180,7 @@ jobs:
|
|||||||
- postgres-uuid
|
- postgres-uuid
|
||||||
- supabase
|
- supabase
|
||||||
- sqlite
|
- sqlite
|
||||||
|
- sqlite-uuid
|
||||||
env:
|
env:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|||||||
@@ -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. |
|
| `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. |
|
| `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. |
|
| `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) |
|
| `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'. |
|
| `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'. |
|
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ export { sql } from 'drizzle-orm'
|
|||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
|
|
||||||
export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||||
const postgresIDType = args.idType || 'serial'
|
const sqliteIDType = args.idType || 'number'
|
||||||
const payloadIDType = postgresIDType === 'serial' ? 'number' : 'text'
|
const payloadIDType = sqliteIDType === 'uuid' ? 'text' : 'number'
|
||||||
|
|
||||||
function adapter({ payload }: { payload: Payload }) {
|
function adapter({ payload }: { payload: Payload }) {
|
||||||
const migrationDir = findMigrationDir(args.migrationDir)
|
const migrationDir = findMigrationDir(args.migrationDir)
|
||||||
@@ -93,7 +93,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
|||||||
json: true,
|
json: true,
|
||||||
},
|
},
|
||||||
fieldConstraints: {},
|
fieldConstraints: {},
|
||||||
idType: postgresIDType,
|
idType: sqliteIDType,
|
||||||
initializing,
|
initializing,
|
||||||
localesSuffix: args.localesSuffix || '_locales',
|
localesSuffix: args.localesSuffix || '_locales',
|
||||||
logger: args.logger,
|
logger: args.logger,
|
||||||
|
|||||||
@@ -59,9 +59,8 @@ export const buildDrizzleTable: BuildDrizzleTable = ({ adapter, locales, rawTabl
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not used yet in SQLite but ready here.
|
|
||||||
case 'uuid': {
|
case 'uuid': {
|
||||||
let builder = text(column.name)
|
let builder = text(column.name, { length: 36 })
|
||||||
|
|
||||||
if (column.defaultRandom) {
|
if (column.defaultRandom) {
|
||||||
builder = builder.$defaultFn(() => uuidv4())
|
builder = builder.$defaultFn(() => uuidv4())
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { SetColumnID } from '@payloadcms/drizzle/types'
|
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')
|
const idField = fields.find((field) => field.name === 'id')
|
||||||
if (idField) {
|
if (idField) {
|
||||||
if (idField.type === 'number') {
|
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 = {
|
columns.id = {
|
||||||
name: 'id',
|
name: 'id',
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export type Args = {
|
|||||||
client: Config
|
client: Config
|
||||||
/** Generated schema from payload generate:db-schema file path */
|
/** Generated schema from payload generate:db-schema file path */
|
||||||
generateSchemaOutputFile?: string
|
generateSchemaOutputFile?: string
|
||||||
idType?: 'serial' | 'uuid'
|
idType?: 'number' | 'uuid'
|
||||||
localesSuffix?: string
|
localesSuffix?: string
|
||||||
logger?: DrizzleConfig['logger']
|
logger?: DrizzleConfig['logger']
|
||||||
migrationDir?: string
|
migrationDir?: string
|
||||||
@@ -106,6 +106,7 @@ type SQLiteDrizzleAdapter = Omit<
|
|||||||
| 'drizzle'
|
| 'drizzle'
|
||||||
| 'dropDatabase'
|
| 'dropDatabase'
|
||||||
| 'execute'
|
| 'execute'
|
||||||
|
| 'idType'
|
||||||
| 'insert'
|
| 'insert'
|
||||||
| 'operators'
|
| 'operators'
|
||||||
| 'relations'
|
| 'relations'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import prompts from 'prompts'
|
import prompts from 'prompts'
|
||||||
|
|
||||||
import type { BasePostgresAdapter } from '../postgres/types.js'
|
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.
|
* Pushes the development schema to the database using Drizzle.
|
||||||
@@ -60,21 +60,24 @@ export const pushDevSchema = async (adapter: DrizzleAdapter) => {
|
|||||||
? `"${adapter.schemaName}"."payload_migrations"`
|
? `"${adapter.schemaName}"."payload_migrations"`
|
||||||
: '"payload_migrations"'
|
: '"payload_migrations"'
|
||||||
|
|
||||||
|
const drizzle = adapter.drizzle as PostgresDB
|
||||||
|
|
||||||
const result = await adapter.execute({
|
const result = await adapter.execute({
|
||||||
drizzle: adapter.drizzle,
|
drizzle,
|
||||||
raw: `SELECT * FROM ${migrationsTable} WHERE batch = '-1'`,
|
raw: `SELECT * FROM ${migrationsTable} WHERE batch = '-1'`,
|
||||||
})
|
})
|
||||||
|
|
||||||
const devPush = result.rows
|
const devPush = result.rows
|
||||||
|
|
||||||
if (!devPush.length) {
|
if (!devPush.length) {
|
||||||
await adapter.execute({
|
// Use drizzle for insert so $defaultFn's are called
|
||||||
drizzle: adapter.drizzle,
|
await drizzle.insert(adapter.tables.payload_migrations).values({
|
||||||
raw: `INSERT INTO ${migrationsTable} (name, batch) VALUES ('dev', '-1')`,
|
name: 'dev',
|
||||||
|
batch: -1,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await adapter.execute({
|
await adapter.execute({
|
||||||
drizzle: adapter.drizzle,
|
drizzle,
|
||||||
raw: `UPDATE ${migrationsTable} SET updated_at = CURRENT_TIMESTAMP WHERE batch = '-1'`,
|
raw: `UPDATE ${migrationsTable} SET updated_at = CURRENT_TIMESTAMP WHERE batch = '-1'`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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', () => {
|
describe('Isolated Transaction ID', () => {
|
||||||
it('should isolate transaction IDs between queries in the same request', async () => {
|
it('should isolate transaction IDs between queries in the same request', async () => {
|
||||||
const query = `query {
|
const query = `query {
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ describe('database', () => {
|
|||||||
describe('transactions', () => {
|
describe('transactions', () => {
|
||||||
describe('local api', () => {
|
describe('local api', () => {
|
||||||
// sqlite cannot handle concurrent write transactions
|
// 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 () => {
|
it('should commit multiple operations in isolation', async () => {
|
||||||
const req = {
|
const req = {
|
||||||
payload,
|
payload,
|
||||||
@@ -1074,7 +1074,8 @@ describe('database', () => {
|
|||||||
data: { title: 'invalid', relationship: 'not-real-id' },
|
data: { title: 'invalid', relationship: 'not-real-id' },
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
expect(error).toBeInstanceOf(Error)
|
// instanceof checks don't work with libsql
|
||||||
|
expect(error).toBeTruthy()
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(invalidDoc).toBeUndefined()
|
expect(invalidDoc).toBeUndefined()
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ export const allDatabaseAdapters = {
|
|||||||
url: process.env.SQLITE_URL || 'file:./payloadtests.db',
|
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: `
|
supabase: `
|
||||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user