feat(drizzle): abstract shared sql code to new package (#7320)
- Abstract shared sql code to a new drizzle package - Adds sqlite package, not ready to publish until drizzle patches some issues - Add `transactionOptions` to allow customizing or disabling db transactions - Adds "experimental" label to the `schemaName` property until drizzle patches an issue
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -22,6 +22,10 @@ meta_shared.json
|
||||
# Ignore test directory media folder/files
|
||||
/media
|
||||
test/media
|
||||
*payloadtests.db
|
||||
*payloadtests.db-journal
|
||||
*payloadtests.db-shm
|
||||
*payloadtests.db-wal
|
||||
/versions
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
6
.idea/payload.iml
generated
6
.idea/payload.iml
generated
@@ -75,8 +75,12 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.swc" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/ui/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/drizzle/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/drizzle/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-sqlite/.turbo" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages/db-sqlite/dist" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
|
||||
@@ -33,18 +33,18 @@ export default buildConfig({
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `pool` \* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres`. |
|
||||
| `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. |
|
||||
| `schemaName` | A string for the postgres schema to use, defaults to 'public'. |
|
||||
| `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'. |
|
||||
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
|
||||
|
||||
|
||||
| Option | Description |
|
||||
|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `pool` \* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres`. |
|
||||
| `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. |
|
||||
| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |
|
||||
| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |
|
||||
| `transactionOptions` | A PgTransactionConfig 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'. |
|
||||
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
|
||||
|
||||
## Access to Drizzle
|
||||
|
||||
|
||||
81
docs/database/sqlite.mdx
Normal file
81
docs/database/sqlite.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: SQLite
|
||||
label: SQLite
|
||||
order: 60
|
||||
desc: Payload supports SQLite through an officially supported Drizzle Database Adapter.
|
||||
keywords: SQLite, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
To use Payload with SQLite, install the package `@payloadcms/db-sqlite`. It leverages Drizzle ORM and `libSQL` to interact with a SQLite database that you provide.
|
||||
|
||||
It automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.
|
||||
|
||||
To configure Payload to use SQLite, pass the `sqliteAdapter` to your Payload Config as follows:
|
||||
|
||||
```ts
|
||||
import { sqliteAdapter } from '@payloadcms/db-sqlite'
|
||||
|
||||
export default buildConfig({
|
||||
// Your config goes here
|
||||
collections: [
|
||||
// Collections go here
|
||||
],
|
||||
// Configure the SQLite adapter here
|
||||
db: sqliteAdapter({
|
||||
// SQLite-specific arguments go here.
|
||||
// `client.url` is required.
|
||||
client: {
|
||||
url: process.env.DATABASE_URL,
|
||||
authToken: process.env.DATABASE_AUTH_TOKEN,
|
||||
}
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Option | Description |
|
||||
|-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `client` \* | [Client connection options](https://orm.drizzle.team/docs/get-started-sqlite#turso) that will be passed to `createClient` from `@libsql/client`. |
|
||||
| `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. |
|
||||
| `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'. |
|
||||
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
|
||||
|
||||
|
||||
|
||||
## Access to Drizzle
|
||||
|
||||
After Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.
|
||||
|
||||
You can access Drizzle as follows:
|
||||
|
||||
```text
|
||||
payload.db.drizzle
|
||||
```
|
||||
|
||||
## Tables and relations
|
||||
|
||||
In addition to exposing Drizzle directly, all of the tables and Drizzle relations are exposed for you via the `payload.db` property as well.
|
||||
|
||||
- Tables - `payload.db.tables`
|
||||
- Relations - `payload.db.relations`
|
||||
|
||||
## Prototyping in development mode
|
||||
|
||||
Drizzle exposes two ways to work locally in development mode.
|
||||
|
||||
The first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](/docs/database/migrations) commands.
|
||||
|
||||
You will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.
|
||||
|
||||
Alternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.
|
||||
|
||||
## Migration workflows
|
||||
|
||||
In SQLite, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.
|
||||
|
||||
For more information about migrations, [click here](/docs/beta/database/migrations#when-to-run-migrations).
|
||||
@@ -68,3 +68,7 @@ The following functions can be used for managing transactions:
|
||||
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
|
||||
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
|
||||
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
|
||||
|
||||
## Disabling Transactions
|
||||
|
||||
If you wish to disable transactions entirely, you can do so by passing `false` as the `transactionOptions` in your database adapter configuration. All the official Payload database adapters support this option.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"build:create-payload-app": "turbo build --filter create-payload-app",
|
||||
"build:db-mongodb": "turbo build --filter db-mongodb",
|
||||
"build:db-postgres": "turbo build --filter db-postgres",
|
||||
"build:db-sqlite": "turbo build --filter db-sqlite",
|
||||
"build:drizzle": "turbo build --filter drizzle",
|
||||
"build:email-nodemailer": "turbo build --filter email-nodemailer",
|
||||
"build:email-resend": "turbo build --filter email-resend",
|
||||
"build:eslint-config": "turbo build --filter eslint-config",
|
||||
@@ -93,6 +95,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@libsql/client": "0.6.2",
|
||||
"@next/bundle-analyzer": "15.0.0-canary.53",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@payloadcms/eslint-plugin": "workspace:*",
|
||||
@@ -116,7 +119,6 @@
|
||||
"create-payload-app": "workspace:*",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "16.4.5",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"escape-html": "^1.0.3",
|
||||
"execa": "5.1.1",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
import { commonjs } from '@hyrious/esbuild-plugin-commonjs'
|
||||
|
||||
throw new Error('asfdadsf')
|
||||
async function build() {
|
||||
const resultServer = await esbuild.build({
|
||||
entryPoints: ['src/index.ts'],
|
||||
@@ -18,9 +18,9 @@ async function build() {
|
||||
'*.scss',
|
||||
'*.css',
|
||||
'drizzle-kit',
|
||||
'libsql',
|
||||
'pg',
|
||||
'@payloadcms/translations',
|
||||
'@payloadcms/drizzle',
|
||||
'payload',
|
||||
'payload/*',
|
||||
],
|
||||
|
||||
@@ -40,11 +40,12 @@
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepack": "pnpm clean && pnpm turbo build",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build",
|
||||
"renamePredefinedMigrations": "tsx ./scripts/renamePredefinedMigrations.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@libsql/client": "^0.5.2",
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { Connect, Payload } from 'payload'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { pushDevSchema } from '@payloadcms/drizzle'
|
||||
import { drizzle } from 'drizzle-orm/node-postgres'
|
||||
import pg from 'pg'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { pushDevSchema } from './utilities/pushDevSchema.js'
|
||||
|
||||
const connectWithReconnect = async function ({
|
||||
adapter,
|
||||
payload,
|
||||
@@ -71,12 +70,7 @@ export const connect: Connect = async function connect(
|
||||
if (!hotReload) {
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
this.payload.logger.info(`---- DROPPING TABLES SCHEMA(${this.schemaName || 'public'}) ----`)
|
||||
await this.drizzle.execute(
|
||||
sql.raw(`
|
||||
drop schema if exists ${this.schemaName || 'public'} cascade;
|
||||
create schema ${this.schemaName || 'public'};
|
||||
`),
|
||||
)
|
||||
await this.dropDatabase({ adapter: this })
|
||||
this.payload.logger.info('---- DROPPED TABLES ----')
|
||||
}
|
||||
}
|
||||
@@ -92,7 +86,7 @@ export const connect: Connect = async function connect(
|
||||
process.env.PAYLOAD_MIGRATING !== 'true' &&
|
||||
this.push !== false
|
||||
) {
|
||||
await pushDevSchema(this)
|
||||
await pushDevSchema(this as unknown as DrizzleAdapter)
|
||||
}
|
||||
|
||||
if (typeof this.resolveInitializing === 'function') this.resolveInitializing()
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { Count, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { ChainedMethods } from './find/chainMethods.js'
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { chainMethods } from './find/chainMethods.js'
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
|
||||
export const count: Count = async function count(
|
||||
this: PostgresAdapter,
|
||||
{ collection, locale, req, where: whereArg },
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const table = this.tables[tableName]
|
||||
|
||||
const { joins, where } = await buildQuery({
|
||||
adapter: this,
|
||||
fields: collectionConfig.fields,
|
||||
locale,
|
||||
tableName,
|
||||
where: whereArg,
|
||||
})
|
||||
|
||||
const selectCountMethods: ChainedMethods = []
|
||||
|
||||
Object.entries(joins).forEach(([joinTable, condition]) => {
|
||||
if (joinTable) {
|
||||
selectCountMethods.push({
|
||||
args: [this.tables[joinTable], condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const countResult = await chainMethods({
|
||||
methods: selectCountMethods,
|
||||
query: db
|
||||
.select({
|
||||
count: sql<number>`count
|
||||
(DISTINCT ${this.tables[tableName].id})`,
|
||||
})
|
||||
.from(table)
|
||||
.where(where),
|
||||
})
|
||||
|
||||
return { totalDocs: Number(countResult[0].count) }
|
||||
}
|
||||
33
packages/db-postgres/src/countDistinct.ts
Normal file
33
packages/db-postgres/src/countDistinct.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { ChainedMethods, TransactionPg } from '@payloadcms/drizzle/types'
|
||||
|
||||
import { chainMethods } from '@payloadcms/drizzle'
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { CountDistinct, PostgresAdapter } from './types.js'
|
||||
|
||||
export const countDistinct: CountDistinct = async function countDistinct(
|
||||
this: PostgresAdapter,
|
||||
{ db, joins, tableName, where },
|
||||
) {
|
||||
const chainedMethods: ChainedMethods = []
|
||||
|
||||
joins.forEach(({ condition, table }) => {
|
||||
chainedMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
})
|
||||
|
||||
const countResult = await chainMethods({
|
||||
methods: chainedMethods,
|
||||
query: (db as TransactionPg)
|
||||
.select({
|
||||
count: sql<string>`count
|
||||
(DISTINCT ${this.tables[tableName].id})`,
|
||||
})
|
||||
.from(this.tables[tableName])
|
||||
.where(where),
|
||||
})
|
||||
|
||||
return Number(countResult[0].count)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable no-restricted-syntax, no-await-in-loop */
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { CreateMigration, MigrationTemplateArgs } from 'payload'
|
||||
import type { CreateMigration } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
@@ -11,38 +10,11 @@ import { fileURLToPath } from 'url'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { defaultDrizzleSnapshot } from './defaultSnapshot.js'
|
||||
import { getMigrationTemplate } from './getMigrationTemplate.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
const migrationTemplate = ({
|
||||
downSQL,
|
||||
imports,
|
||||
upSQL,
|
||||
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||
${imports ? `${imports}\n` : ''}
|
||||
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
|
||||
${upSQL}
|
||||
};
|
||||
|
||||
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
|
||||
${downSQL}
|
||||
};
|
||||
`
|
||||
|
||||
const getDefaultDrizzleSnapshot = (): DrizzleSnapshotJSON => ({
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
_meta: {
|
||||
columns: {},
|
||||
schemas: {},
|
||||
tables: {},
|
||||
},
|
||||
dialect: 'pg',
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
schemas: {},
|
||||
tables: {},
|
||||
version: '5',
|
||||
})
|
||||
|
||||
export const createMigration: CreateMigration = async function createMigration(
|
||||
this: PostgresAdapter,
|
||||
{ file, forceAcceptWarning, migrationName, payload },
|
||||
@@ -75,7 +47,7 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
|
||||
const filePath = `${dir}/${fileName}`
|
||||
|
||||
let drizzleJsonBefore = getDefaultDrizzleSnapshot()
|
||||
let drizzleJsonBefore = defaultDrizzleSnapshot
|
||||
|
||||
if (!upSQL) {
|
||||
// Get latest migration snapshot
|
||||
@@ -93,7 +65,7 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
|
||||
const sqlStatementsUp = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
|
||||
const sqlStatementsDown = await generateMigration(drizzleJsonAfter, drizzleJsonBefore)
|
||||
const sqlExecute = 'await payload.db.drizzle.execute(sql`'
|
||||
const sqlExecute = 'await db.execute(sql`'
|
||||
|
||||
if (sqlStatementsUp?.length) {
|
||||
upSQL = `${sqlExecute}\n ${sqlStatementsUp?.join('\n')}\`)`
|
||||
@@ -121,15 +93,15 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write schema
|
||||
fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2))
|
||||
// write schema
|
||||
fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2))
|
||||
}
|
||||
|
||||
// write migration
|
||||
fs.writeFileSync(
|
||||
`${filePath}.ts`,
|
||||
migrationTemplate({
|
||||
getMigrationTemplate({
|
||||
downSQL: downSQL || ` // Migration code`,
|
||||
imports,
|
||||
upSQL: upSQL || ` // Migration code`,
|
||||
|
||||
16
packages/db-postgres/src/defaultSnapshot.ts
Normal file
16
packages/db-postgres/src/defaultSnapshot.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
|
||||
export const defaultDrizzleSnapshot: DrizzleSnapshotJSON = {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
_meta: {
|
||||
columns: {},
|
||||
schemas: {},
|
||||
tables: {},
|
||||
},
|
||||
dialect: 'pg',
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
schemas: {},
|
||||
tables: {},
|
||||
version: '5',
|
||||
}
|
||||
8
packages/db-postgres/src/deleteWhere.ts
Normal file
8
packages/db-postgres/src/deleteWhere.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
|
||||
import type { DeleteWhere } from './types.js'
|
||||
|
||||
export const deleteWhere: DeleteWhere = async function deleteWhere({ db, tableName, where }) {
|
||||
const table = this.tables[tableName]
|
||||
await (db as TransactionPg).delete(table).where(where)
|
||||
}
|
||||
9
packages/db-postgres/src/dropDatabase.ts
Normal file
9
packages/db-postgres/src/dropDatabase.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { DropDatabase } from './types.js'
|
||||
|
||||
export const dropDatabase: DropDatabase = async function dropDatabase({ adapter }) {
|
||||
await adapter.execute({
|
||||
drizzle: adapter.drizzle,
|
||||
raw: `drop schema if exists ${this.schemaName || 'public'} cascade;
|
||||
create schema ${this.schemaName || 'public'};`,
|
||||
})
|
||||
}
|
||||
13
packages/db-postgres/src/execute.ts
Normal file
13
packages/db-postgres/src/execute.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { Execute } from './types.js'
|
||||
|
||||
export const execute: Execute<any> = function execute({ db, drizzle, raw, sql: statement }) {
|
||||
const executeFrom = db ?? drizzle
|
||||
|
||||
if (raw) {
|
||||
return executeFrom.execute(sql.raw(raw))
|
||||
} else {
|
||||
return executeFrom.execute(sql`${statement}`)
|
||||
}
|
||||
}
|
||||
16
packages/db-postgres/src/getMigrationTemplate.ts
Normal file
16
packages/db-postgres/src/getMigrationTemplate.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { MigrationTemplateArgs } from 'payload'
|
||||
|
||||
export const getMigrationTemplate = ({
|
||||
downSQL,
|
||||
imports,
|
||||
upSQL,
|
||||
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
|
||||
${imports ? `${imports}\n` : ''}
|
||||
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||
${upSQL}
|
||||
}
|
||||
|
||||
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||
${downSQL}
|
||||
}
|
||||
`
|
||||
@@ -1,42 +1,54 @@
|
||||
import type { DatabaseAdapterObj, Payload } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import {
|
||||
beginTransaction,
|
||||
commitTransaction,
|
||||
count,
|
||||
create,
|
||||
createGlobal,
|
||||
createGlobalVersion,
|
||||
createVersion,
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
destroy,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
findMigrationDir,
|
||||
findOne,
|
||||
findVersions,
|
||||
migrate,
|
||||
migrateDown,
|
||||
migrateFresh,
|
||||
migrateRefresh,
|
||||
migrateReset,
|
||||
migrateStatus,
|
||||
operatorMap,
|
||||
queryDrafts,
|
||||
rollbackTransaction,
|
||||
updateGlobal,
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { createDatabaseAdapter } from 'payload'
|
||||
|
||||
import type { Args, PostgresAdapter } from './types.js'
|
||||
|
||||
import { connect } from './connect.js'
|
||||
import { count } from './count.js'
|
||||
import { create } from './create.js'
|
||||
import { createGlobal } from './createGlobal.js'
|
||||
import { createGlobalVersion } from './createGlobalVersion.js'
|
||||
import { countDistinct } from './countDistinct.js'
|
||||
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal.js'
|
||||
import { createJSONQuery } from './createJSONQuery/index.js'
|
||||
import { createMigration } from './createMigration.js'
|
||||
import { createVersion } from './createVersion.js'
|
||||
import { deleteMany } from './deleteMany.js'
|
||||
import { deleteOne } from './deleteOne.js'
|
||||
import { deleteVersions } from './deleteVersions.js'
|
||||
import { destroy } from './destroy.js'
|
||||
import { find } from './find.js'
|
||||
import { findGlobal } from './findGlobal.js'
|
||||
import { findGlobalVersions } from './findGlobalVersions.js'
|
||||
import { findOne } from './findOne.js'
|
||||
import { findVersions } from './findVersions.js'
|
||||
import { defaultDrizzleSnapshot } from './defaultSnapshot.js'
|
||||
import { deleteWhere } from './deleteWhere.js'
|
||||
import { dropDatabase } from './dropDatabase.js'
|
||||
import { execute } from './execute.js'
|
||||
import { getMigrationTemplate } from './getMigrationTemplate.js'
|
||||
import { init } from './init.js'
|
||||
import { migrate } from './migrate.js'
|
||||
import { migrateDown } from './migrateDown.js'
|
||||
import { migrateFresh } from './migrateFresh.js'
|
||||
import { migrateRefresh } from './migrateRefresh.js'
|
||||
import { migrateReset } from './migrateReset.js'
|
||||
import { migrateStatus } from './migrateStatus.js'
|
||||
import { queryDrafts } from './queryDrafts.js'
|
||||
import { beginTransaction } from './transactions/beginTransaction.js'
|
||||
import { commitTransaction } from './transactions/commitTransaction.js'
|
||||
import { rollbackTransaction } from './transactions/rollbackTransaction.js'
|
||||
import { updateOne } from './update.js'
|
||||
import { updateGlobal } from './updateGlobal.js'
|
||||
import { updateGlobalVersion } from './updateGlobalVersion.js'
|
||||
import { updateVersion } from './updateVersion.js'
|
||||
import { insert } from './insert.js'
|
||||
import { requireDrizzleKit } from './requireDrizzleKit.js'
|
||||
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types.js'
|
||||
|
||||
@@ -58,13 +70,19 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
|
||||
return createDatabaseAdapter<PostgresAdapter>({
|
||||
name: 'postgres',
|
||||
defaultDrizzleSnapshot,
|
||||
drizzle: undefined,
|
||||
enums: {},
|
||||
features: {
|
||||
json: true,
|
||||
},
|
||||
fieldConstraints: {},
|
||||
getMigrationTemplate,
|
||||
idType: postgresIDType,
|
||||
initializing,
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
operators: operatorMap,
|
||||
pgSchema: undefined,
|
||||
pool: undefined,
|
||||
poolOptions: args.pool,
|
||||
@@ -76,29 +94,37 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
sessions: {},
|
||||
tableNameMap: new Map<string, string>(),
|
||||
tables: {},
|
||||
transactionOptions: args.transactionOptions || undefined,
|
||||
versionsSuffix: args.versionsSuffix || '_v',
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction,
|
||||
beginTransaction: args.transactionOptions === false ? undefined : beginTransaction,
|
||||
commitTransaction,
|
||||
connect,
|
||||
convertPathToJSONTraversal,
|
||||
count,
|
||||
countDistinct,
|
||||
create,
|
||||
createGlobal,
|
||||
createGlobalVersion,
|
||||
createJSONQuery,
|
||||
createMigration,
|
||||
createVersion,
|
||||
defaultIDType: payloadIDType,
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
deleteWhere,
|
||||
destroy,
|
||||
dropDatabase,
|
||||
execute,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
findOne,
|
||||
findVersions,
|
||||
init,
|
||||
insert,
|
||||
migrate,
|
||||
migrateDown,
|
||||
migrateFresh,
|
||||
@@ -109,6 +135,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
payload,
|
||||
queryDrafts,
|
||||
rejectInitializing,
|
||||
requireDrizzleKit,
|
||||
resolveInitializing,
|
||||
rollbackTransaction,
|
||||
updateGlobal,
|
||||
@@ -123,42 +150,3 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
init: adapter,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find migrations directory.
|
||||
*
|
||||
* Checks for the following directories in order:
|
||||
* - `migrationDir` argument from Payload config
|
||||
* - `src/migrations`
|
||||
* - `dist/migrations`
|
||||
* - `migrations`
|
||||
*
|
||||
* Defaults to `src/migrations`
|
||||
*
|
||||
* @param migrationDir
|
||||
* @returns
|
||||
*/
|
||||
function findMigrationDir(migrationDir?: string): string {
|
||||
const cwd = process.cwd()
|
||||
const srcDir = path.resolve(cwd, 'src/migrations')
|
||||
const distDir = path.resolve(cwd, 'dist/migrations')
|
||||
const relativeMigrations = path.resolve(cwd, 'migrations')
|
||||
|
||||
// Use arg if provided
|
||||
if (migrationDir) return migrationDir
|
||||
|
||||
// Check other common locations
|
||||
if (fs.existsSync(srcDir)) {
|
||||
return srcDir
|
||||
}
|
||||
|
||||
if (fs.existsSync(distDir)) {
|
||||
return distDir
|
||||
}
|
||||
|
||||
if (fs.existsSync(relativeMigrations)) {
|
||||
return relativeMigrations
|
||||
}
|
||||
|
||||
return srcDir
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { createTableName } from '../../drizzle/src/createTableName.js'
|
||||
import { buildTable } from './schema/build.js'
|
||||
import { createTableName } from './schema/createTableName.js'
|
||||
|
||||
export const init: Init = function init(this: PostgresAdapter) {
|
||||
if (this.schemaName) {
|
||||
@@ -17,7 +17,6 @@ export const init: Init = function init(this: PostgresAdapter) {
|
||||
} else {
|
||||
this.pgSchema = { table: pgTable }
|
||||
}
|
||||
|
||||
if (this.payload.config.localization) {
|
||||
this.enums.enum__locales = pgEnum(
|
||||
'_locales',
|
||||
|
||||
25
packages/db-postgres/src/insert.ts
Normal file
25
packages/db-postgres/src/insert.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
|
||||
import type { Insert } from './types.js'
|
||||
|
||||
export const insert: Insert = async function insert({
|
||||
db,
|
||||
onConflictDoUpdate,
|
||||
tableName,
|
||||
values,
|
||||
}): Promise<Record<string, unknown>[]> {
|
||||
const table = this.tables[tableName]
|
||||
let result
|
||||
|
||||
if (onConflictDoUpdate) {
|
||||
result = await (db as TransactionPg)
|
||||
.insert(table)
|
||||
.values(values)
|
||||
.onConflictDoUpdate(onConflictDoUpdate)
|
||||
.returning()
|
||||
} else {
|
||||
result = await (db as TransactionPg).insert(table).values(values).returning()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
import type { Field, Payload, PayloadRequest } from 'payload'
|
||||
|
||||
import type { DrizzleTransaction, PostgresAdapter } from '../../../types.js'
|
||||
import { upsertRow } from '@payloadcms/drizzle'
|
||||
|
||||
import type { PostgresAdapter } from '../../../types.js'
|
||||
import type { DocsToResave } from '../types.js'
|
||||
|
||||
import { upsertRow } from '../../../upsertRow/index.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
collectionSlug?: string
|
||||
db: DrizzleTransaction
|
||||
db: TransactionPg
|
||||
debug: boolean
|
||||
docsToResave: DocsToResave
|
||||
fields: Field[]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
|
||||
@@ -37,8 +38,8 @@ type Args = {
|
||||
* @param req
|
||||
*/
|
||||
export const migratePostgresV2toV3 = async ({ debug, payload, req }: Args) => {
|
||||
const adapter = payload.db as PostgresAdapter
|
||||
const db = adapter.sessions[await req.transactionID]?.db
|
||||
const adapter = payload.db as unknown as PostgresAdapter
|
||||
const db = adapter.sessions[await req.transactionID].db as TransactionPg
|
||||
const dir = payload.db.migrationDir
|
||||
|
||||
// get the drizzle migrateUpSQL from drizzle using the last schema
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
import type { Field, Payload, PayloadRequest } from 'payload'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { DrizzleTransaction, PostgresAdapter } from '../../types.js'
|
||||
import type { PostgresAdapter } from '../../types.js'
|
||||
import type { DocsToResave, PathsToQuery } from './types.js'
|
||||
|
||||
import { fetchAndResave } from './fetchAndResave/index.js'
|
||||
@@ -10,7 +11,7 @@ import { fetchAndResave } from './fetchAndResave/index.js'
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
collectionSlug?: string
|
||||
db: DrizzleTransaction
|
||||
db: TransactionPg
|
||||
debug: boolean
|
||||
fields: Field[]
|
||||
globalSlug?: string
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
import type { Field, Payload } from 'payload'
|
||||
|
||||
import { tabHasName } from 'payload/shared'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { DrizzleTransaction, PostgresAdapter } from '../../types.js'
|
||||
import type { PostgresAdapter } from '../../types.js'
|
||||
import type { PathsToQuery } from './types.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
collectionSlug?: string
|
||||
columnPrefix: string
|
||||
db: DrizzleTransaction
|
||||
db: TransactionPg
|
||||
disableNotNull: boolean
|
||||
fields: Field[]
|
||||
globalSlug?: string
|
||||
|
||||
5
packages/db-postgres/src/requireDrizzleKit.ts
Normal file
5
packages/db-postgres/src/requireDrizzleKit.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { RequireDrizzleKit } from '@payloadcms/drizzle/types'
|
||||
|
||||
import { createRequire } from 'module'
|
||||
const require = createRequire(import.meta.url)
|
||||
export const requireDrizzleKit: RequireDrizzleKit = () => require('drizzle-kit/payload')
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { Field } from 'payload'
|
||||
|
||||
import { createTableName } from '@payloadcms/drizzle'
|
||||
import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
foreignKey,
|
||||
@@ -24,7 +25,6 @@ import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { GenericColumns, GenericTable, IDType, PostgresAdapter } from '../types.js'
|
||||
|
||||
import { createTableName } from './createTableName.js'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap.js'
|
||||
import { setColumnID } from './setColumnID.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
@@ -3,6 +3,11 @@ import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, PgColumnBuilder } from 'drizzle-orm/pg-core'
|
||||
import type { Field, TabAsField } from 'payload'
|
||||
|
||||
import {
|
||||
createTableName,
|
||||
hasLocalesTable,
|
||||
validateExistingBlockIsIdentical,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
PgNumericBuilder,
|
||||
@@ -26,13 +31,10 @@ import toSnakeCase from 'to-snake-case'
|
||||
import type { GenericColumns, IDType, PostgresAdapter } from '../types.js'
|
||||
import type { BaseExtraConfig, RelationMap } from './build.js'
|
||||
|
||||
import { hasLocalesTable } from '../utilities/hasLocalesTable.js'
|
||||
import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { createTableName } from './createTableName.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap.js'
|
||||
import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
import type { Operators } from '@payloadcms/drizzle'
|
||||
import type {
|
||||
BuildQueryJoinAliases,
|
||||
DrizzleAdapter,
|
||||
TransactionPg,
|
||||
} from '@payloadcms/drizzle/types'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type {
|
||||
ColumnBaseConfig,
|
||||
ColumnDataType,
|
||||
DrizzleConfig,
|
||||
ExtractTablesWithRelations,
|
||||
Relation,
|
||||
Relations,
|
||||
SQL,
|
||||
} from 'drizzle-orm'
|
||||
import type { NodePgDatabase, NodePgQueryResultHKT } from 'drizzle-orm/node-postgres'
|
||||
import type { NodePgDatabase } from 'drizzle-orm/node-postgres'
|
||||
import type {
|
||||
PgColumn,
|
||||
PgEnum,
|
||||
PgInsertOnConflictDoUpdateConfig,
|
||||
PgSchema,
|
||||
PgTableWithColumns,
|
||||
PgTransaction,
|
||||
PgTransactionConfig,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
|
||||
import type { BaseDatabaseAdapter, Payload, PayloadRequest } from 'payload'
|
||||
import type { Pool, PoolConfig } from 'pg'
|
||||
|
||||
export type DrizzleDB = NodePgDatabase<Record<string, unknown>>
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
import type { Pool, PoolConfig, QueryResult } from 'pg'
|
||||
|
||||
export type Args = {
|
||||
idType?: 'serial' | 'uuid'
|
||||
@@ -28,7 +34,12 @@ export type Args = {
|
||||
pool: PoolConfig
|
||||
push?: boolean
|
||||
relationshipsSuffix?: string
|
||||
/**
|
||||
* The schema name to use for the database
|
||||
* @experimental This only works when there are not other tables or enums of the same name in the database under a different schema. Awaiting fix from Drizzle.
|
||||
*/
|
||||
schemaName?: string
|
||||
transactionOptions?: PgTransactionConfig | false
|
||||
versionsSuffix?: string
|
||||
}
|
||||
|
||||
@@ -45,22 +56,64 @@ export type GenericTable = PgTableWithColumns<{
|
||||
columns: GenericColumns
|
||||
dialect: string
|
||||
name: string
|
||||
schema: undefined
|
||||
schema: string
|
||||
}>
|
||||
|
||||
export type GenericEnum = PgEnum<[string, ...string[]]>
|
||||
|
||||
export type GenericRelation = Relations<string, Record<string, Relation<string>>>
|
||||
|
||||
export type DrizzleTransaction = PgTransaction<
|
||||
NodePgQueryResultHKT,
|
||||
Record<string, unknown>,
|
||||
ExtractTablesWithRelations<Record<string, unknown>>
|
||||
export type PostgresDB = NodePgDatabase<Record<string, unknown>>
|
||||
|
||||
export type CountDistinct = (args: {
|
||||
db: PostgresDB | TransactionPg
|
||||
joins: BuildQueryJoinAliases
|
||||
tableName: string
|
||||
where: SQL
|
||||
}) => Promise<number>
|
||||
|
||||
export type DeleteWhere = (args: {
|
||||
db: PostgresDB | TransactionPg
|
||||
tableName: string
|
||||
where: SQL
|
||||
}) => Promise<void>
|
||||
|
||||
export type DropDatabase = (args: { adapter: PostgresAdapter }) => Promise<void>
|
||||
|
||||
export type Execute<T> = (args: {
|
||||
db?: PostgresDB | TransactionPg
|
||||
drizzle?: PostgresDB
|
||||
raw?: string
|
||||
sql?: SQL<unknown>
|
||||
}) => Promise<QueryResult<Record<string, T>>>
|
||||
|
||||
export type Insert = (args: {
|
||||
db: PostgresDB | TransactionPg
|
||||
onConflictDoUpdate?: PgInsertOnConflictDoUpdateConfig<any>
|
||||
tableName: string
|
||||
values: Record<string, unknown> | Record<string, unknown>[]
|
||||
}) => Promise<Record<string, unknown>[]>
|
||||
|
||||
type PostgresDrizzleAdapter = Omit<
|
||||
DrizzleAdapter,
|
||||
| 'countDistinct'
|
||||
| 'deleteWhere'
|
||||
| 'drizzle'
|
||||
| 'dropDatabase'
|
||||
| 'execute'
|
||||
| 'insert'
|
||||
| 'operators'
|
||||
| 'relations'
|
||||
>
|
||||
|
||||
export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
drizzle: DrizzleDB
|
||||
export type PostgresAdapter = {
|
||||
countDistinct: CountDistinct
|
||||
defaultDrizzleSnapshot: DrizzleSnapshotJSON
|
||||
deleteWhere: DeleteWhere
|
||||
drizzle: PostgresDB
|
||||
dropDatabase: DropDatabase
|
||||
enums: Record<string, GenericEnum>
|
||||
execute: Execute<unknown>
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
* Used for returning properly formed errors from unique fields
|
||||
@@ -68,8 +121,10 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: Args['idType']
|
||||
initializing: Promise<void>
|
||||
insert: Insert
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
operators: Operators
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
@@ -82,44 +137,45 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
schemaName?: Args['schemaName']
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
db: DrizzleTransaction
|
||||
db: PostgresDB | TransactionPg
|
||||
reject: () => Promise<void>
|
||||
resolve: () => Promise<void>
|
||||
}
|
||||
}
|
||||
tableNameMap: Map<string, string>
|
||||
tables: Record<string, GenericTable | PgTableWithColumns<any>>
|
||||
tables: Record<string, GenericTable>
|
||||
versionsSuffix?: string
|
||||
}
|
||||
} & PostgresDrizzleAdapter
|
||||
|
||||
export type IDType = 'integer' | 'numeric' | 'uuid' | 'varchar'
|
||||
|
||||
export type PostgresAdapterResult = (args: { payload: Payload }) => PostgresAdapter
|
||||
|
||||
export type MigrateUpArgs = { payload: Payload; req?: Partial<PayloadRequest> }
|
||||
export type MigrateDownArgs = { payload: Payload; req?: Partial<PayloadRequest> }
|
||||
|
||||
declare module 'payload' {
|
||||
export interface DatabaseAdapter
|
||||
extends Omit<Args, 'migrationDir' | 'pool'>,
|
||||
BaseDatabaseAdapter {
|
||||
drizzle: DrizzleDB
|
||||
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
|
||||
DrizzleAdapter {
|
||||
enums: Record<string, GenericEnum>
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
* Used for returning properly formed errors from unique fields
|
||||
*/
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
localeSuffix?: string
|
||||
idType: Args['idType']
|
||||
initializing: Promise<void>
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
push: boolean
|
||||
relations: Record<string, GenericRelation>
|
||||
rejectInitializing: () => void
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
db: DrizzleTransaction
|
||||
reject: () => Promise<void>
|
||||
resolve: () => Promise<void>
|
||||
}
|
||||
}
|
||||
tables: Record<string, GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
tableNameMap: Map<string, string>
|
||||
versionsSuffix?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
|
||||
export const createMigrationTable = async (adapter: PostgresAdapter): Promise<void> => {
|
||||
const prependSchema = adapter.schemaName ? `"${adapter.schemaName}".` : ''
|
||||
|
||||
await adapter.drizzle.execute(
|
||||
sql.raw(`CREATE TABLE IF NOT EXISTS ${prependSchema}"payload_migrations" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" varchar,
|
||||
"batch" numeric,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);`),
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { DrizzleDB } from '../types.js'
|
||||
|
||||
export const migrationTableExists = async (db: DrizzleDB): Promise<boolean> => {
|
||||
const queryRes = await db.execute(sql`SELECT to_regclass('public.payload_migrations');`)
|
||||
|
||||
// Returns table name 'payload_migrations' or null
|
||||
const exists = queryRes.rows?.[0]?.to_regclass === 'payload_migrations'
|
||||
return exists
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { numeric, timestamp, varchar } from 'drizzle-orm/pg-core'
|
||||
import { createRequire } from 'module'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
/**
|
||||
* Pushes the development schema to the database using Drizzle.
|
||||
*
|
||||
* @param {PostgresAdapter} db - The PostgresAdapter instance connected to the database.
|
||||
* @returns {Promise<void>} - A promise that resolves once the schema push is complete.
|
||||
*/
|
||||
export const pushDevSchema = async (db: PostgresAdapter) => {
|
||||
const { pushSchema } = require('drizzle-kit/payload')
|
||||
|
||||
// This will prompt if clarifications are needed for Drizzle to push new schema
|
||||
const { apply, hasDataLoss, warnings } = await pushSchema(db.schema, db.drizzle)
|
||||
|
||||
if (warnings.length) {
|
||||
let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n`
|
||||
|
||||
if (hasDataLoss) {
|
||||
message += `DATA LOSS WARNING: Possible data loss detected if schema is pushed.\n\n`
|
||||
}
|
||||
|
||||
message += `Accept warnings and push schema to database?`
|
||||
|
||||
const { confirm: acceptWarnings } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Exit if user does not accept warnings.
|
||||
// Q: Is this the right type of exit for this interaction?
|
||||
if (!acceptWarnings) {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
await apply()
|
||||
|
||||
// Migration table def in order to use query using drizzle
|
||||
const migrationsSchema = db.pgSchema.table('payload_migrations', {
|
||||
name: varchar('name'),
|
||||
batch: numeric('batch'),
|
||||
created_at: timestamp('created_at'),
|
||||
updated_at: timestamp('updated_at'),
|
||||
})
|
||||
|
||||
const devPush = await db.drizzle
|
||||
.select()
|
||||
.from(migrationsSchema)
|
||||
.where(eq(migrationsSchema.batch, '-1'))
|
||||
|
||||
if (!devPush.length) {
|
||||
await db.drizzle.insert(migrationsSchema).values({
|
||||
name: 'dev',
|
||||
batch: '-1',
|
||||
})
|
||||
} else {
|
||||
await db.drizzle
|
||||
.update(migrationsSchema)
|
||||
.set({
|
||||
updated_at: new Date(),
|
||||
})
|
||||
.where(eq(migrationsSchema.batch, '-1'))
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false /* Do not emit outputs. */,
|
||||
// Make sure typescript knows that this module depends on their references
|
||||
"composite": true,
|
||||
/* Do not emit outputs. */
|
||||
"noEmit": false,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */
|
||||
/* Specify an output folder for all emitted files. */
|
||||
"outDir": "./dist",
|
||||
/* Specify the root folder within your source files. */
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
@@ -19,6 +23,19 @@
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }, { "path": "../translations" }]
|
||||
"include": [
|
||||
"src",
|
||||
"src/**/*.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../payload"
|
||||
},
|
||||
{
|
||||
"path": "../translations"
|
||||
},
|
||||
{
|
||||
"path": "../drizzle"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
10
packages/db-sqlite/.eslintignore
Normal file
10
packages/db-sqlite/.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
7
packages/db-sqlite/.eslintrc.cjs
Normal file
7
packages/db-sqlite/.eslintrc.cjs
Normal file
@@ -0,0 +1,7 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
1
packages/db-sqlite/.gitignore
vendored
Normal file
1
packages/db-sqlite/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/migrations
|
||||
10
packages/db-sqlite/.prettierignore
Normal file
10
packages/db-sqlite/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
15
packages/db-sqlite/.swcrc
Normal file
15
packages/db-sqlite/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
}
|
||||
}
|
||||
30
packages/db-sqlite/README.md
Normal file
30
packages/db-sqlite/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Payload Postgres Adapter
|
||||
|
||||
Official SQLite adapter for [Payload](https://payloadcms.com).
|
||||
|
||||
- [Main Repository](https://github.com/payloadcms/payload)
|
||||
- [Payload Docs](https://payloadcms.com/docs)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @payloadcms/db-sqlite
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { sqliteAdapter } from '@payloadcms/db-sqlite'
|
||||
|
||||
export default buildConfig({
|
||||
db: sqliteAdapter({
|
||||
client: {
|
||||
url: process.env.DATABASE_URI,
|
||||
},
|
||||
}),
|
||||
// ...rest of config
|
||||
})
|
||||
```
|
||||
|
||||
More detailed usage can be found in the [Payload Docs](https://payloadcms.com/docs/configuration/overview).
|
||||
38
packages/db-sqlite/bundle.js
Normal file
38
packages/db-sqlite/bundle.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import * as esbuild from 'esbuild'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
import { commonjs } from '@hyrious/esbuild-plugin-commonjs'
|
||||
|
||||
async function build() {
|
||||
const resultServer = await esbuild.build({
|
||||
entryPoints: ['src/index.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
outfile: 'dist/index.js',
|
||||
splitting: false,
|
||||
external: [
|
||||
'*.scss',
|
||||
'*.css',
|
||||
'drizzle-kit',
|
||||
'libsql',
|
||||
'pg',
|
||||
'@payloadcms/translations',
|
||||
'@payloadcms/drizzle',
|
||||
'payload',
|
||||
'payload/*',
|
||||
],
|
||||
minify: true,
|
||||
metafile: true,
|
||||
tsconfig: path.resolve(dirname, './tsconfig.json'),
|
||||
plugins: [commonjs()],
|
||||
sourcemap: true,
|
||||
})
|
||||
console.log('db-sqlite bundled successfully')
|
||||
|
||||
fs.writeFileSync('meta_server.json', JSON.stringify(resultServer.metafile))
|
||||
}
|
||||
await build()
|
||||
85
packages/db-sqlite/package.json
Normal file
85
packages/db-sqlite/package.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.0.0-beta.36",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git",
|
||||
"directory": "packages/db-sqlite"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./src/types.ts",
|
||||
"require": "./src/types.ts",
|
||||
"types": "./src/types.ts"
|
||||
},
|
||||
"./migration-utils": {
|
||||
"import": "./src/exports/migration-utils.ts",
|
||||
"require": "./src/exports/migration-utils.ts",
|
||||
"types": "./src/exports/migration-utils.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/types.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"mock.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepack": "pnpm clean && pnpm turbo build",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@libsql/client": "^0.6.2",
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/pg": "8.10.2",
|
||||
"@types/to-snake-case": "1.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./dist/types.js",
|
||||
"require": "./dist/types.js",
|
||||
"types": "./dist/types.d.ts"
|
||||
},
|
||||
"./migration-utils": {
|
||||
"import": "./dist/exports/migration-utils.js",
|
||||
"require": "./dist/exports/migration-utils.js",
|
||||
"types": "./dist/exports/migration-utils.d.ts"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
55
packages/db-sqlite/src/connect.ts
Normal file
55
packages/db-sqlite/src/connect.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type { Connect } from 'payload'
|
||||
|
||||
import { createClient } from '@libsql/client'
|
||||
import { pushDevSchema } from '@payloadcms/drizzle'
|
||||
import { drizzle } from 'drizzle-orm/libsql'
|
||||
|
||||
import type { SQLiteAdapter } from './types.js'
|
||||
|
||||
export const connect: Connect = async function connect(
|
||||
this: SQLiteAdapter,
|
||||
options = {
|
||||
hotReload: false,
|
||||
},
|
||||
) {
|
||||
const { hotReload } = options
|
||||
|
||||
this.schema = {
|
||||
...this.tables,
|
||||
...this.relations,
|
||||
}
|
||||
|
||||
try {
|
||||
if (!this.client) {
|
||||
this.client = createClient(this.clientConfig)
|
||||
}
|
||||
|
||||
const logger = this.logger || false
|
||||
this.drizzle = drizzle(this.client, { logger, schema: this.schema }) as LibSQLDatabase
|
||||
|
||||
if (!hotReload) {
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
this.payload.logger.info(`---- DROPPING TABLES ----`)
|
||||
await this.dropDatabase({ adapter: this })
|
||||
this.payload.logger.info('---- DROPPED TABLES ----')
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.payload.logger.error(`Error: cannot connect to SQLite. Details: ${err.message}`, err)
|
||||
if (typeof this.rejectInitializing === 'function') this.rejectInitializing()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Only push schema if not in production
|
||||
if (
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
process.env.PAYLOAD_MIGRATING !== 'true' &&
|
||||
this.push !== false
|
||||
) {
|
||||
await pushDevSchema(this as unknown as DrizzleAdapter)
|
||||
}
|
||||
|
||||
if (typeof this.resolveInitializing === 'function') this.resolveInitializing()
|
||||
}
|
||||
33
packages/db-sqlite/src/countDistinct.ts
Normal file
33
packages/db-sqlite/src/countDistinct.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { ChainedMethods } from '@payloadcms/drizzle/types'
|
||||
|
||||
import { chainMethods } from '@payloadcms/drizzle'
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { CountDistinct, SQLiteAdapter } from './types.js'
|
||||
|
||||
export const countDistinct: CountDistinct = async function countDistinct(
|
||||
this: SQLiteAdapter,
|
||||
{ db, joins, tableName, where },
|
||||
) {
|
||||
const chainedMethods: ChainedMethods = []
|
||||
|
||||
joins.forEach(({ condition, table }) => {
|
||||
chainedMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
})
|
||||
|
||||
const countResult = await chainMethods({
|
||||
methods: chainedMethods,
|
||||
query: db
|
||||
.select({
|
||||
count: sql<number>`count
|
||||
(DISTINCT ${this.tables[tableName].id})`,
|
||||
})
|
||||
.from(this.tables[tableName])
|
||||
.where(where),
|
||||
})
|
||||
|
||||
return Number(countResult[0].count)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export const convertPathToJSONTraversal = (incomingSegments: string[]): string => {
|
||||
const segments = [...incomingSegments]
|
||||
segments.shift()
|
||||
|
||||
return segments.reduce((res, segment) => {
|
||||
const formattedSegment = Number.isNaN(parseInt(segment)) ? `'${segment}'` : segment
|
||||
return `${res}->>${formattedSegment}`
|
||||
}, '')
|
||||
}
|
||||
86
packages/db-sqlite/src/createJSONQuery/index.ts
Normal file
86
packages/db-sqlite/src/createJSONQuery/index.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { CreateJSONQueryArgs } from '@payloadcms/drizzle/types'
|
||||
|
||||
type FromArrayArgs = {
|
||||
isRoot?: true
|
||||
operator: string
|
||||
pathSegments: string[]
|
||||
table: string
|
||||
treatAsArray?: string[]
|
||||
value: boolean | number | string
|
||||
}
|
||||
|
||||
const fromArray = ({
|
||||
isRoot,
|
||||
operator,
|
||||
pathSegments,
|
||||
table,
|
||||
treatAsArray,
|
||||
value,
|
||||
}: FromArrayArgs) => {
|
||||
const newPathSegments = pathSegments.slice(1)
|
||||
const alias = `${pathSegments[isRoot ? 0 : 1]}_alias_${newPathSegments.length}`
|
||||
|
||||
return `EXISTS (
|
||||
SELECT 1
|
||||
FROM json_each(${table}.${pathSegments[0]}) AS ${alias}
|
||||
WHERE ${createJSONQuery({
|
||||
operator,
|
||||
pathSegments: newPathSegments,
|
||||
table: alias,
|
||||
treatAsArray,
|
||||
value,
|
||||
})}
|
||||
)`
|
||||
}
|
||||
|
||||
type CreateConstraintArgs = {
|
||||
alias?: string
|
||||
operator: string
|
||||
pathSegments: string[]
|
||||
treatAsArray?: string[]
|
||||
value: boolean | number | string
|
||||
}
|
||||
|
||||
const createConstraint = ({
|
||||
alias,
|
||||
operator,
|
||||
pathSegments,
|
||||
value,
|
||||
}: CreateConstraintArgs): string => {
|
||||
const newAlias = `${pathSegments[0]}_alias_${pathSegments.length - 1}`
|
||||
let formattedValue = value
|
||||
let formattedOperator = operator
|
||||
|
||||
if (['contains', 'like'].includes(operator)) {
|
||||
formattedOperator = 'like'
|
||||
formattedValue = `%${value}%`
|
||||
} else if (operator === 'equals') {
|
||||
formattedOperator = '='
|
||||
}
|
||||
|
||||
return `EXISTS (
|
||||
SELECT 1
|
||||
FROM json_each(${alias}.value -> '${pathSegments[0]}') AS ${newAlias}
|
||||
WHERE ${newAlias}.value ->> '${pathSegments[1]}' ${formattedOperator} '${formattedValue}'
|
||||
)`
|
||||
}
|
||||
|
||||
export const createJSONQuery = ({
|
||||
operator,
|
||||
pathSegments,
|
||||
table,
|
||||
treatAsArray,
|
||||
value,
|
||||
}: CreateJSONQueryArgs): string => {
|
||||
if (treatAsArray.includes(pathSegments[1])) {
|
||||
return fromArray({
|
||||
operator,
|
||||
pathSegments,
|
||||
table,
|
||||
treatAsArray,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
return createConstraint({ alias: table, operator, pathSegments, treatAsArray, value })
|
||||
}
|
||||
116
packages/db-sqlite/src/createMigration.ts
Normal file
116
packages/db-sqlite/src/createMigration.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { CreateMigration } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
import path from 'path'
|
||||
import { getPredefinedMigration } from 'payload'
|
||||
import prompts from 'prompts'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { SQLiteAdapter } from './types.js'
|
||||
|
||||
import { defaultDrizzleSnapshot } from './defaultSnapshot.js'
|
||||
import { getMigrationTemplate } from './getMigrationTemplate.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
export const createMigration: CreateMigration = async function createMigration(
|
||||
this: SQLiteAdapter,
|
||||
{ file, forceAcceptWarning, migrationName, payload },
|
||||
) {
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
const dir = payload.db.migrationDir
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir)
|
||||
}
|
||||
const { generateSQLiteDrizzleJson, generateSQLiteMigration } = require('drizzle-kit/payload')
|
||||
const drizzleJsonAfter = await generateSQLiteDrizzleJson(this.schema)
|
||||
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
|
||||
const formattedDate = yyymmdd.replace(/\D/g, '')
|
||||
const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '')
|
||||
let imports: string = ''
|
||||
let downSQL: string
|
||||
let upSQL: string
|
||||
;({ downSQL, imports, upSQL } = await getPredefinedMigration({
|
||||
dirname,
|
||||
file,
|
||||
migrationName,
|
||||
payload,
|
||||
}))
|
||||
|
||||
const timestamp = `${formattedDate}_${formattedTime}`
|
||||
|
||||
const name = migrationName || file?.split('/').slice(2).join('/')
|
||||
const fileName = `${timestamp}${name ? `_${name.replace(/\W/g, '_')}` : ''}`
|
||||
|
||||
const filePath = `${dir}/${fileName}`
|
||||
|
||||
let drizzleJsonBefore = defaultDrizzleSnapshot as any
|
||||
|
||||
if (!upSQL) {
|
||||
// Get latest migration snapshot
|
||||
const latestSnapshot = fs
|
||||
.readdirSync(dir)
|
||||
.filter((file) => file.endsWith('.json'))
|
||||
.sort()
|
||||
.reverse()?.[0]
|
||||
|
||||
if (latestSnapshot) {
|
||||
drizzleJsonBefore = JSON.parse(
|
||||
fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'),
|
||||
) as DrizzleSnapshotJSON
|
||||
}
|
||||
|
||||
const sqlStatementsUp = await generateSQLiteMigration(drizzleJsonBefore, drizzleJsonAfter)
|
||||
const sqlStatementsDown = await generateSQLiteMigration(drizzleJsonAfter, drizzleJsonBefore)
|
||||
// need to create tables as separate statements
|
||||
const sqlExecute = 'await db.run(sql`'
|
||||
|
||||
if (sqlStatementsUp?.length) {
|
||||
upSQL = sqlStatementsUp
|
||||
.map((statement) => `${sqlExecute}${statement?.replaceAll('`', '\\`')}\`)`)
|
||||
.join('\n')
|
||||
}
|
||||
if (sqlStatementsDown?.length) {
|
||||
downSQL = sqlStatementsDown
|
||||
.map((statement) => `${sqlExecute}${statement?.replaceAll('`', '\\`')}\`)`)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
if (!upSQL?.length && !downSQL?.length && !forceAcceptWarning) {
|
||||
const { confirm: shouldCreateBlankMigration } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message: 'No schema changes detected. Would you like to create a blank migration file?',
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if (!shouldCreateBlankMigration) {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// write schema
|
||||
fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2))
|
||||
}
|
||||
|
||||
// write migration
|
||||
fs.writeFileSync(
|
||||
`${filePath}.ts`,
|
||||
getMigrationTemplate({
|
||||
downSQL: downSQL || ` // Migration code`,
|
||||
imports,
|
||||
upSQL: upSQL || ` // Migration code`,
|
||||
}),
|
||||
)
|
||||
payload.logger.info({ msg: `Migration created at ${filePath}.ts` })
|
||||
}
|
||||
14
packages/db-sqlite/src/defaultSnapshot.ts
Normal file
14
packages/db-sqlite/src/defaultSnapshot.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { DrizzleSQLiteSnapshotJSON } from 'drizzle-kit/payload'
|
||||
|
||||
export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
_meta: {
|
||||
columns: {},
|
||||
tables: {},
|
||||
},
|
||||
dialect: 'sqlite',
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
tables: {},
|
||||
version: '3',
|
||||
}
|
||||
6
packages/db-sqlite/src/deleteWhere.ts
Normal file
6
packages/db-sqlite/src/deleteWhere.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { DeleteWhere } from './types.js'
|
||||
|
||||
export const deleteWhere: DeleteWhere = async function deleteWhere({ db, tableName, where }) {
|
||||
const table = this.tables[tableName]
|
||||
await db.delete(table).where(where)
|
||||
}
|
||||
21
packages/db-sqlite/src/dropDatabase.ts
Normal file
21
packages/db-sqlite/src/dropDatabase.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { DropDatabase } from './types.js'
|
||||
|
||||
const getTables = (adapter) => {
|
||||
return adapter.client.execute(`SELECT name
|
||||
FROM sqlite_master
|
||||
WHERE type = 'table'
|
||||
AND name NOT LIKE 'sqlite_%';`)
|
||||
}
|
||||
|
||||
const dropTables = (adapter, rows) => {
|
||||
const multi = `
|
||||
PRAGMA foreign_keys = OFF;\n
|
||||
${rows.map(({ name }) => `DROP TABLE IF EXISTS ${name}`).join(';\n ')};\n
|
||||
PRAGMA foreign_keys = ON;`
|
||||
return adapter.client.executeMultiple(multi)
|
||||
}
|
||||
|
||||
export const dropDatabase: DropDatabase = async function dropDatabase({ adapter }) {
|
||||
const result = await getTables(adapter)
|
||||
await dropTables(adapter, result.rows)
|
||||
}
|
||||
15
packages/db-sqlite/src/execute.ts
Normal file
15
packages/db-sqlite/src/execute.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { Execute } from './types.js'
|
||||
|
||||
export const execute: Execute<any> = function execute({ db, drizzle, raw, sql: statement }) {
|
||||
const executeFrom = db ?? drizzle
|
||||
|
||||
if (raw) {
|
||||
const result = executeFrom.run(sql.raw(raw))
|
||||
return result
|
||||
} else {
|
||||
const result = executeFrom.run(statement)
|
||||
return result
|
||||
}
|
||||
}
|
||||
16
packages/db-sqlite/src/getMigrationTemplate.ts
Normal file
16
packages/db-sqlite/src/getMigrationTemplate.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { MigrationTemplateArgs } from 'payload'
|
||||
|
||||
export const getMigrationTemplate = ({
|
||||
downSQL,
|
||||
imports,
|
||||
upSQL,
|
||||
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-sqlite'
|
||||
${imports ? `${imports}\n` : ''}
|
||||
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||
${upSQL}
|
||||
}
|
||||
|
||||
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
|
||||
${downSQL}
|
||||
}
|
||||
`
|
||||
159
packages/db-sqlite/src/index.ts
Normal file
159
packages/db-sqlite/src/index.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import type { Operators } from '@payloadcms/drizzle'
|
||||
import type { DatabaseAdapterObj, Payload } from 'payload'
|
||||
|
||||
import {
|
||||
beginTransaction,
|
||||
commitTransaction,
|
||||
count,
|
||||
create,
|
||||
createGlobal,
|
||||
createGlobalVersion,
|
||||
createVersion,
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
destroy,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
findMigrationDir,
|
||||
findOne,
|
||||
findVersions,
|
||||
migrate,
|
||||
migrateDown,
|
||||
migrateFresh,
|
||||
migrateRefresh,
|
||||
migrateReset,
|
||||
migrateStatus,
|
||||
operatorMap,
|
||||
queryDrafts,
|
||||
rollbackTransaction,
|
||||
updateGlobal,
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { like } from 'drizzle-orm'
|
||||
import { createDatabaseAdapter } from 'payload'
|
||||
|
||||
import type { Args, SQLiteAdapter } from './types.js'
|
||||
|
||||
import { connect } from './connect.js'
|
||||
import { countDistinct } from './countDistinct.js'
|
||||
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal.js'
|
||||
import { createJSONQuery } from './createJSONQuery/index.js'
|
||||
import { createMigration } from './createMigration.js'
|
||||
import { defaultDrizzleSnapshot } from './defaultSnapshot.js'
|
||||
import { deleteWhere } from './deleteWhere.js'
|
||||
import { dropDatabase } from './dropDatabase.js'
|
||||
import { execute } from './execute.js'
|
||||
import { getMigrationTemplate } from './getMigrationTemplate.js'
|
||||
import { init } from './init.js'
|
||||
import { insert } from './insert.js'
|
||||
import { requireDrizzleKit } from './requireDrizzleKit.js'
|
||||
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types.js'
|
||||
|
||||
export { sql } from 'drizzle-orm'
|
||||
|
||||
export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
const postgresIDType = args.idType || 'serial'
|
||||
const payloadIDType = postgresIDType === 'serial' ? 'number' : 'text'
|
||||
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
const migrationDir = findMigrationDir(args.migrationDir)
|
||||
let resolveInitializing
|
||||
let rejectInitializing
|
||||
|
||||
const initializing = new Promise<void>((res, rej) => {
|
||||
resolveInitializing = res
|
||||
rejectInitializing = rej
|
||||
})
|
||||
|
||||
// sqlite's like operator is case-insensitive, so we overwrite the DrizzleAdapter operators to not use ilike
|
||||
const operators = {
|
||||
...operatorMap,
|
||||
contains: like,
|
||||
like,
|
||||
} as unknown as Operators
|
||||
|
||||
return createDatabaseAdapter<SQLiteAdapter>({
|
||||
name: 'sqlite',
|
||||
client: undefined,
|
||||
clientConfig: args.client,
|
||||
defaultDrizzleSnapshot,
|
||||
drizzle: undefined,
|
||||
features: {
|
||||
json: true,
|
||||
},
|
||||
fieldConstraints: {},
|
||||
getMigrationTemplate,
|
||||
idType: postgresIDType,
|
||||
initializing,
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
operators,
|
||||
push: args.push,
|
||||
relations: {},
|
||||
relationshipsSuffix: args.relationshipsSuffix || '_rels',
|
||||
schema: {},
|
||||
schemaName: args.schemaName,
|
||||
sessions: {},
|
||||
tableNameMap: new Map<string, string>(),
|
||||
tables: {},
|
||||
transactionOptions: args.transactionOptions || undefined,
|
||||
versionsSuffix: args.versionsSuffix || '_v',
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction: args.transactionOptions === false ? undefined : beginTransaction,
|
||||
commitTransaction,
|
||||
connect,
|
||||
convertPathToJSONTraversal,
|
||||
count,
|
||||
countDistinct,
|
||||
create,
|
||||
createGlobal,
|
||||
createGlobalVersion,
|
||||
createJSONQuery,
|
||||
createMigration,
|
||||
createVersion,
|
||||
defaultIDType: payloadIDType,
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
deleteWhere,
|
||||
destroy,
|
||||
dropDatabase,
|
||||
execute,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
findOne,
|
||||
findVersions,
|
||||
init,
|
||||
insert,
|
||||
migrate,
|
||||
migrateDown,
|
||||
migrateFresh,
|
||||
migrateRefresh,
|
||||
migrateReset,
|
||||
migrateStatus,
|
||||
migrationDir,
|
||||
payload,
|
||||
queryDrafts,
|
||||
rejectInitializing,
|
||||
requireDrizzleKit,
|
||||
resolveInitializing,
|
||||
rollbackTransaction,
|
||||
updateGlobal,
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
defaultIDType: payloadIDType,
|
||||
init: adapter,
|
||||
}
|
||||
}
|
||||
108
packages/db-sqlite/src/init.ts
Normal file
108
packages/db-sqlite/src/init.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { Init, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { createTableName } from '@payloadcms/drizzle'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { SQLiteAdapter } from './types.js'
|
||||
|
||||
import { buildTable } from './schema/build.js'
|
||||
|
||||
export const init: Init = function init(this: SQLiteAdapter) {
|
||||
let locales: [string, ...string[]] | undefined
|
||||
if (this.payload.config.localization) {
|
||||
locales = this.payload.config.localization.locales.map(({ code }) => code) as [
|
||||
string,
|
||||
...string[],
|
||||
]
|
||||
}
|
||||
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
createTableName({
|
||||
adapter: this as unknown as DrizzleAdapter,
|
||||
config: collection,
|
||||
})
|
||||
|
||||
if (collection.versions) {
|
||||
createTableName({
|
||||
adapter: this as unknown as DrizzleAdapter,
|
||||
config: collection,
|
||||
versions: true,
|
||||
versionsCustomName: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
disableNotNull: !!collection?.versions?.drafts,
|
||||
disableUnique: false,
|
||||
fields: collection.fields,
|
||||
locales,
|
||||
tableName,
|
||||
timestamps: collection.timestamps,
|
||||
versions: false,
|
||||
})
|
||||
|
||||
if (collection.versions) {
|
||||
const versionsTableName = this.tableNameMap.get(
|
||||
`_${toSnakeCase(collection.slug)}${this.versionsSuffix}`,
|
||||
)
|
||||
const versionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
disableNotNull: !!collection.versions?.drafts,
|
||||
disableUnique: true,
|
||||
fields: versionFields,
|
||||
locales,
|
||||
tableName: versionsTableName,
|
||||
timestamps: true,
|
||||
versions: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
const tableName = createTableName({
|
||||
adapter: this as unknown as DrizzleAdapter,
|
||||
config: global,
|
||||
})
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
disableNotNull: !!global?.versions?.drafts,
|
||||
disableUnique: false,
|
||||
fields: global.fields,
|
||||
locales,
|
||||
tableName,
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
})
|
||||
|
||||
if (global.versions) {
|
||||
const versionsTableName = createTableName({
|
||||
adapter: this as unknown as DrizzleAdapter,
|
||||
config: global,
|
||||
versions: true,
|
||||
versionsCustomName: true,
|
||||
})
|
||||
const versionFields = buildVersionGlobalFields(global)
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
disableNotNull: !!global.versions?.drafts,
|
||||
disableUnique: true,
|
||||
fields: versionFields,
|
||||
locales,
|
||||
tableName: versionsTableName,
|
||||
timestamps: true,
|
||||
versions: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
19
packages/db-sqlite/src/insert.ts
Normal file
19
packages/db-sqlite/src/insert.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { Insert } from './types.js'
|
||||
|
||||
export const insert: Insert = async function insert({
|
||||
db,
|
||||
onConflictDoUpdate,
|
||||
tableName,
|
||||
values,
|
||||
}): Promise<Record<string, unknown>[]> {
|
||||
const table = this.tables[tableName]
|
||||
let result
|
||||
|
||||
if (onConflictDoUpdate) {
|
||||
result = db.insert(table).values(values).onConflictDoUpdate(onConflictDoUpdate).returning()
|
||||
} else {
|
||||
result = db.insert(table).values(values).returning()
|
||||
}
|
||||
result = await result
|
||||
return result
|
||||
}
|
||||
15
packages/db-sqlite/src/requireDrizzleKit.ts
Normal file
15
packages/db-sqlite/src/requireDrizzleKit.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { RequireDrizzleKit } from '@payloadcms/drizzle/types'
|
||||
|
||||
import { createRequire } from 'module'
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
/**
|
||||
* Dynamically requires the `drizzle-kit` package to access the `generateSQLiteDrizzleJson` and `pushSQLiteSchema` functions and exports them generically to call them from @payloadcms/drizzle.
|
||||
*/
|
||||
export const requireDrizzleKit: RequireDrizzleKit = () => {
|
||||
const {
|
||||
generateSQLiteDrizzleJson: generateDrizzleJson,
|
||||
pushSQLiteSchema: pushSchema,
|
||||
} = require('drizzle-kit/payload')
|
||||
return { generateDrizzleJson, pushSchema }
|
||||
}
|
||||
490
packages/db-sqlite/src/schema/build.ts
Normal file
490
packages/db-sqlite/src/schema/build.ts
Normal file
@@ -0,0 +1,490 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { ColumnDataType, Relation } from 'drizzle-orm'
|
||||
import type {
|
||||
ForeignKeyBuilder,
|
||||
IndexBuilder,
|
||||
SQLiteColumn,
|
||||
SQLiteColumnBuilder,
|
||||
SQLiteTableWithColumns,
|
||||
UniqueConstraintBuilder,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import type { Field } from 'payload'
|
||||
|
||||
import { createTableName } from '@payloadcms/drizzle'
|
||||
import { relations, sql } from 'drizzle-orm'
|
||||
import {
|
||||
foreignKey,
|
||||
index,
|
||||
integer,
|
||||
numeric,
|
||||
sqliteTable,
|
||||
text,
|
||||
unique,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { GenericColumns, GenericTable, IDType, SQLiteAdapter } from '../types.js'
|
||||
|
||||
import { getIDColumn } from './getIDColumn.js'
|
||||
import { setColumnID } from './setColumnID.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
export type BaseExtraConfig = Record<
|
||||
string,
|
||||
(cols: {
|
||||
[x: string]: SQLiteColumn<{
|
||||
baseColumn: never
|
||||
columnType: string
|
||||
data: unknown
|
||||
dataType: ColumnDataType
|
||||
driverParam: unknown
|
||||
enumValues: string[]
|
||||
hasDefault: false
|
||||
name: string
|
||||
notNull: false
|
||||
tableName: string
|
||||
}>
|
||||
}) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder
|
||||
>
|
||||
|
||||
export type RelationMap = Map<string, { localized: boolean; target: string; type: 'many' | 'one' }>
|
||||
|
||||
type Args = {
|
||||
adapter: SQLiteAdapter
|
||||
baseColumns?: Record<string, SQLiteColumnBuilder>
|
||||
baseExtraConfig?: BaseExtraConfig
|
||||
buildNumbers?: boolean
|
||||
buildRelationships?: boolean
|
||||
disableNotNull: boolean
|
||||
disableUnique: boolean
|
||||
fields: Field[]
|
||||
locales?: [string, ...string[]]
|
||||
rootRelationsToBuild?: RelationMap
|
||||
rootRelationships?: Set<string>
|
||||
rootTableIDColType?: IDType
|
||||
rootTableName?: string
|
||||
tableName: string
|
||||
timestamps?: boolean
|
||||
versions: boolean
|
||||
}
|
||||
|
||||
type Result = {
|
||||
hasManyNumberField: 'index' | boolean
|
||||
hasManyTextField: 'index' | boolean
|
||||
relationsToBuild: RelationMap
|
||||
}
|
||||
|
||||
export const buildTable = ({
|
||||
adapter,
|
||||
baseColumns = {},
|
||||
baseExtraConfig = {},
|
||||
disableNotNull,
|
||||
disableUnique = false,
|
||||
fields,
|
||||
locales,
|
||||
rootRelationsToBuild,
|
||||
rootRelationships,
|
||||
rootTableIDColType,
|
||||
rootTableName: incomingRootTableName,
|
||||
tableName,
|
||||
timestamps,
|
||||
versions,
|
||||
}: Args): Result => {
|
||||
const isRoot = !incomingRootTableName
|
||||
const rootTableName = incomingRootTableName || tableName
|
||||
const columns: Record<string, SQLiteColumnBuilder> = baseColumns
|
||||
const indexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
|
||||
|
||||
const localesColumns: Record<string, SQLiteColumnBuilder> = {}
|
||||
const localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
|
||||
let localesTable: GenericTable | SQLiteTableWithColumns<any>
|
||||
let textsTable: GenericTable | SQLiteTableWithColumns<any>
|
||||
let numbersTable: GenericTable | SQLiteTableWithColumns<any>
|
||||
|
||||
// Relationships to the base collection
|
||||
const relationships: Set<string> = rootRelationships || new Set()
|
||||
|
||||
let relationshipsTable: GenericTable | SQLiteTableWithColumns<any>
|
||||
|
||||
// Drizzle relations
|
||||
const relationsToBuild: RelationMap = new Map()
|
||||
|
||||
const idColType: IDType = setColumnID({ columns, fields })
|
||||
|
||||
const {
|
||||
hasLocalizedField,
|
||||
hasLocalizedManyNumberField,
|
||||
hasLocalizedManyTextField,
|
||||
hasLocalizedRelationshipField,
|
||||
hasManyNumberField,
|
||||
hasManyTextField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
columns,
|
||||
disableNotNull,
|
||||
disableUnique,
|
||||
fields,
|
||||
indexes,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName: tableName,
|
||||
parentTableName: tableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild: rootRelationsToBuild || relationsToBuild,
|
||||
rootTableIDColType: rootTableIDColType || idColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
// split the relationsToBuild by localized and non-localized
|
||||
const localizedRelations = new Map()
|
||||
const nonLocalizedRelations = new Map()
|
||||
|
||||
relationsToBuild.forEach(({ type, localized, target }, key) => {
|
||||
const map = localized ? localizedRelations : nonLocalizedRelations
|
||||
map.set(key, { type, target })
|
||||
})
|
||||
|
||||
if (timestamps) {
|
||||
columns.createdAt = text('created_at')
|
||||
.default(sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`)
|
||||
.notNull()
|
||||
columns.updatedAt = text('updated_at')
|
||||
.default(sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`)
|
||||
.notNull()
|
||||
}
|
||||
|
||||
const table = sqliteTable(tableName, columns, (cols) => {
|
||||
const extraConfig = Object.entries(baseExtraConfig).reduce((config, [key, func]) => {
|
||||
config[key] = func(cols)
|
||||
return config
|
||||
}, {})
|
||||
|
||||
const result = Object.entries(indexes).reduce((acc, [colName, func]) => {
|
||||
acc[colName] = func(cols)
|
||||
return acc
|
||||
}, extraConfig)
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
adapter.tables[tableName] = table
|
||||
|
||||
if (hasLocalizedField || localizedRelations.size) {
|
||||
const localeTableName = `${tableName}${adapter.localesSuffix}`
|
||||
localesColumns.id = integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true })
|
||||
localesColumns._locale = text('_locale', { enum: locales }).notNull()
|
||||
localesColumns._parentID = getIDColumn({
|
||||
name: '_parent_id',
|
||||
type: idColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
})
|
||||
|
||||
localesTable = sqliteTable(localeTableName, localesColumns, (cols) => {
|
||||
return Object.entries(localesIndexes).reduce(
|
||||
(acc, [colName, func]) => {
|
||||
acc[colName] = func(cols)
|
||||
return acc
|
||||
},
|
||||
{
|
||||
_localeParent: unique(`${localeTableName}_locale_parent_id_unique`).on(
|
||||
cols._locale,
|
||||
cols._parentID,
|
||||
),
|
||||
_parentIdFk: foreignKey({
|
||||
name: `${localeTableName}_parent_id_fk`,
|
||||
columns: [cols._parentID],
|
||||
foreignColumns: [table.id],
|
||||
}).onDelete('cascade'),
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
adapter.tables[localeTableName] = localesTable
|
||||
|
||||
adapter.relations[`relations_${localeTableName}`] = relations(localesTable, ({ many, one }) => {
|
||||
const result: Record<string, Relation<string>> = {}
|
||||
|
||||
result._parentID = one(table, {
|
||||
fields: [localesTable._parentID],
|
||||
references: [table.id],
|
||||
// name the relationship by what the many() relationName is
|
||||
relationName: '_locales',
|
||||
})
|
||||
|
||||
localizedRelations.forEach(({ type, target }, key) => {
|
||||
if (type === 'one') {
|
||||
result[key] = one(adapter.tables[target], {
|
||||
fields: [localesTable[key]],
|
||||
references: [adapter.tables[target].id],
|
||||
relationName: key,
|
||||
})
|
||||
}
|
||||
if (type === 'many') {
|
||||
result[key] = many(adapter.tables[target], {
|
||||
relationName: key,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
if (hasManyTextField) {
|
||||
const textsTableName = `${rootTableName}_texts`
|
||||
const columns: Record<string, SQLiteColumnBuilder> = {
|
||||
id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }),
|
||||
order: integer('order').notNull(),
|
||||
parent: getIDColumn({
|
||||
name: 'parent_id',
|
||||
type: idColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
}),
|
||||
path: text('path').notNull(),
|
||||
text: text('text'),
|
||||
}
|
||||
|
||||
if (hasLocalizedManyTextField) {
|
||||
columns.locale = text('locale', { enum: locales })
|
||||
}
|
||||
|
||||
textsTable = sqliteTable(textsTableName, columns, (cols) => {
|
||||
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
|
||||
orderParentIdx: index(`${textsTableName}_order_parent_idx`).on(cols.order, cols.parent),
|
||||
parentFk: foreignKey({
|
||||
name: `${textsTableName}_parent_fk`,
|
||||
columns: [cols.parent],
|
||||
foreignColumns: [table.id],
|
||||
}).onDelete('cascade'),
|
||||
}
|
||||
|
||||
if (hasManyTextField === 'index') {
|
||||
config.text_idx = index(`${textsTableName}_text_idx`).on(cols.text)
|
||||
}
|
||||
|
||||
if (hasLocalizedManyTextField) {
|
||||
config.localeParent = index(`${textsTableName}_locale_parent`).on(
|
||||
cols.locale,
|
||||
cols.parent,
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
|
||||
adapter.tables[textsTableName] = textsTable
|
||||
|
||||
adapter.relations[`relations_${textsTableName}`] = relations(textsTable, ({ one }) => ({
|
||||
parent: one(table, {
|
||||
fields: [textsTable.parent],
|
||||
references: [table.id],
|
||||
relationName: '_texts',
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
if (hasManyNumberField) {
|
||||
const numbersTableName = `${rootTableName}_numbers`
|
||||
const columns: Record<string, SQLiteColumnBuilder> = {
|
||||
id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }),
|
||||
number: numeric('number'),
|
||||
order: integer('order').notNull(),
|
||||
parent: getIDColumn({
|
||||
name: 'parent_id',
|
||||
type: idColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
}),
|
||||
path: text('path').notNull(),
|
||||
}
|
||||
|
||||
if (hasLocalizedManyNumberField) {
|
||||
columns.locale = text('locale', { enum: locales })
|
||||
}
|
||||
|
||||
numbersTable = sqliteTable(numbersTableName, columns, (cols) => {
|
||||
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
|
||||
orderParentIdx: index(`${numbersTableName}_order_parent_idx`).on(cols.order, cols.parent),
|
||||
parentFk: foreignKey({
|
||||
name: `${numbersTableName}_parent_fk`,
|
||||
columns: [cols.parent],
|
||||
foreignColumns: [table.id],
|
||||
}).onDelete('cascade'),
|
||||
}
|
||||
|
||||
if (hasManyNumberField === 'index') {
|
||||
config.numberIdx = index(`${numbersTableName}_number_idx`).on(cols.number)
|
||||
}
|
||||
|
||||
if (hasLocalizedManyNumberField) {
|
||||
config.localeParent = index(`${numbersTableName}_locale_parent`).on(
|
||||
cols.locale,
|
||||
cols.parent,
|
||||
)
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
|
||||
adapter.tables[numbersTableName] = numbersTable
|
||||
|
||||
adapter.relations[`relations_${numbersTableName}`] = relations(numbersTable, ({ one }) => ({
|
||||
parent: one(table, {
|
||||
fields: [numbersTable.parent],
|
||||
references: [table.id],
|
||||
relationName: '_numbers',
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
if (relationships.size) {
|
||||
const relationshipColumns: Record<string, SQLiteColumnBuilder> = {
|
||||
id: integer('id', { mode: 'number' }).primaryKey({ autoIncrement: true }),
|
||||
order: integer('order'),
|
||||
parent: getIDColumn({
|
||||
name: 'parent_id',
|
||||
type: idColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
}),
|
||||
path: text('path').notNull(),
|
||||
}
|
||||
|
||||
if (hasLocalizedRelationshipField) {
|
||||
relationshipColumns.locale = text('locale', { enum: locales })
|
||||
}
|
||||
|
||||
const relationExtraConfig: BaseExtraConfig = {}
|
||||
const relationshipsTableName = `${tableName}${adapter.relationshipsSuffix}`
|
||||
|
||||
relationships.forEach((relationTo) => {
|
||||
const relationshipConfig = adapter.payload.collections[relationTo].config
|
||||
const formattedRelationTo = createTableName({
|
||||
adapter,
|
||||
config: relationshipConfig,
|
||||
})
|
||||
let colType: IDType = 'integer'
|
||||
const relatedCollectionCustomIDType =
|
||||
adapter.payload.collections[relationshipConfig.slug]?.customIDType
|
||||
|
||||
if (relatedCollectionCustomIDType === 'number') colType = 'numeric'
|
||||
if (relatedCollectionCustomIDType === 'text') colType = 'text'
|
||||
|
||||
relationshipColumns[`${relationTo}ID`] = getIDColumn({
|
||||
name: `${formattedRelationTo}_id`,
|
||||
type: colType,
|
||||
primaryKey: false,
|
||||
})
|
||||
|
||||
relationExtraConfig[`${relationTo}IdFk`] = (cols) =>
|
||||
foreignKey({
|
||||
name: `${relationshipsTableName}_${toSnakeCase(relationTo)}_fk`,
|
||||
columns: [cols[`${relationTo}ID`]],
|
||||
foreignColumns: [adapter.tables[formattedRelationTo].id],
|
||||
}).onDelete('cascade')
|
||||
})
|
||||
|
||||
relationshipsTable = sqliteTable(relationshipsTableName, relationshipColumns, (cols) => {
|
||||
const result: Record<string, ForeignKeyBuilder | IndexBuilder> = Object.entries(
|
||||
relationExtraConfig,
|
||||
).reduce(
|
||||
(config, [key, func]) => {
|
||||
config[key] = func(cols)
|
||||
return config
|
||||
},
|
||||
{
|
||||
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
|
||||
parentFk: foreignKey({
|
||||
name: `${relationshipsTableName}_parent_fk`,
|
||||
columns: [cols.parent],
|
||||
foreignColumns: [table.id],
|
||||
}).onDelete('cascade'),
|
||||
parentIdx: index(`${relationshipsTableName}_parent_idx`).on(cols.parent),
|
||||
pathIdx: index(`${relationshipsTableName}_path_idx`).on(cols.path),
|
||||
},
|
||||
)
|
||||
|
||||
if (hasLocalizedRelationshipField) {
|
||||
result.localeIdx = index(`${relationshipsTableName}_locale_idx`).on(cols.locale)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
adapter.tables[relationshipsTableName] = relationshipsTable
|
||||
|
||||
adapter.relations[`relations_${relationshipsTableName}`] = relations(
|
||||
relationshipsTable,
|
||||
({ one }) => {
|
||||
const result: Record<string, Relation<string>> = {
|
||||
parent: one(table, {
|
||||
fields: [relationshipsTable.parent],
|
||||
references: [table.id],
|
||||
relationName: '_rels',
|
||||
}),
|
||||
}
|
||||
|
||||
relationships.forEach((relationTo) => {
|
||||
const relatedTableName = createTableName({
|
||||
adapter,
|
||||
config: adapter.payload.collections[relationTo].config,
|
||||
})
|
||||
const idColumnName = `${relationTo}ID`
|
||||
result[idColumnName] = one(adapter.tables[relatedTableName], {
|
||||
fields: [relationshipsTable[idColumnName]],
|
||||
references: [adapter.tables[relatedTableName].id],
|
||||
relationName: relationTo,
|
||||
})
|
||||
})
|
||||
|
||||
return result
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
adapter.relations[`relations_${tableName}`] = relations(table, ({ many, one }) => {
|
||||
const result: Record<string, Relation<string>> = {}
|
||||
|
||||
nonLocalizedRelations.forEach(({ type, target }, key) => {
|
||||
if (type === 'one') {
|
||||
result[key] = one(adapter.tables[target], {
|
||||
fields: [table[key]],
|
||||
references: [adapter.tables[target].id],
|
||||
relationName: key,
|
||||
})
|
||||
}
|
||||
if (type === 'many') {
|
||||
result[key] = many(adapter.tables[target], { relationName: key })
|
||||
}
|
||||
})
|
||||
|
||||
if (hasLocalizedField) {
|
||||
result._locales = many(localesTable, { relationName: '_locales' })
|
||||
}
|
||||
|
||||
if (hasManyTextField) {
|
||||
result._texts = many(textsTable, { relationName: '_texts' })
|
||||
}
|
||||
|
||||
if (hasManyNumberField) {
|
||||
result._numbers = many(numbersTable, { relationName: '_numbers' })
|
||||
}
|
||||
|
||||
if (relationships.size && relationshipsTable) {
|
||||
result._rels = many(relationshipsTable, {
|
||||
relationName: '_rels',
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
return { hasManyNumberField, hasManyTextField, relationsToBuild }
|
||||
}
|
||||
28
packages/db-sqlite/src/schema/createIndex.ts
Normal file
28
packages/db-sqlite/src/schema/createIndex.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { index, uniqueIndex } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
import type { GenericColumn } from '../types.js'
|
||||
|
||||
type CreateIndexArgs = {
|
||||
columnName: string
|
||||
name: string | string[]
|
||||
tableName: string
|
||||
unique?: boolean
|
||||
}
|
||||
|
||||
export const createIndex = ({ name, columnName, tableName, unique }: CreateIndexArgs) => {
|
||||
return (table: { [x: string]: GenericColumn }) => {
|
||||
let columns
|
||||
if (Array.isArray(name)) {
|
||||
columns = name
|
||||
.map((columnName) => table[columnName])
|
||||
// exclude fields were included in compound indexes but do not exist on the table
|
||||
.filter((col) => typeof col !== 'undefined')
|
||||
} else {
|
||||
columns = [table[name]]
|
||||
}
|
||||
if (unique)
|
||||
return uniqueIndex(`${tableName}_${columnName}_idx`).on(columns[0], ...columns.slice(1))
|
||||
return index(`${tableName}_${columnName}_idx`).on(columns[0], ...columns.slice(1))
|
||||
}
|
||||
}
|
||||
38
packages/db-sqlite/src/schema/getIDColumn.ts
Normal file
38
packages/db-sqlite/src/schema/getIDColumn.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { integer, numeric, text } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
import type { IDType } from '../types.js'
|
||||
|
||||
export const getIDColumn = ({
|
||||
name,
|
||||
type,
|
||||
notNull,
|
||||
primaryKey,
|
||||
}: {
|
||||
name: string
|
||||
notNull?: boolean
|
||||
primaryKey: boolean
|
||||
type: IDType
|
||||
}) => {
|
||||
let column
|
||||
switch (type) {
|
||||
case 'integer':
|
||||
column = integer(name)
|
||||
break
|
||||
case 'numeric':
|
||||
column = numeric(name)
|
||||
break
|
||||
case 'text':
|
||||
column = text(name)
|
||||
break
|
||||
}
|
||||
|
||||
if (notNull) {
|
||||
column.notNull()
|
||||
}
|
||||
|
||||
if (primaryKey) {
|
||||
column.primaryKey()
|
||||
}
|
||||
|
||||
return column
|
||||
}
|
||||
13
packages/db-sqlite/src/schema/idToUUID.ts
Normal file
13
packages/db-sqlite/src/schema/idToUUID.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Field } from 'payload'
|
||||
|
||||
export const idToUUID = (fields: Field[]): Field[] =>
|
||||
fields.map((field) => {
|
||||
if ('name' in field && field.name === 'id') {
|
||||
return {
|
||||
...field,
|
||||
name: '_uuid',
|
||||
}
|
||||
}
|
||||
|
||||
return field
|
||||
})
|
||||
31
packages/db-sqlite/src/schema/setColumnID.ts
Normal file
31
packages/db-sqlite/src/schema/setColumnID.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { SQLiteColumnBuilder } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
import { integer, numeric, text } from 'drizzle-orm/sqlite-core'
|
||||
import { type Field, flattenTopLevelFields } from 'payload'
|
||||
import { fieldAffectsData } from 'payload/shared'
|
||||
|
||||
import type { IDType } from '../types.js'
|
||||
|
||||
type Args = {
|
||||
columns: Record<string, SQLiteColumnBuilder>
|
||||
fields: Field[]
|
||||
}
|
||||
export const setColumnID = ({ columns, fields }: Args): IDType => {
|
||||
const idField = flattenTopLevelFields(fields).find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
if (idField) {
|
||||
if (idField.type === 'number') {
|
||||
columns.id = numeric('id').primaryKey()
|
||||
return 'numeric'
|
||||
}
|
||||
|
||||
if (idField.type === 'text') {
|
||||
columns.id = text('id').primaryKey()
|
||||
return 'text'
|
||||
}
|
||||
}
|
||||
|
||||
columns.id = integer('id').primaryKey()
|
||||
return 'integer'
|
||||
}
|
||||
787
packages/db-sqlite/src/schema/traverseFields.ts
Normal file
787
packages/db-sqlite/src/schema/traverseFields.ts
Normal file
@@ -0,0 +1,787 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, SQLiteColumnBuilder } from 'drizzle-orm/sqlite-core'
|
||||
import type { Field, TabAsField } from 'payload'
|
||||
|
||||
import {
|
||||
createTableName,
|
||||
hasLocalesTable,
|
||||
validateExistingBlockIsIdentical,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
SQLiteIntegerBuilder,
|
||||
SQLiteNumericBuilder,
|
||||
SQLiteTextBuilder,
|
||||
foreignKey,
|
||||
index,
|
||||
integer,
|
||||
numeric,
|
||||
text,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import { InvalidConfiguration } from 'payload'
|
||||
import { fieldAffectsData, optionIsObject } from 'payload/shared'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { GenericColumns, IDType, SQLiteAdapter } from '../types.js'
|
||||
import type { BaseExtraConfig, RelationMap } from './build.js'
|
||||
|
||||
import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { getIDColumn } from './getIDColumn.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
|
||||
type Args = {
|
||||
adapter: SQLiteAdapter
|
||||
columnPrefix?: string
|
||||
columns: Record<string, SQLiteColumnBuilder>
|
||||
disableNotNull: boolean
|
||||
disableUnique?: boolean
|
||||
fieldPrefix?: string
|
||||
fields: (Field | TabAsField)[]
|
||||
forceLocalized?: boolean
|
||||
indexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
locales: [string, ...string[]]
|
||||
localesColumns: Record<string, SQLiteColumnBuilder>
|
||||
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
|
||||
newTableName: string
|
||||
parentTableName: string
|
||||
relationsToBuild: RelationMap
|
||||
relationships: Set<string>
|
||||
rootRelationsToBuild?: RelationMap
|
||||
rootTableIDColType: IDType
|
||||
rootTableName: string
|
||||
versions: boolean
|
||||
}
|
||||
|
||||
type Result = {
|
||||
hasLocalizedField: boolean
|
||||
hasLocalizedManyNumberField: boolean
|
||||
hasLocalizedManyTextField: boolean
|
||||
hasLocalizedRelationshipField: boolean
|
||||
hasManyNumberField: 'index' | boolean
|
||||
hasManyTextField: 'index' | boolean
|
||||
}
|
||||
|
||||
export const traverseFields = ({
|
||||
adapter,
|
||||
columnPrefix,
|
||||
columns,
|
||||
disableNotNull,
|
||||
disableUnique = false,
|
||||
fieldPrefix,
|
||||
fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
}: Args): Result => {
|
||||
let hasLocalizedField = false
|
||||
let hasLocalizedRelationshipField = false
|
||||
let hasManyTextField: 'index' | boolean = false
|
||||
let hasLocalizedManyTextField = false
|
||||
let hasManyNumberField: 'index' | boolean = false
|
||||
let hasLocalizedManyNumberField = false
|
||||
|
||||
let parentIDColType: IDType = 'integer'
|
||||
if (columns.id instanceof SQLiteIntegerBuilder) parentIDColType = 'integer'
|
||||
if (columns.id instanceof SQLiteNumericBuilder) parentIDColType = 'numeric'
|
||||
if (columns.id instanceof SQLiteTextBuilder) parentIDColType = 'text'
|
||||
|
||||
fields.forEach((field) => {
|
||||
if ('name' in field && field.name === 'id') return
|
||||
let columnName: string
|
||||
let fieldName: string
|
||||
|
||||
let targetTable = columns
|
||||
let targetIndexes = indexes
|
||||
|
||||
if (fieldAffectsData(field)) {
|
||||
columnName = `${columnPrefix || ''}${field.name[0] === '_' ? '_' : ''}${toSnakeCase(
|
||||
field.name,
|
||||
)}`
|
||||
fieldName = `${fieldPrefix?.replace('.', '_') || ''}${field.name}`
|
||||
|
||||
// If field is localized,
|
||||
// add the column to the locale table instead of main table
|
||||
if (
|
||||
adapter.payload.config.localization &&
|
||||
(field.localized || forceLocalized) &&
|
||||
field.type !== 'array' &&
|
||||
field.type !== 'blocks' &&
|
||||
(('hasMany' in field && field.hasMany !== true) || !('hasMany' in field))
|
||||
) {
|
||||
hasLocalizedField = true
|
||||
targetTable = localesColumns
|
||||
targetIndexes = localesIndexes
|
||||
}
|
||||
|
||||
if (
|
||||
(field.unique || field.index) &&
|
||||
!['array', 'blocks', 'group', 'point', 'relationship', 'upload'].includes(field.type) &&
|
||||
!('hasMany' in field && field.hasMany === true)
|
||||
) {
|
||||
const unique = disableUnique !== true && field.unique
|
||||
if (unique) {
|
||||
const constraintValue = `${fieldPrefix || ''}${field.name}`
|
||||
if (!adapter.fieldConstraints?.[rootTableName]) {
|
||||
adapter.fieldConstraints[rootTableName] = {}
|
||||
}
|
||||
adapter.fieldConstraints[rootTableName][`${columnName}_idx`] = constraintValue
|
||||
}
|
||||
targetIndexes[`${newTableName}_${field.name}Idx`] = createIndex({
|
||||
name: fieldName,
|
||||
columnName,
|
||||
tableName: newTableName,
|
||||
unique,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
switch (field.type) {
|
||||
case 'text': {
|
||||
if (field.hasMany) {
|
||||
if (field.localized) {
|
||||
hasLocalizedManyTextField = true
|
||||
}
|
||||
|
||||
if (field.index) {
|
||||
hasManyTextField = 'index'
|
||||
} else if (!hasManyTextField) {
|
||||
hasManyTextField = true
|
||||
}
|
||||
|
||||
if (field.unique) {
|
||||
throw new InvalidConfiguration(
|
||||
'Unique is not supported in SQLite for hasMany text fields.',
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = text(columnName)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'email':
|
||||
case 'code':
|
||||
case 'textarea': {
|
||||
targetTable[fieldName] = text(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
case 'number': {
|
||||
if (field.hasMany) {
|
||||
if (field.localized) {
|
||||
hasLocalizedManyNumberField = true
|
||||
}
|
||||
|
||||
if (field.index) {
|
||||
hasManyNumberField = 'index'
|
||||
} else if (!hasManyNumberField) {
|
||||
hasManyNumberField = true
|
||||
}
|
||||
|
||||
if (field.unique) {
|
||||
throw new InvalidConfiguration(
|
||||
'Unique is not supported in Postgres for hasMany number fields.',
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = numeric(columnName)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'richText':
|
||||
case 'json': {
|
||||
targetTable[fieldName] = text(columnName, { mode: 'json' })
|
||||
break
|
||||
}
|
||||
|
||||
case 'date': {
|
||||
targetTable[fieldName] = text(columnName)
|
||||
break
|
||||
}
|
||||
|
||||
case 'point': {
|
||||
break
|
||||
}
|
||||
|
||||
case 'radio':
|
||||
case 'select': {
|
||||
const options = field.options.map((option) => {
|
||||
if (optionIsObject(option)) {
|
||||
return option.value
|
||||
}
|
||||
|
||||
return option
|
||||
}) as [string, ...string[]]
|
||||
|
||||
if (field.type === 'select' && field.hasMany) {
|
||||
const selectTableName = createTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: newTableName,
|
||||
prefix: `${newTableName}_`,
|
||||
versionsCustomName: versions,
|
||||
})
|
||||
const baseColumns: Record<string, SQLiteColumnBuilder> = {
|
||||
order: integer('order').notNull(),
|
||||
parent: getIDColumn({
|
||||
name: 'parent_id',
|
||||
type: parentIDColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
}),
|
||||
value: text('value', { enum: options }),
|
||||
}
|
||||
|
||||
const baseExtraConfig: BaseExtraConfig = {
|
||||
orderIdx: (cols) => index(`${selectTableName}_order_idx`).on(cols.order),
|
||||
parentFk: (cols) =>
|
||||
foreignKey({
|
||||
name: `${selectTableName}_parent_fk`,
|
||||
columns: [cols.parent],
|
||||
foreignColumns: [adapter.tables[parentTableName].id],
|
||||
}).onDelete('cascade'),
|
||||
parentIdx: (cols) => index(`${selectTableName}_parent_idx`).on(cols.parent),
|
||||
}
|
||||
|
||||
if (field.localized) {
|
||||
baseColumns.locale = text('locale', { enum: locales }).notNull()
|
||||
baseExtraConfig.localeIdx = (cols) =>
|
||||
index(`${selectTableName}_locale_idx`).on(cols.locale)
|
||||
}
|
||||
|
||||
if (field.index) {
|
||||
baseExtraConfig.value = (cols) => index(`${selectTableName}_value_idx`).on(cols.value)
|
||||
}
|
||||
|
||||
buildTable({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
disableNotNull,
|
||||
disableUnique,
|
||||
fields: [],
|
||||
rootTableName,
|
||||
tableName: selectTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
relationsToBuild.set(fieldName, {
|
||||
type: 'many',
|
||||
// selects have their own localized table, independent of the base table.
|
||||
localized: false,
|
||||
target: selectTableName,
|
||||
})
|
||||
|
||||
adapter.relations[`relations_${selectTableName}`] = relations(
|
||||
adapter.tables[selectTableName],
|
||||
({ one }) => ({
|
||||
parent: one(adapter.tables[parentTableName], {
|
||||
fields: [adapter.tables[selectTableName].parent],
|
||||
references: [adapter.tables[parentTableName].id],
|
||||
relationName: fieldName,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
targetTable[fieldName] = text(fieldName, { enum: options })
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'checkbox': {
|
||||
targetTable[fieldName] = integer(columnName, { mode: 'boolean' })
|
||||
break
|
||||
}
|
||||
|
||||
case 'array': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const arrayTableName = createTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: newTableName,
|
||||
prefix: `${newTableName}_`,
|
||||
versionsCustomName: versions,
|
||||
})
|
||||
|
||||
const baseColumns: Record<string, SQLiteColumnBuilder> = {
|
||||
_order: integer('_order').notNull(),
|
||||
_parentID: getIDColumn({
|
||||
name: '_parent_id',
|
||||
type: parentIDColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
}),
|
||||
}
|
||||
|
||||
const baseExtraConfig: BaseExtraConfig = {
|
||||
_orderIdx: (cols) => index(`${arrayTableName}_order_idx`).on(cols._order),
|
||||
_parentIDFk: (cols) =>
|
||||
foreignKey({
|
||||
name: `${arrayTableName}_parent_id_fk`,
|
||||
columns: [cols['_parentID']],
|
||||
foreignColumns: [adapter.tables[parentTableName].id],
|
||||
}).onDelete('cascade'),
|
||||
_parentIDIdx: (cols) => index(`${arrayTableName}_parent_id_idx`).on(cols._parentID),
|
||||
}
|
||||
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
baseColumns._locale = text('_locale', { enum: locales }).notNull()
|
||||
baseExtraConfig._localeIdx = (cols) =>
|
||||
index(`${arrayTableName}_locale_idx`).on(cols._locale)
|
||||
}
|
||||
|
||||
const {
|
||||
hasManyNumberField: subHasManyNumberField,
|
||||
hasManyTextField: subHasManyTextField,
|
||||
relationsToBuild: subRelationsToBuild,
|
||||
} = buildTable({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fields: disableUnique ? idToUUID(field.fields) : field.fields,
|
||||
rootRelationsToBuild,
|
||||
rootRelationships: relationships,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName: arrayTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (subHasManyTextField) {
|
||||
if (!hasManyTextField || subHasManyTextField === 'index')
|
||||
hasManyTextField = subHasManyTextField
|
||||
}
|
||||
if (subHasManyNumberField) {
|
||||
if (!hasManyNumberField || subHasManyNumberField === 'index')
|
||||
hasManyNumberField = subHasManyNumberField
|
||||
}
|
||||
|
||||
relationsToBuild.set(fieldName, {
|
||||
type: 'many',
|
||||
// arrays have their own localized table, independent of the base table.
|
||||
localized: false,
|
||||
target: arrayTableName,
|
||||
})
|
||||
|
||||
adapter.relations[`relations_${arrayTableName}`] = relations(
|
||||
adapter.tables[arrayTableName],
|
||||
({ many, one }) => {
|
||||
const result: Record<string, Relation<string>> = {
|
||||
_parentID: one(adapter.tables[parentTableName], {
|
||||
fields: [adapter.tables[arrayTableName]._parentID],
|
||||
references: [adapter.tables[parentTableName].id],
|
||||
relationName: fieldName,
|
||||
}),
|
||||
}
|
||||
|
||||
if (hasLocalesTable(field.fields)) {
|
||||
result._locales = many(adapter.tables[`${arrayTableName}${adapter.localesSuffix}`], {
|
||||
relationName: '_locales',
|
||||
})
|
||||
}
|
||||
|
||||
subRelationsToBuild.forEach(({ type, localized, target }, key) => {
|
||||
if (type === 'one') {
|
||||
const arrayWithLocalized = localized
|
||||
? `${arrayTableName}${adapter.localesSuffix}`
|
||||
: arrayTableName
|
||||
result[key] = one(adapter.tables[target], {
|
||||
fields: [adapter.tables[arrayWithLocalized][key]],
|
||||
references: [adapter.tables[target].id],
|
||||
relationName: key,
|
||||
})
|
||||
}
|
||||
if (type === 'many') {
|
||||
result[key] = many(adapter.tables[target], { relationName: key })
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
},
|
||||
)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'blocks': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
field.blocks.forEach((block) => {
|
||||
const blockTableName = createTableName({
|
||||
adapter,
|
||||
config: block,
|
||||
parentTableName: rootTableName,
|
||||
prefix: `${rootTableName}_blocks_`,
|
||||
versionsCustomName: versions,
|
||||
})
|
||||
if (!adapter.tables[blockTableName]) {
|
||||
const baseColumns: Record<string, SQLiteColumnBuilder> = {
|
||||
_order: integer('_order').notNull(),
|
||||
_parentID: getIDColumn({
|
||||
name: '_parent_id',
|
||||
type: rootTableIDColType,
|
||||
notNull: true,
|
||||
primaryKey: false,
|
||||
}),
|
||||
_path: text('_path').notNull(),
|
||||
}
|
||||
|
||||
const baseExtraConfig: BaseExtraConfig = {
|
||||
_orderIdx: (cols) => index(`${blockTableName}_order_idx`).on(cols._order),
|
||||
_parentIDIdx: (cols) => index(`${blockTableName}_parent_id_idx`).on(cols._parentID),
|
||||
_parentIdFk: (cols) =>
|
||||
foreignKey({
|
||||
name: `${blockTableName}_parent_id_fk`,
|
||||
columns: [cols._parentID],
|
||||
foreignColumns: [adapter.tables[rootTableName].id],
|
||||
}).onDelete('cascade'),
|
||||
_pathIdx: (cols) => index(`${blockTableName}_path_idx`).on(cols._path),
|
||||
}
|
||||
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
baseColumns._locale = text('_locale', { enum: locales }).notNull()
|
||||
baseExtraConfig._localeIdx = (cols) =>
|
||||
index(`${blockTableName}_locale_idx`).on(cols._locale)
|
||||
}
|
||||
|
||||
const {
|
||||
hasManyNumberField: subHasManyNumberField,
|
||||
hasManyTextField: subHasManyTextField,
|
||||
relationsToBuild: subRelationsToBuild,
|
||||
} = buildTable({
|
||||
adapter,
|
||||
baseColumns,
|
||||
baseExtraConfig,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fields: disableUnique ? idToUUID(block.fields) : block.fields,
|
||||
rootRelationsToBuild,
|
||||
rootRelationships: relationships,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName: blockTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (subHasManyTextField) {
|
||||
if (!hasManyTextField || subHasManyTextField === 'index')
|
||||
hasManyTextField = subHasManyTextField
|
||||
}
|
||||
|
||||
if (subHasManyNumberField) {
|
||||
if (!hasManyNumberField || subHasManyNumberField === 'index')
|
||||
hasManyNumberField = subHasManyNumberField
|
||||
}
|
||||
|
||||
adapter.relations[`relations_${blockTableName}`] = relations(
|
||||
adapter.tables[blockTableName],
|
||||
({ many, one }) => {
|
||||
const result: Record<string, Relation<string>> = {
|
||||
_parentID: one(adapter.tables[rootTableName], {
|
||||
fields: [adapter.tables[blockTableName]._parentID],
|
||||
references: [adapter.tables[rootTableName].id],
|
||||
relationName: `_blocks_${block.slug}`,
|
||||
}),
|
||||
}
|
||||
|
||||
if (hasLocalesTable(block.fields)) {
|
||||
result._locales = many(
|
||||
adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
|
||||
{ relationName: '_locales' },
|
||||
)
|
||||
}
|
||||
|
||||
subRelationsToBuild.forEach(({ type, localized, target }, key) => {
|
||||
if (type === 'one') {
|
||||
const blockWithLocalized = localized
|
||||
? `${blockTableName}${adapter.localesSuffix}`
|
||||
: blockTableName
|
||||
result[key] = one(adapter.tables[target], {
|
||||
fields: [adapter.tables[blockWithLocalized][key]],
|
||||
references: [adapter.tables[target].id],
|
||||
relationName: key,
|
||||
})
|
||||
}
|
||||
if (type === 'many') {
|
||||
result[key] = many(adapter.tables[target], { relationName: key })
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
},
|
||||
)
|
||||
} else if (process.env.NODE_ENV !== 'production' && !versions) {
|
||||
validateExistingBlockIsIdentical({
|
||||
block,
|
||||
localized: field.localized,
|
||||
rootTableName,
|
||||
table: adapter.tables[blockTableName],
|
||||
tableLocales: adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
|
||||
})
|
||||
}
|
||||
// blocks relationships are defined from the collection or globals table down to the block, bypassing any subBlocks
|
||||
rootRelationsToBuild.set(`_blocks_${block.slug}`, {
|
||||
type: 'many',
|
||||
// blocks are not localized on the parent table
|
||||
localized: false,
|
||||
target: blockTableName,
|
||||
})
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'tab':
|
||||
case 'group': {
|
||||
if (!('name' in field)) {
|
||||
const {
|
||||
hasLocalizedField: groupHasLocalizedField,
|
||||
hasLocalizedManyNumberField: groupHasLocalizedManyNumberField,
|
||||
hasLocalizedManyTextField: groupHasLocalizedManyTextField,
|
||||
hasLocalizedRelationshipField: groupHasLocalizedRelationshipField,
|
||||
hasManyNumberField: groupHasManyNumberField,
|
||||
hasManyTextField: groupHasManyTextField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
columnPrefix,
|
||||
columns,
|
||||
disableNotNull,
|
||||
disableUnique,
|
||||
fieldPrefix,
|
||||
fields: field.fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (groupHasLocalizedField) hasLocalizedField = true
|
||||
if (groupHasLocalizedRelationshipField) hasLocalizedRelationshipField = true
|
||||
if (groupHasManyTextField) hasManyTextField = true
|
||||
if (groupHasLocalizedManyTextField) hasLocalizedManyTextField = true
|
||||
if (groupHasManyNumberField) hasManyNumberField = true
|
||||
if (groupHasLocalizedManyNumberField) hasLocalizedManyNumberField = true
|
||||
break
|
||||
}
|
||||
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const {
|
||||
hasLocalizedField: groupHasLocalizedField,
|
||||
hasLocalizedManyNumberField: groupHasLocalizedManyNumberField,
|
||||
hasLocalizedManyTextField: groupHasLocalizedManyTextField,
|
||||
hasLocalizedRelationshipField: groupHasLocalizedRelationshipField,
|
||||
hasManyNumberField: groupHasManyNumberField,
|
||||
hasManyTextField: groupHasManyTextField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
columnPrefix: `${columnName}_`,
|
||||
columns,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fieldPrefix: `${fieldName}.`,
|
||||
fields: field.fields,
|
||||
forceLocalized: field.localized,
|
||||
indexes,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName: `${parentTableName}_${columnName}`,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (groupHasLocalizedField) hasLocalizedField = true
|
||||
if (groupHasLocalizedRelationshipField) hasLocalizedRelationshipField = true
|
||||
if (groupHasManyTextField) hasManyTextField = true
|
||||
if (groupHasLocalizedManyTextField) hasLocalizedManyTextField = true
|
||||
if (groupHasManyNumberField) hasManyNumberField = true
|
||||
if (groupHasLocalizedManyNumberField) hasLocalizedManyNumberField = true
|
||||
break
|
||||
}
|
||||
|
||||
case 'tabs': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const {
|
||||
hasLocalizedField: tabHasLocalizedField,
|
||||
hasLocalizedManyNumberField: tabHasLocalizedManyNumberField,
|
||||
hasLocalizedManyTextField: tabHasLocalizedManyTextField,
|
||||
hasLocalizedRelationshipField: tabHasLocalizedRelationshipField,
|
||||
hasManyNumberField: tabHasManyNumberField,
|
||||
hasManyTextField: tabHasManyTextField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
columnPrefix,
|
||||
columns,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fieldPrefix,
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
forceLocalized,
|
||||
indexes,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (tabHasLocalizedField) hasLocalizedField = true
|
||||
if (tabHasLocalizedRelationshipField) hasLocalizedRelationshipField = true
|
||||
if (tabHasManyTextField) hasManyTextField = true
|
||||
if (tabHasLocalizedManyTextField) hasLocalizedManyTextField = true
|
||||
if (tabHasManyNumberField) hasManyNumberField = true
|
||||
if (tabHasLocalizedManyNumberField) hasLocalizedManyNumberField = true
|
||||
break
|
||||
}
|
||||
|
||||
case 'row':
|
||||
case 'collapsible': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
const {
|
||||
hasLocalizedField: rowHasLocalizedField,
|
||||
hasLocalizedManyNumberField: rowHasLocalizedManyNumberField,
|
||||
hasLocalizedManyTextField: rowHasLocalizedManyTextField,
|
||||
hasLocalizedRelationshipField: rowHasLocalizedRelationshipField,
|
||||
hasManyNumberField: rowHasManyNumberField,
|
||||
hasManyTextField: rowHasManyTextField,
|
||||
} = traverseFields({
|
||||
adapter,
|
||||
columnPrefix,
|
||||
columns,
|
||||
disableNotNull: disableNotNullFromHere,
|
||||
disableUnique,
|
||||
fieldPrefix,
|
||||
fields: field.fields,
|
||||
forceLocalized,
|
||||
indexes,
|
||||
locales,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (rowHasLocalizedField) hasLocalizedField = true
|
||||
if (rowHasLocalizedRelationshipField) hasLocalizedRelationshipField = true
|
||||
if (rowHasManyTextField) hasManyTextField = true
|
||||
if (rowHasLocalizedManyTextField) hasLocalizedManyTextField = true
|
||||
if (rowHasManyNumberField) hasManyNumberField = true
|
||||
if (rowHasLocalizedManyNumberField) hasLocalizedManyNumberField = true
|
||||
break
|
||||
}
|
||||
|
||||
case 'relationship':
|
||||
case 'upload':
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
field.relationTo.forEach((relation) => relationships.add(relation))
|
||||
} else if (field.type === 'relationship' && field.hasMany) {
|
||||
relationships.add(field.relationTo)
|
||||
} else {
|
||||
// simple relationships get a column on the targetTable with a foreign key to the relationTo table
|
||||
const relationshipConfig = adapter.payload.collections[field.relationTo].config
|
||||
|
||||
const tableName = adapter.tableNameMap.get(toSnakeCase(field.relationTo))
|
||||
|
||||
// get the id type of the related collection
|
||||
let colType: IDType = 'integer'
|
||||
const relatedCollectionCustomID = relationshipConfig.fields.find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
if (relatedCollectionCustomID?.type === 'number') colType = 'numeric'
|
||||
if (relatedCollectionCustomID?.type === 'text') colType = 'text'
|
||||
|
||||
// make the foreign key column for relationship using the correct id column type
|
||||
targetTable[fieldName] = getIDColumn({
|
||||
name: `${columnName}_id`,
|
||||
type: colType,
|
||||
primaryKey: false,
|
||||
}).references(() => adapter.tables[tableName].id, { onDelete: 'set null' })
|
||||
|
||||
// add relationship to table
|
||||
relationsToBuild.set(fieldName, {
|
||||
type: 'one',
|
||||
localized: adapter.payload.config.localization && field.localized,
|
||||
target: tableName,
|
||||
})
|
||||
|
||||
// add notNull when not required
|
||||
if (!disableNotNull && field.required && !field.admin?.condition) {
|
||||
targetTable[fieldName].notNull()
|
||||
}
|
||||
break
|
||||
}
|
||||
if (adapter.payload.config.localization && field.localized) {
|
||||
hasLocalizedRelationshipField = true
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
const condition = field.admin && field.admin.condition
|
||||
|
||||
if (
|
||||
!disableNotNull &&
|
||||
targetTable[fieldName] &&
|
||||
'required' in field &&
|
||||
field.required &&
|
||||
!condition
|
||||
) {
|
||||
targetTable[fieldName].notNull()
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
hasLocalizedField,
|
||||
hasLocalizedManyNumberField,
|
||||
hasLocalizedManyTextField,
|
||||
hasLocalizedRelationshipField,
|
||||
hasManyNumberField,
|
||||
hasManyTextField,
|
||||
}
|
||||
}
|
||||
167
packages/db-sqlite/src/types.ts
Normal file
167
packages/db-sqlite/src/types.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import type { Client, Config, ResultSet } from '@libsql/client'
|
||||
import type { Operators } from '@payloadcms/drizzle'
|
||||
import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { ColumnDataType, DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type {
|
||||
SQLiteColumn,
|
||||
SQLiteInsertOnConflictDoUpdateConfig,
|
||||
SQLiteTableWithColumns,
|
||||
SQLiteTransactionConfig,
|
||||
} from 'drizzle-orm/sqlite-core'
|
||||
import type { SQLiteRaw } from 'drizzle-orm/sqlite-core/query-builders/raw'
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
|
||||
export type Args = {
|
||||
client: Config
|
||||
idType?: 'serial' | 'uuid'
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
push?: boolean
|
||||
relationshipsSuffix?: string
|
||||
schemaName?: string
|
||||
transactionOptions?: SQLiteTransactionConfig | false
|
||||
versionsSuffix?: string
|
||||
}
|
||||
|
||||
export type GenericColumn = SQLiteColumn<
|
||||
{
|
||||
baseColumn: never
|
||||
columnType: string
|
||||
data: unknown
|
||||
dataType: ColumnDataType
|
||||
driverParam: unknown
|
||||
enumValues: string[]
|
||||
hasDefault: false
|
||||
name: string
|
||||
notNull: false
|
||||
tableName: string
|
||||
},
|
||||
object
|
||||
>
|
||||
|
||||
export type GenericColumns = {
|
||||
[x: string]: GenericColumn
|
||||
}
|
||||
|
||||
export type GenericTable = SQLiteTableWithColumns<{
|
||||
columns: GenericColumns
|
||||
dialect: string
|
||||
name: string
|
||||
schema: string
|
||||
}>
|
||||
|
||||
export type GenericRelation = Relations<string, Record<string, Relation<string>>>
|
||||
|
||||
export type CountDistinct = (args: {
|
||||
db: LibSQLDatabase
|
||||
joins: BuildQueryJoinAliases
|
||||
tableName: string
|
||||
where: SQL
|
||||
}) => Promise<number>
|
||||
|
||||
export type DeleteWhere = (args: {
|
||||
db: LibSQLDatabase
|
||||
tableName: string
|
||||
where: SQL
|
||||
}) => Promise<void>
|
||||
|
||||
export type DropDatabase = (args: { adapter: SQLiteAdapter }) => Promise<void>
|
||||
|
||||
export type Execute<T> = (args: {
|
||||
db?: LibSQLDatabase
|
||||
drizzle?: LibSQLDatabase
|
||||
raw?: string
|
||||
sql?: SQL<unknown>
|
||||
}) => SQLiteRaw<Promise<T>> | SQLiteRaw<ResultSet>
|
||||
|
||||
export type Insert = (args: {
|
||||
db: LibSQLDatabase
|
||||
onConflictDoUpdate?: SQLiteInsertOnConflictDoUpdateConfig<any>
|
||||
tableName: string
|
||||
values: Record<string, unknown> | Record<string, unknown>[]
|
||||
}) => Promise<Record<string, unknown>[]>
|
||||
|
||||
// Explicitly omit drizzle property for complete override in SQLiteAdapter, required in ts 5.5
|
||||
type SQLiteDrizzleAdapter = Omit<
|
||||
DrizzleAdapter,
|
||||
| 'countDistinct'
|
||||
| 'deleteWhere'
|
||||
| 'drizzle'
|
||||
| 'dropDatabase'
|
||||
| 'execute'
|
||||
| 'insert'
|
||||
| 'operators'
|
||||
| 'relations'
|
||||
>
|
||||
|
||||
export type SQLiteAdapter = {
|
||||
client: Client
|
||||
clientConfig: Args['client']
|
||||
countDistinct: CountDistinct
|
||||
defaultDrizzleSnapshot: any
|
||||
deleteWhere: DeleteWhere
|
||||
drizzle: LibSQLDatabase
|
||||
dropDatabase: DropDatabase
|
||||
execute: Execute<unknown>
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
* Used for returning properly formed errors from unique fields
|
||||
*/
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: Args['idType']
|
||||
initializing: Promise<void>
|
||||
insert: Insert
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
operators: Operators
|
||||
push: boolean
|
||||
rejectInitializing: () => void
|
||||
relations: Record<string, GenericRelation>
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, GenericRelation | GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
tableNameMap: Map<string, string>
|
||||
tables: Record<string, GenericTable>
|
||||
transactionOptions: SQLiteTransactionConfig
|
||||
versionsSuffix?: string
|
||||
} & SQLiteDrizzleAdapter
|
||||
|
||||
export type IDType = 'integer' | 'numeric' | 'text'
|
||||
|
||||
export type MigrateUpArgs = {
|
||||
db: LibSQLDatabase
|
||||
payload: Payload
|
||||
req?: Partial<PayloadRequest>
|
||||
}
|
||||
export type MigrateDownArgs = {
|
||||
db: LibSQLDatabase
|
||||
payload: Payload
|
||||
req?: Partial<PayloadRequest>
|
||||
}
|
||||
|
||||
declare module 'payload' {
|
||||
export interface DatabaseAdapter
|
||||
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
|
||||
DrizzleAdapter {
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
* Used for returning properly formed errors from unique fields
|
||||
*/
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: Args['idType']
|
||||
initializing: Promise<void>
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
push: boolean
|
||||
rejectInitializing: () => void
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, GenericRelation | GenericTable>
|
||||
tableNameMap: Map<string, string>
|
||||
transactionOptions: SQLiteTransactionConfig
|
||||
versionsSuffix?: string
|
||||
}
|
||||
}
|
||||
41
packages/db-sqlite/tsconfig.json
Normal file
41
packages/db-sqlite/tsconfig.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
// Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false
|
||||
/* Do not emit outputs. */,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist"
|
||||
/* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src"
|
||||
/* Specify the root folder within your source files. */
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
"eslint.config.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": [
|
||||
"src",
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../payload"
|
||||
},
|
||||
{
|
||||
"path": "../translations"
|
||||
},
|
||||
{
|
||||
"path": "../drizzle"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
packages/drizzle/.eslintignore
Normal file
10
packages/drizzle/.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
7
packages/drizzle/.eslintrc.cjs
Normal file
7
packages/drizzle/.eslintrc.cjs
Normal file
@@ -0,0 +1,7 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
10
packages/drizzle/.prettierignore
Normal file
10
packages/drizzle/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
15
packages/drizzle/.swcrc
Normal file
15
packages/drizzle/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
}
|
||||
}
|
||||
3
packages/drizzle/README.md
Normal file
3
packages/drizzle/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Payload Drizzle Adapter
|
||||
|
||||
The Drizzle package is used by db-postgres and db-sqlite for shared functionality of SQL databases. It is not meant to be used directly in Payload projects.
|
||||
71
packages/drizzle/package.json
Normal file
71
packages/drizzle/package.json
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.0.0-beta.36",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git",
|
||||
"directory": "packages/drizzle"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Payload <dev@payloadcms.com> (https://payloadcms.com)",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./src/types.ts",
|
||||
"types": "./src/types.ts",
|
||||
"default": "./src/types.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/types.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"mock.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepack": "pnpm clean && pnpm turbo build",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build"
|
||||
},
|
||||
"dependencies": {
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@libsql/client": "^0.6.2",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/pg": "8.10.2",
|
||||
"@types/to-snake-case": "1.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./dist/types.js",
|
||||
"types": "./dist/types.d.ts"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
36
packages/drizzle/src/count.ts
Normal file
36
packages/drizzle/src/count.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { Count } from 'payload'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
|
||||
export const count: Count = async function count(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, locale, req, where: whereArg },
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
|
||||
const { joins, where } = await buildQuery({
|
||||
adapter: this,
|
||||
fields: collectionConfig.fields,
|
||||
locale,
|
||||
tableName,
|
||||
where: whereArg,
|
||||
})
|
||||
|
||||
const countResult = await this.countDistinct({
|
||||
db,
|
||||
joins,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
return { totalDocs: countResult }
|
||||
}
|
||||
@@ -2,12 +2,12 @@ import type { Create } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export const create: Create = async function create(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, data, req },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
@@ -2,12 +2,12 @@ import type { CreateGlobalArgs, PayloadRequest } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function createGlobal<T extends Record<string, unknown>>(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest }: CreateGlobalArgs,
|
||||
): Promise<T> {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
@@ -4,12 +4,12 @@ import { sql } from 'drizzle-orm'
|
||||
import { buildVersionGlobalFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function createGlobalVersion<T extends TypeWithID>(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ autosave, globalSlug, req = {} as PayloadRequest, versionData }: CreateGlobalVersionArgs,
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
@@ -32,13 +32,15 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
})
|
||||
|
||||
const table = this.tables[tableName]
|
||||
|
||||
if (global.versions.drafts) {
|
||||
await db.execute(sql`
|
||||
UPDATE ${table}
|
||||
SET latest = false
|
||||
WHERE ${table.id} != ${result.id};
|
||||
`)
|
||||
await this.execute({
|
||||
db,
|
||||
sql: sql`
|
||||
UPDATE ${table}
|
||||
SET latest = false
|
||||
WHERE ${table.id} != ${result.id};
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -3,10 +3,10 @@ import type { DBIdentifierName } from 'payload'
|
||||
import { APIError } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
adapter: Pick<DrizzleAdapter, 'tableNameMap' | 'versionsSuffix'>
|
||||
/** The collection, global or field config **/
|
||||
config: {
|
||||
dbName?: DBIdentifierName
|
||||
@@ -20,6 +20,7 @@ type Args = {
|
||||
prefix?: string
|
||||
/** For tables based on fields that could have both enumName and dbName (ie: select with hasMany), default: 'dbName' */
|
||||
target?: 'dbName' | 'enumName'
|
||||
/** Throws error if true for postgres when table and enum names exceed 63 characters */
|
||||
throwValidationError?: boolean
|
||||
/** Adds the versions suffix to the default table name - should only be used on the base collection to avoid duplicate suffixing */
|
||||
versions?: boolean
|
||||
@@ -4,12 +4,12 @@ import { sql } from 'drizzle-orm'
|
||||
import { buildVersionCollectionFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function createVersion<T extends TypeWithID>(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{
|
||||
autosave,
|
||||
collectionSlug,
|
||||
@@ -45,12 +45,15 @@ export async function createVersion<T extends TypeWithID>(
|
||||
const table = this.tables[tableName]
|
||||
|
||||
if (collection.versions.drafts) {
|
||||
await db.execute(sql`
|
||||
await this.execute({
|
||||
db,
|
||||
sql: sql`
|
||||
UPDATE ${table}
|
||||
SET latest = false
|
||||
WHERE ${table.id} != ${result.id}
|
||||
AND ${table.parent} = ${parent}
|
||||
`)
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -3,12 +3,12 @@ import type { DeleteMany, PayloadRequest } from 'payload'
|
||||
import { inArray } from 'drizzle-orm'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
@@ -35,6 +35,10 @@ export const deleteMany: DeleteMany = async function deleteMany(
|
||||
})
|
||||
|
||||
if (ids.length > 0) {
|
||||
await db.delete(this.tables[tableName]).where(inArray(this.tables[tableName].id, ids))
|
||||
await this.deleteWhere({
|
||||
db,
|
||||
tableName,
|
||||
where: inArray(this.tables[tableName].id, ids),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import type { DeleteOne, PayloadRequest } from 'payload'
|
||||
import { eq } from 'drizzle-orm'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { buildFindManyArgs } from './find/buildFindManyArgs.js'
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
@@ -11,7 +11,7 @@ import { selectDistinct } from './queries/selectDistinct.js'
|
||||
import { transform } from './transform/read/index.js'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, req = {} as PayloadRequest, where: whereArg },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
@@ -63,7 +63,11 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
fields: collection.fields,
|
||||
})
|
||||
|
||||
await db.delete(this.tables[tableName]).where(eq(this.tables[tableName].id, docToDelete.id))
|
||||
await this.deleteWhere({
|
||||
db,
|
||||
tableName,
|
||||
where: eq(this.tables[tableName].id, docToDelete.id),
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -4,12 +4,12 @@ import { inArray } from 'drizzle-orm'
|
||||
import { buildVersionCollectionFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export const deleteVersions: DeleteVersions = async function deleteVersion(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where: where },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
@@ -40,7 +40,11 @@ export const deleteVersions: DeleteVersions = async function deleteVersion(
|
||||
})
|
||||
|
||||
if (ids.length > 0) {
|
||||
await db.delete(this.tables[tableName]).where(inArray(this.tables[tableName].id, ids))
|
||||
await this.deleteWhere({
|
||||
db,
|
||||
tableName,
|
||||
where: inArray(this.tables[tableName].id, ids),
|
||||
})
|
||||
}
|
||||
|
||||
return docs
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Destroy } from 'payload'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export const destroy: Destroy = async function destroy(this: PostgresAdapter) {
|
||||
this.enums = {}
|
||||
export const destroy: Destroy = async function destroy(this: DrizzleAdapter) {
|
||||
if (this.enums) this.enums = {}
|
||||
this.schema = {}
|
||||
this.tables = {}
|
||||
this.relations = {}
|
||||
@@ -2,12 +2,12 @@ import type { Find, PayloadRequest, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export const find: Find = async function find(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{
|
||||
collection,
|
||||
limit,
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { DBQueryConfig } from 'drizzle-orm'
|
||||
import type { Field } from 'payload'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
import type { DrizzleAdapter } from '../types.js'
|
||||
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
type BuildFindQueryArgs = {
|
||||
adapter: PostgresAdapter
|
||||
adapter: DrizzleAdapter
|
||||
depth: number
|
||||
fields: Field[]
|
||||
tableName: string
|
||||
@@ -1,21 +1,20 @@
|
||||
import type { Field, FindArgs, PayloadRequest, TypeWithID } from 'payload'
|
||||
|
||||
import { inArray, sql } from 'drizzle-orm'
|
||||
import { inArray } from 'drizzle-orm'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
import type { DrizzleAdapter } from '../types.js'
|
||||
import type { ChainedMethods } from './chainMethods.js'
|
||||
|
||||
import buildQuery from '../queries/buildQuery.js'
|
||||
import { selectDistinct } from '../queries/selectDistinct.js'
|
||||
import { transform } from '../transform/read/index.js'
|
||||
import { buildFindManyArgs } from './buildFindManyArgs.js'
|
||||
import { chainMethods } from './chainMethods.js'
|
||||
|
||||
type Args = Omit<FindArgs, 'collection'> & {
|
||||
adapter: PostgresAdapter
|
||||
type Args = {
|
||||
adapter: DrizzleAdapter
|
||||
fields: Field[]
|
||||
tableName: string
|
||||
}
|
||||
} & Omit<FindArgs, 'collection'>
|
||||
|
||||
export const findMany = async function find({
|
||||
adapter,
|
||||
@@ -31,14 +30,17 @@ export const findMany = async function find({
|
||||
where: whereArg,
|
||||
}: Args) {
|
||||
const db = adapter.sessions[await req.transactionID]?.db || adapter.drizzle
|
||||
const table = adapter.tables[tableName]
|
||||
|
||||
const limit = limitArg ?? 10
|
||||
let limit = limitArg
|
||||
let totalDocs: number
|
||||
let totalPages: number
|
||||
let hasPrevPage: boolean
|
||||
let hasNextPage: boolean
|
||||
let pagingCounter: number
|
||||
const offset = skip || (page - 1) * limit
|
||||
|
||||
if (limit === 0) {
|
||||
limit = undefined
|
||||
}
|
||||
|
||||
const { joins, orderBy, selectFields, where } = await buildQuery({
|
||||
adapter,
|
||||
@@ -68,8 +70,8 @@ export const findMany = async function find({
|
||||
tableName,
|
||||
})
|
||||
|
||||
selectDistinctMethods.push({ args: [skip || (page - 1) * limit], method: 'offset' })
|
||||
selectDistinctMethods.push({ args: [limit === 0 ? undefined : limit], method: 'limit' })
|
||||
selectDistinctMethods.push({ args: [offset], method: 'offset' })
|
||||
selectDistinctMethods.push({ args: [limit], method: 'limit' })
|
||||
|
||||
const selectDistinctResult = await selectDistinct({
|
||||
adapter,
|
||||
@@ -104,40 +106,25 @@ export const findMany = async function find({
|
||||
findManyArgs.where = inArray(adapter.tables[tableName].id, orderedIDs)
|
||||
}
|
||||
} else {
|
||||
findManyArgs.limit = limitArg === 0 ? undefined : limitArg
|
||||
|
||||
const offset = skip || (page - 1) * limitArg
|
||||
|
||||
if (!Number.isNaN(offset)) findManyArgs.offset = offset
|
||||
findManyArgs.limit = limit
|
||||
findManyArgs.offset = offset
|
||||
findManyArgs.orderBy = orderBy.order(orderBy.column)
|
||||
|
||||
if (where) {
|
||||
findManyArgs.where = where
|
||||
}
|
||||
findManyArgs.orderBy = orderBy.order(orderBy.column)
|
||||
}
|
||||
|
||||
const findPromise = db.query[tableName].findMany(findManyArgs)
|
||||
|
||||
if (pagination !== false && (orderedIDs ? orderedIDs?.length <= limit : true)) {
|
||||
const selectCountMethods: ChainedMethods = []
|
||||
joins.forEach(({ condition, table }) => {
|
||||
selectCountMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
totalDocs = await adapter.countDistinct({
|
||||
db,
|
||||
joins,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
const countResult = await chainMethods({
|
||||
methods: selectCountMethods,
|
||||
query: db
|
||||
.select({
|
||||
count: sql<number>`count
|
||||
(DISTINCT ${adapter.tables[tableName].id})`,
|
||||
})
|
||||
.from(table)
|
||||
.where(where),
|
||||
})
|
||||
totalDocs = Number(countResult[0].count)
|
||||
totalPages = typeof limit === 'number' && limit !== 0 ? Math.ceil(totalDocs / limit) : 1
|
||||
hasPrevPage = page > 1
|
||||
hasNextPage = totalPages > page
|
||||
@@ -171,7 +158,7 @@ export const findMany = async function find({
|
||||
docs,
|
||||
hasNextPage,
|
||||
hasPrevPage,
|
||||
limit,
|
||||
limit: limitArg,
|
||||
nextPage: hasNextPage ? page + 1 : null,
|
||||
page,
|
||||
pagingCounter,
|
||||
@@ -4,12 +4,12 @@ import type { Field } from 'payload'
|
||||
import { fieldAffectsData, tabHasName } from 'payload/shared'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
import type { DrizzleAdapter } from '../types.js'
|
||||
import type { Result } from './buildFindManyArgs.js'
|
||||
|
||||
type TraverseFieldArgs = {
|
||||
_locales: Result
|
||||
adapter: PostgresAdapter
|
||||
adapter: DrizzleAdapter
|
||||
currentArgs: Result
|
||||
currentTableName: string
|
||||
depth?: number
|
||||
@@ -2,12 +2,12 @@ import type { FindGlobal } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ slug, locale, req, where },
|
||||
) {
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
@@ -3,12 +3,12 @@ import type { FindGlobalVersions, PayloadRequest, SanitizedGlobalConfig } from '
|
||||
import { buildVersionGlobalFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{
|
||||
global,
|
||||
limit,
|
||||
41
packages/drizzle/src/findMigrationDir.ts
Normal file
41
packages/drizzle/src/findMigrationDir.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
* Attempt to find migrations directory.
|
||||
*
|
||||
* Checks for the following directories in order:
|
||||
* - `migrationDir` argument from Payload config
|
||||
* - `src/migrations`
|
||||
* - `dist/migrations`
|
||||
* - `migrations`
|
||||
*
|
||||
* Defaults to `src/migrations`
|
||||
*
|
||||
* @param migrationDir
|
||||
* @returns
|
||||
*/
|
||||
export function findMigrationDir(migrationDir?: string): string {
|
||||
const cwd = process.cwd()
|
||||
const srcDir = path.resolve(cwd, 'src/migrations')
|
||||
const distDir = path.resolve(cwd, 'dist/migrations')
|
||||
const relativeMigrations = path.resolve(cwd, 'migrations')
|
||||
|
||||
// Use arg if provided
|
||||
if (migrationDir) return migrationDir
|
||||
|
||||
// Check other common locations
|
||||
if (fs.existsSync(srcDir)) {
|
||||
return srcDir
|
||||
}
|
||||
|
||||
if (fs.existsSync(distDir)) {
|
||||
return distDir
|
||||
}
|
||||
|
||||
if (fs.existsSync(relativeMigrations)) {
|
||||
return relativeMigrations
|
||||
}
|
||||
|
||||
return srcDir
|
||||
}
|
||||
@@ -2,12 +2,12 @@ import type { FindOneArgs, PayloadRequest, SanitizedCollectionConfig, TypeWithID
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export async function findOne<T extends TypeWithID>(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where }: FindOneArgs,
|
||||
): Promise<T> {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
@@ -3,12 +3,12 @@ import type { FindVersions, PayloadRequest, SanitizedCollectionConfig } from 'pa
|
||||
import { buildVersionCollectionFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
|
||||
export const findVersions: FindVersions = async function findVersions(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{
|
||||
collection,
|
||||
limit,
|
||||
37
packages/drizzle/src/index.ts
Normal file
37
packages/drizzle/src/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export { count } from './count.js'
|
||||
export { create } from './create.js'
|
||||
export { createGlobal } from './createGlobal.js'
|
||||
export { createGlobalVersion } from './createGlobalVersion.js'
|
||||
export { createTableName } from './createTableName.js'
|
||||
export { createVersion } from './createVersion.js'
|
||||
export { deleteMany } from './deleteMany.js'
|
||||
export { deleteOne } from './deleteOne.js'
|
||||
export { deleteVersions } from './deleteVersions.js'
|
||||
export { destroy } from './destroy.js'
|
||||
export { find } from './find.js'
|
||||
export { chainMethods } from './find/chainMethods.js'
|
||||
export { findGlobal } from './findGlobal.js'
|
||||
export { findGlobalVersions } from './findGlobalVersions.js'
|
||||
export { findMigrationDir } from './findMigrationDir.js'
|
||||
export { findOne } from './findOne.js'
|
||||
export { findVersions } from './findVersions.js'
|
||||
export { migrate } from './migrate.js'
|
||||
export { migrateDown } from './migrateDown.js'
|
||||
export { migrateFresh } from './migrateFresh.js'
|
||||
export { migrateRefresh } from './migrateRefresh.js'
|
||||
export { migrateReset } from './migrateReset.js'
|
||||
export { migrateStatus } from './migrateStatus.js'
|
||||
export { operatorMap } from './queries/operatorMap.js'
|
||||
export type { Operators } from './queries/operatorMap.js'
|
||||
export { queryDrafts } from './queryDrafts.js'
|
||||
export { beginTransaction } from './transactions/beginTransaction.js'
|
||||
export { commitTransaction } from './transactions/commitTransaction.js'
|
||||
export { rollbackTransaction } from './transactions/rollbackTransaction.js'
|
||||
export { updateOne } from './update.js'
|
||||
export { updateGlobal } from './updateGlobal.js'
|
||||
export { updateGlobalVersion } from './updateGlobalVersion.js'
|
||||
export { updateVersion } from './updateVersion.js'
|
||||
export { upsertRow } from './upsertRow/index.js'
|
||||
export { hasLocalesTable } from './utilities/hasLocalesTable.js'
|
||||
export { pushDevSchema } from './utilities/pushDevSchema.js'
|
||||
export { validateExistingBlockIsIdentical } from './utilities/validateExistingBlockIsIdentical.js'
|
||||
@@ -1,21 +1,15 @@
|
||||
/* eslint-disable no-restricted-syntax, no-await-in-loop */
|
||||
import type { Payload } from 'payload'
|
||||
import type { PayloadRequest } from 'payload'
|
||||
import type { Migration } from 'payload'
|
||||
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
|
||||
import { createRequire } from 'module'
|
||||
import { commitTransaction, initTransaction, killTransaction, readMigrationFiles } from 'payload'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter, Migration } from './types.js'
|
||||
|
||||
import { createMigrationTable } from './utilities/createMigrationTable.js'
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
export async function migrate(this: DrizzleAdapter): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
|
||||
@@ -27,7 +21,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
let latestBatch = 0
|
||||
let migrationsInDB = []
|
||||
|
||||
const hasMigrationTable = await migrationTableExists(this.drizzle)
|
||||
const hasMigrationTable = await migrationTableExists(this)
|
||||
|
||||
if (hasMigrationTable) {
|
||||
;({ docs: migrationsInDB } = await payload.find({
|
||||
@@ -38,8 +32,6 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
if (Number(migrationsInDB?.[0]?.batch) > 0) {
|
||||
latestBatch = Number(migrationsInDB[0]?.batch)
|
||||
}
|
||||
} else {
|
||||
await createMigrationTable(this)
|
||||
}
|
||||
|
||||
if (migrationsInDB.find((m) => m.batch === -1)) {
|
||||
@@ -72,7 +64,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
|
||||
// If already ran, skip
|
||||
if (alreadyRan) {
|
||||
continue // eslint-disable-line no-continue
|
||||
continue
|
||||
}
|
||||
|
||||
await runMigrationFile(payload, migration, newBatch)
|
||||
@@ -80,19 +72,20 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
}
|
||||
|
||||
async function runMigrationFile(payload: Payload, migration: Migration, batch: number) {
|
||||
const { generateDrizzleJson } = require('drizzle-kit/payload')
|
||||
|
||||
const db = payload.db as DrizzleAdapter
|
||||
const { generateDrizzleJson } = db.requireDrizzleKit()
|
||||
const start = Date.now()
|
||||
const req = { payload } as PayloadRequest
|
||||
const adapter = payload.db as DrizzleAdapter
|
||||
|
||||
payload.logger.info({ msg: `Migrating: ${migration.name}` })
|
||||
|
||||
const pgAdapter = payload.db
|
||||
const drizzleJSON = generateDrizzleJson(pgAdapter.schema)
|
||||
const drizzleJSON = await generateDrizzleJson({ schema: adapter.schema })
|
||||
|
||||
try {
|
||||
await initTransaction(req)
|
||||
await migration.up({ payload, req })
|
||||
const db = adapter?.sessions[await req.transactionID]?.db || adapter.drizzle
|
||||
await migration.up({ db, payload, req })
|
||||
payload.logger.info({ msg: `Migrated: ${migration.name} (${Date.now() - start}ms)` })
|
||||
await payload.create({
|
||||
collection: 'payload-migrations',
|
||||
@@ -9,12 +9,12 @@ import {
|
||||
readMigrationFiles,
|
||||
} from 'payload'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
|
||||
export async function migrateDown(this: PostgresAdapter): Promise<void> {
|
||||
export async function migrateDown(this: DrizzleAdapter): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function migrateDown(this: PostgresAdapter): Promise<void> {
|
||||
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
|
||||
})
|
||||
|
||||
const tableExists = await migrationTableExists(this.drizzle)
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
await payload.delete({
|
||||
id: migration.id,
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { PayloadRequest } from 'payload'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { commitTransaction, initTransaction, killTransaction, readMigrationFiles } from 'payload'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter, Migration } from './types.js'
|
||||
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
|
||||
@@ -12,7 +11,7 @@ import { parseError } from './utilities/parseError.js'
|
||||
* Drop the current database and run all migrate up functions
|
||||
*/
|
||||
export async function migrateFresh(
|
||||
this: PostgresAdapter,
|
||||
this: DrizzleAdapter,
|
||||
{ forceAcceptWarning = false },
|
||||
): Promise<void> {
|
||||
const { payload } = this
|
||||
@@ -41,12 +40,9 @@ export async function migrateFresh(
|
||||
msg: `Dropping database.`,
|
||||
})
|
||||
|
||||
await this.drizzle.execute(
|
||||
sql.raw(`drop schema ${this.schemaName || 'public'} cascade;
|
||||
create schema ${this.schemaName || 'public'};`),
|
||||
)
|
||||
await this.dropDatabase({ adapter: this })
|
||||
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
const migrationFiles = (await readMigrationFiles({ payload })) as Migration[]
|
||||
payload.logger.debug({
|
||||
msg: `Found ${migrationFiles.length} migration files.`,
|
||||
})
|
||||
@@ -58,7 +54,9 @@ export async function migrateFresh(
|
||||
try {
|
||||
const start = Date.now()
|
||||
await initTransaction(req)
|
||||
await migration.up({ payload, req })
|
||||
const adapter = payload.db as DrizzleAdapter
|
||||
const db = adapter?.sessions[await req.transactionID]?.db || adapter.drizzle
|
||||
await migration.up({ db, payload, req })
|
||||
await payload.create({
|
||||
collection: 'payload-migrations',
|
||||
data: {
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
readMigrationFiles,
|
||||
} from 'payload'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
@@ -17,7 +17,7 @@ import { parseError } from './utilities/parseError.js'
|
||||
/**
|
||||
* Run all migration down functions before running up
|
||||
*/
|
||||
export async function migrateRefresh(this: PostgresAdapter) {
|
||||
export async function migrateRefresh(this: DrizzleAdapter) {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function migrateRefresh(this: PostgresAdapter) {
|
||||
msg: `Migrated down: ${migration.name} (${Date.now() - start}ms)`,
|
||||
})
|
||||
|
||||
const tableExists = await migrationTableExists(this.drizzle)
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
await payload.delete({
|
||||
collection: 'payload-migrations',
|
||||
@@ -9,14 +9,14 @@ import {
|
||||
readMigrationFiles,
|
||||
} from 'payload'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
|
||||
/**
|
||||
* Run all migrate down functions
|
||||
*/
|
||||
export async function migrateReset(this: PostgresAdapter): Promise<void> {
|
||||
export async function migrateReset(this: DrizzleAdapter): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function migrateReset(this: PostgresAdapter): Promise<void> {
|
||||
msg: `Migrated down: ${migrationFile.name} (${Date.now() - start}ms)`,
|
||||
})
|
||||
|
||||
const tableExists = await migrationTableExists(this.drizzle)
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
await payload.delete({
|
||||
id: migration.id,
|
||||
@@ -71,7 +71,7 @@ export async function migrateReset(this: PostgresAdapter): Promise<void> {
|
||||
|
||||
// Delete dev migration
|
||||
|
||||
const tableExists = await migrationTableExists(this.drizzle)
|
||||
const tableExists = await migrationTableExists(this)
|
||||
if (tableExists) {
|
||||
try {
|
||||
await payload.delete({
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Table } from 'console-table-printer'
|
||||
import { getMigrations, readMigrationFiles } from 'payload'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
|
||||
export async function migrateStatus(this: PostgresAdapter): Promise<void> {
|
||||
export async function migrateStatus(this: DrizzleAdapter): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function migrateStatus(this: PostgresAdapter): Promise<void> {
|
||||
})
|
||||
|
||||
let existingMigrations = []
|
||||
const hasMigrationTable = await migrationTableExists(this.drizzle)
|
||||
const hasMigrationTable = await migrationTableExists(this)
|
||||
|
||||
if (hasMigrationTable) {
|
||||
;({ existingMigrations } = await getMigrations({ payload }))
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { Field, Where } from 'payload'
|
||||
|
||||
import type { GenericColumn, PostgresAdapter } from '../types.js'
|
||||
import type { DrizzleAdapter, GenericColumn } from '../types.js'
|
||||
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
||||
|
||||
import { parseParams } from './parseParams.js'
|
||||
@@ -15,7 +15,7 @@ export async function buildAndOrConditions({
|
||||
tableName,
|
||||
where,
|
||||
}: {
|
||||
adapter: PostgresAdapter
|
||||
adapter: DrizzleAdapter
|
||||
collectionSlug?: string
|
||||
fields: Field[]
|
||||
globalSlug?: string
|
||||
@@ -4,20 +4,18 @@ import type { Field, Where } from 'payload'
|
||||
|
||||
import { asc, desc } from 'drizzle-orm'
|
||||
|
||||
import type { GenericColumn, GenericTable, PostgresAdapter } from '../types.js'
|
||||
import type { DrizzleAdapter, GenericColumn, GenericTable } from '../types.js'
|
||||
|
||||
import { getTableColumnFromPath } from './getTableColumnFromPath.js'
|
||||
import { parseParams } from './parseParams.js'
|
||||
|
||||
export type BuildQueryJoins = Record<string, SQL>
|
||||
|
||||
export type BuildQueryJoinAliases = {
|
||||
condition: SQL
|
||||
table: GenericTable | PgTableWithColumns<any>
|
||||
}[]
|
||||
|
||||
type BuildQueryArgs = {
|
||||
adapter: PostgresAdapter
|
||||
adapter: DrizzleAdapter
|
||||
fields: Field[]
|
||||
locale?: string
|
||||
sort?: string
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user