feat(db-postgres): configurable custom schema to use (#5047)

* feat(db-postgres): configurable custom schema to use

* test(db-postgres): use public schema

* chore(db-postgres): simplify drop schema

* chore: add postgres-custom-schema test to ci

* chore: add custom schema to postgres ci

* chore(db-postgres): custom schema in migrate

* chore: ci postgres wait condition
This commit is contained in:
Dan Ribbens
2024-02-23 12:48:06 -05:00
committed by GitHub
parent ceca5c4e97
commit e8f2ca484e
15 changed files with 116 additions and 69 deletions

View File

@@ -3,7 +3,7 @@ import type { Connect } from 'payload/database'
import { eq, sql } from 'drizzle-orm'
import { drizzle } from 'drizzle-orm/node-postgres'
import { numeric, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'
import { numeric, timestamp, varchar } from 'drizzle-orm/pg-core'
import { Pool } from 'pg'
import prompts from 'prompts'
@@ -61,9 +61,13 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
this.drizzle = drizzle(this.pool, { logger, schema: this.schema })
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
this.payload.logger.info('---- DROPPING TABLES ----')
await this.drizzle.execute(sql`drop schema public cascade;
create schema public;`)
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'};
`),
)
this.payload.logger.info('---- DROPPED TABLES ----')
}
} catch (err) {
@@ -120,7 +124,7 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
await apply()
// Migration table def in order to use query using drizzle
const migrationsSchema = pgTable('payload_migrations', {
const migrationsSchema = this.pgSchema.table('payload_migrations', {
name: varchar('name'),
batch: numeric('batch'),
created_at: timestamp('created_at'),

View File

@@ -52,11 +52,13 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
fieldConstraints: {},
idType,
logger: args.logger,
pgSchema: undefined,
pool: undefined,
poolOptions: args.pool,
push: args.push,
relations: {},
schema: {},
schemaName: args.schemaName,
sessions: {},
tables: {},

View File

@@ -2,7 +2,7 @@
import type { Init } from 'payload/database'
import type { SanitizedCollectionConfig } from 'payload/types'
import { pgEnum } from 'drizzle-orm/pg-core'
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
@@ -11,6 +11,12 @@ import type { PostgresAdapter } from './types'
import { buildTable } from './schema/build'
export const init: Init = async function init(this: PostgresAdapter) {
if (this.schemaName) {
this.pgSchema = pgSchema(this.schemaName)
} else {
this.pgSchema = { table: pgTable }
}
if (this.payload.config.localization) {
this.enums.enum__locales = pgEnum(
'_locales',

View File

@@ -39,7 +39,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
latestBatch = Number(migrationsInDB[0]?.batch)
}
} else {
await createMigrationTable(this.drizzle)
await createMigrationTable(this)
}
if (migrationsInDB.find((m) => m.batch === -1)) {

View File

@@ -44,8 +44,10 @@ export async function migrateFresh(
msg: `Dropping database.`,
})
await this.drizzle.execute(sql`drop schema public cascade;
create schema public;`)
await this.drizzle.execute(
sql.raw(`drop schema ${this.schemaName || 'public'} cascade;
create schema ${this.schemaName || 'public'};`),
)
const migrationFiles = await readMigrationFiles({ payload })
payload.logger.debug({

View File

@@ -1,4 +1,5 @@
import type { SQL } from 'drizzle-orm'
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
import type { Field, Where } from 'payload/types'
import { asc, desc } from 'drizzle-orm'
@@ -12,7 +13,7 @@ export type BuildQueryJoins = Record<string, SQL>
export type BuildQueryJoinAliases = {
condition: SQL
table: GenericTable
table: GenericTable | PgTableWithColumns<any>
}[]
type BuildQueryArgs = {

View File

@@ -1,6 +1,7 @@
/* eslint-disable no-param-reassign */
import type { SQL } from 'drizzle-orm'
import type { Field, FieldAffectingData, NumberField, TabAsField, TextField } from 'payload/types'
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
import type { Field, FieldAffectingData, NumberField, TabAsField, TextField } from 'payload/types'
import { and, eq, like, sql } from 'drizzle-orm'
import { alias } from 'drizzle-orm/pg-core'
@@ -15,7 +16,7 @@ import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
type Constraint = {
columnName: string
table: GenericTable
table: GenericTable | PgTableWithColumns<any>
value: unknown
}
@@ -26,12 +27,12 @@ type TableColumn = {
getNotNullColumnByValue?: (val: unknown) => string
pathSegments?: string[]
rawColumn?: SQL
table: GenericTable
table: GenericTable | PgTableWithColumns<any>
}
type Args = {
adapter: PostgresAdapter
aliasTable?: GenericTable
aliasTable?: GenericTable | PgTableWithColumns<any>
collectionPath: string
columnPrefix?: string
constraintPath?: string

View File

@@ -1,19 +1,15 @@
/* eslint-disable no-param-reassign */
import type { Relation } from 'drizzle-orm'
import type { IndexBuilder, PgColumnBuilder, UniqueConstraintBuilder } from 'drizzle-orm/pg-core'
import type {
IndexBuilder,
PgColumnBuilder,
PgTableWithColumns,
UniqueConstraintBuilder,
} from 'drizzle-orm/pg-core'
import type { Field } from 'payload/types'
import { relations } from 'drizzle-orm'
import {
index,
integer,
numeric,
pgTable,
serial,
timestamp,
unique,
varchar,
} from 'drizzle-orm/pg-core'
import { index, integer, numeric, serial, timestamp, unique, varchar } from 'drizzle-orm/pg-core'
import { fieldAffectsData } from 'payload/types'
import toSnakeCase from 'to-snake-case'
@@ -77,14 +73,14 @@ export const buildTable = ({
const localesColumns: Record<string, PgColumnBuilder> = {}
const localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
let localesTable: GenericTable
let textsTable: GenericTable
let numbersTable: GenericTable
let localesTable: GenericTable | PgTableWithColumns<any>
let textsTable: GenericTable | PgTableWithColumns<any>
let numbersTable: GenericTable | PgTableWithColumns<any>
// Relationships to the base collection
const relationships: Set<string> = rootRelationships || new Set()
let relationshipsTable: GenericTable
let relationshipsTable: GenericTable | PgTableWithColumns<any>
// Drizzle relations
const relationsToBuild: Map<string, string> = new Map()
@@ -136,7 +132,7 @@ export const buildTable = ({
.notNull()
}
const table = pgTable(tableName, columns, (cols) => {
const table = adapter.pgSchema.table(tableName, columns, (cols) => {
const extraConfig = Object.entries(baseExtraConfig).reduce((config, [key, func]) => {
config[key] = func(cols)
return config
@@ -158,7 +154,7 @@ export const buildTable = ({
.references(() => table.id, { onDelete: 'cascade' })
.notNull()
localesTable = pgTable(localeTableName, localesColumns, (cols) => {
localesTable = adapter.pgSchema.table(localeTableName, localesColumns, (cols) => {
return Object.entries(localesIndexes).reduce(
(acc, [colName, func]) => {
acc[colName] = func(cols)
@@ -201,7 +197,7 @@ export const buildTable = ({
columns.locale = adapter.enums.enum__locales('locale')
}
textsTable = pgTable(textsTableName, columns, (cols) => {
textsTable = adapter.pgSchema.table(textsTableName, columns, (cols) => {
const indexes: Record<string, IndexBuilder> = {
orderParentIdx: index(`${textsTableName}_order_parent_idx`).on(cols.order, cols.parent),
}
@@ -245,7 +241,7 @@ export const buildTable = ({
columns.locale = adapter.enums.enum__locales('locale')
}
numbersTable = pgTable(numbersTableName, columns, (cols) => {
numbersTable = adapter.pgSchema.table(numbersTableName, columns, (cols) => {
const indexes: Record<string, IndexBuilder> = {
orderParentIdx: index(`${numbersTableName}_order_parent_idx`).on(cols.order, cols.parent),
}
@@ -307,19 +303,23 @@ export const buildTable = ({
const relationshipsTableName = `${tableName}_rels`
relationshipsTable = pgTable(relationshipsTableName, relationshipColumns, (cols) => {
const result: Record<string, unknown> = {
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
parentIdx: index(`${relationshipsTableName}_parent_idx`).on(cols.parent),
pathIdx: index(`${relationshipsTableName}_path_idx`).on(cols.path),
}
relationshipsTable = adapter.pgSchema.table(
relationshipsTableName,
relationshipColumns,
(cols) => {
const result: Record<string, unknown> = {
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
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)
}
if (hasLocalizedRelationshipField) {
result.localeIdx = index(`${relationshipsTableName}_locale_idx`).on(cols.locale)
}
return result
})
return result
},
)
adapter.tables[relationshipsTableName] = relationshipsTable

View File

@@ -7,7 +7,14 @@ import type {
Relations,
} from 'drizzle-orm'
import type { NodePgDatabase, NodePgQueryResultHKT } from 'drizzle-orm/node-postgres'
import type { PgColumn, PgEnum, PgTableWithColumns, PgTransaction } from 'drizzle-orm/pg-core'
import type {
PgColumn,
PgEnum,
PgSchema,
PgTableWithColumns,
PgTransaction,
} from 'drizzle-orm/pg-core'
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
import type { Payload } from 'payload'
import type { BaseDatabaseAdapter } from 'payload/database'
import type { PayloadRequest } from 'payload/types'
@@ -21,6 +28,7 @@ export type Args = {
migrationDir?: string
pool: PoolConfig
push?: boolean
schemaName?: string
}
export type GenericColumn = PgColumn<
@@ -59,11 +67,13 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
fieldConstraints: Record<string, Record<string, string>>
idType: Args['idType']
logger: DrizzleConfig['logger']
pgSchema?: { table: PgTableFn } | PgSchema
pool: Pool
poolOptions: Args['pool']
push: boolean
relations: Record<string, GenericRelation>
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
schemaName?: Args['schemaName']
sessions: {
[id: string]: {
db: DrizzleTransaction
@@ -71,7 +81,7 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
resolve: () => Promise<void>
}
}
tables: Record<string, GenericTable>
tables: Record<string, GenericTable | PgTableWithColumns<any>>
}
export type IDType = 'integer' | 'numeric' | 'uuid' | 'varchar'

View File

@@ -1,13 +1,17 @@
import { sql } from 'drizzle-orm'
import type { DrizzleDB } from '../types'
import type { PostgresAdapter } from '../types'
export const createMigrationTable = async (db: DrizzleDB): Promise<void> => {
await db.execute(sql`CREATE TABLE IF NOT EXISTS "payload_migrations" (
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
);`)
);`),
)
}