diff --git a/packages/drizzle/src/find/traverseFields.ts b/packages/drizzle/src/find/traverseFields.ts index 5d1c1bd170..d6ad716239 100644 --- a/packages/drizzle/src/find/traverseFields.ts +++ b/packages/drizzle/src/find/traverseFields.ts @@ -22,6 +22,7 @@ import type { Result } from './buildFindManyArgs.js' import buildQuery from '../queries/buildQuery.js' import { getTableAlias } from '../queries/getTableAlias.js' import { operatorMap } from '../queries/operatorMap.js' +import { getArrayRelationName } from '../utilities/getArrayRelationName.js' import { getNameFromDrizzleTable } from '../utilities/getNameFromDrizzleTable.js' import { jsonAggBuildObject } from '../utilities/json.js' import { rawConstraint } from '../utilities/rawConstraint.js' @@ -196,7 +197,12 @@ export const traverseFields = ({ } } - const relationName = field.dbName ? `_${arrayTableName}` : `${path}${field.name}` + const relationName = getArrayRelationName({ + field, + path: `${path}${field.name}`, + tableName: arrayTableName, + }) + currentArgs.with[relationName] = withArray traverseFields({ diff --git a/packages/drizzle/src/schema/traverseFields.ts b/packages/drizzle/src/schema/traverseFields.ts index a10c09e704..79136ec2f0 100644 --- a/packages/drizzle/src/schema/traverseFields.ts +++ b/packages/drizzle/src/schema/traverseFields.ts @@ -23,6 +23,7 @@ import type { import { createTableName } from '../createTableName.js' import { buildIndexName } from '../utilities/buildIndexName.js' +import { getArrayRelationName } from '../utilities/getArrayRelationName.js' import { hasLocalesTable } from '../utilities/hasLocalesTable.js' import { validateExistingBlockIsIdentical } from '../utilities/validateExistingBlockIsIdentical.js' import { buildTable } from './build.js' @@ -288,7 +289,11 @@ export const traverseFields = ({ } } - const relationName = field.dbName ? `_${arrayTableName}` : fieldName + const relationName = getArrayRelationName({ + field, + path: fieldName, + tableName: arrayTableName, + }) relationsToBuild.set(relationName, { type: 'many', diff --git a/packages/drizzle/src/transform/read/traverseFields.ts b/packages/drizzle/src/transform/read/traverseFields.ts index 43fbf0539c..b063d7f65c 100644 --- a/packages/drizzle/src/transform/read/traverseFields.ts +++ b/packages/drizzle/src/transform/read/traverseFields.ts @@ -6,6 +6,7 @@ import toSnakeCase from 'to-snake-case' import type { DrizzleAdapter } from '../../types.js' import type { BlocksMap } from '../../utilities/createBlocksMap.js' +import { getArrayRelationName } from '../../utilities/getArrayRelationName.js' import { transformHasManyNumber } from './hasManyNumber.js' import { transformHasManyText } from './hasManyText.js' import { transformRelationship } from './relationship.js' @@ -121,9 +122,7 @@ export const traverseFields = >({ `${currentTableName}_${tablePath}${toSnakeCase(field.name)}`, ) - if (field.dbName) { - fieldData = table[`_${arrayTableName}`] - } + fieldData = table[getArrayRelationName({ field, path: fieldName, tableName: arrayTableName })] if (Array.isArray(fieldData)) { if (isLocalized) { diff --git a/packages/drizzle/src/utilities/getArrayRelationName.ts b/packages/drizzle/src/utilities/getArrayRelationName.ts new file mode 100644 index 0000000000..4127d26a4a --- /dev/null +++ b/packages/drizzle/src/utilities/getArrayRelationName.ts @@ -0,0 +1,17 @@ +import type { ArrayField } from 'payload' + +export const getArrayRelationName = ({ + field, + path, + tableName, +}: { + field: ArrayField + path: string + tableName: string +}) => { + if (field.dbName && path.length > 63) { + return `_${tableName}` + } + + return path +} diff --git a/test/database/config.ts b/test/database/config.ts index 806872ee68..a25e358a0c 100644 --- a/test/database/config.ts +++ b/test/database/config.ts @@ -700,6 +700,55 @@ export default buildConfigWithDefaults({ }, ], globals: [ + { + slug: 'header', + fields: [ + { + name: 'itemsLvl1', + type: 'array', + dbName: 'header_items_lvl1', + fields: [ + { + name: 'label', + type: 'text', + }, + { + name: 'itemsLvl2', + type: 'array', + dbName: 'header_items_lvl2', + fields: [ + { + name: 'label', + type: 'text', + }, + { + name: 'itemsLvl3', + type: 'array', + dbName: 'header_items_lvl3', + fields: [ + { + name: 'label', + type: 'text', + }, + { + name: 'itemsLvl4', + type: 'array', + dbName: 'header_items_lvl4', + fields: [ + { + name: 'label', + type: 'text', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, { slug: 'global', dbName: 'customGlobal', diff --git a/test/database/int.spec.ts b/test/database/int.spec.ts index efad1d825d..a7dbf81032 100644 --- a/test/database/int.spec.ts +++ b/test/database/int.spec.ts @@ -2452,6 +2452,17 @@ describe('database', () => { expect(res.docs[0].id).toBe(customID.id) }) + it('deep nested arrays', async () => { + await payload.updateGlobal({ + slug: 'header', + data: { itemsLvl1: [{ itemsLvl2: [{ itemsLvl3: [{ itemsLvl4: [{ label: 'label' }] }] }] }] }, + }) + + const header = await payload.findGlobal({ slug: 'header' }) + + expect(header.itemsLvl1[0]?.itemsLvl2[0]?.itemsLvl3[0]?.itemsLvl4[0]?.label).toBe('label') + }) + it('should count with a query that contains subqueries', async () => { const category = await payload.create({ collection: 'categories', diff --git a/test/database/payload-types.ts b/test/database/payload-types.ts index 5cdc1faac9..88bc6d131d 100644 --- a/test/database/payload-types.ts +++ b/test/database/payload-types.ts @@ -115,12 +115,14 @@ export interface Config { defaultIDType: string; }; globals: { + header: Header; global: Global; 'global-2': Global2; 'global-3': Global3; 'virtual-relation-global': VirtualRelationGlobal; }; globalsSelect: { + header: HeaderSelect | HeaderSelect; global: GlobalSelect | GlobalSelect; 'global-2': Global2Select | Global2Select; 'global-3': Global3Select | Global3Select; @@ -977,6 +979,39 @@ export interface PayloadMigrationsSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "header". + */ +export interface Header { + id: string; + itemsLvl1?: + | { + label?: string | null; + itemsLvl2?: + | { + label?: string | null; + itemsLvl3?: + | { + label?: string | null; + itemsLvl4?: + | { + label?: string | null; + id?: string | null; + }[] + | null; + id?: string | null; + }[] + | null; + id?: string | null; + }[] + | null; + id?: string | null; + }[] + | null; + updatedAt?: string | null; + createdAt?: string | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "global". @@ -1018,6 +1053,39 @@ export interface VirtualRelationGlobal { updatedAt?: string | null; createdAt?: string | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "header_select". + */ +export interface HeaderSelect { + itemsLvl1?: + | T + | { + label?: T; + itemsLvl2?: + | T + | { + label?: T; + itemsLvl3?: + | T + | { + label?: T; + itemsLvl4?: + | T + | { + label?: T; + id?: T; + }; + id?: T; + }; + id?: T; + }; + id?: T; + }; + updatedAt?: T; + createdAt?: T; + globalType?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "global_select". diff --git a/test/database/up-down-migration/migrations/20250328_185055.json b/test/database/up-down-migration/migrations/20250328_185055.json deleted file mode 100644 index 7ef134f6aa..0000000000 --- a/test/database/up-down-migration/migrations/20250328_185055.json +++ /dev/null @@ -1,463 +0,0 @@ -{ - "version": "6", - "dialect": "sqlite", - "tables": { - "users": { - "name": "users", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "updated_at": { - "name": "updated_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - }, - "created_at": { - "name": "created_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "reset_password_token": { - "name": "reset_password_token", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "reset_password_expiration": { - "name": "reset_password_expiration", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "salt": { - "name": "salt", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "hash": { - "name": "hash", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "login_attempts": { - "name": "login_attempts", - "type": "numeric", - "primaryKey": false, - "notNull": false, - "autoincrement": false, - "default": 0 - }, - "lock_until": { - "name": "lock_until", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": { - "users_updated_at_idx": { - "name": "users_updated_at_idx", - "columns": ["updated_at"], - "isUnique": false - }, - "users_created_at_idx": { - "name": "users_created_at_idx", - "columns": ["created_at"], - "isUnique": false - }, - "users_email_idx": { - "name": "users_email_idx", - "columns": ["email"], - "isUnique": true - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "payload_locked_documents": { - "name": "payload_locked_documents", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "global_slug": { - "name": "global_slug", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "updated_at": { - "name": "updated_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - }, - "created_at": { - "name": "created_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - } - }, - "indexes": { - "payload_locked_documents_global_slug_idx": { - "name": "payload_locked_documents_global_slug_idx", - "columns": ["global_slug"], - "isUnique": false - }, - "payload_locked_documents_updated_at_idx": { - "name": "payload_locked_documents_updated_at_idx", - "columns": ["updated_at"], - "isUnique": false - }, - "payload_locked_documents_created_at_idx": { - "name": "payload_locked_documents_created_at_idx", - "columns": ["created_at"], - "isUnique": false - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "payload_locked_documents_rels": { - "name": "payload_locked_documents_rels", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "order": { - "name": "order", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "parent_id": { - "name": "parent_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "path": { - "name": "path", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "users_id": { - "name": "users_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": { - "payload_locked_documents_rels_order_idx": { - "name": "payload_locked_documents_rels_order_idx", - "columns": ["order"], - "isUnique": false - }, - "payload_locked_documents_rels_parent_idx": { - "name": "payload_locked_documents_rels_parent_idx", - "columns": ["parent_id"], - "isUnique": false - }, - "payload_locked_documents_rels_path_idx": { - "name": "payload_locked_documents_rels_path_idx", - "columns": ["path"], - "isUnique": false - }, - "payload_locked_documents_rels_users_id_idx": { - "name": "payload_locked_documents_rels_users_id_idx", - "columns": ["users_id"], - "isUnique": false - } - }, - "foreignKeys": { - "payload_locked_documents_rels_parent_fk": { - "name": "payload_locked_documents_rels_parent_fk", - "tableFrom": "payload_locked_documents_rels", - "tableTo": "payload_locked_documents", - "columnsFrom": ["parent_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "payload_locked_documents_rels_users_fk": { - "name": "payload_locked_documents_rels_users_fk", - "tableFrom": "payload_locked_documents_rels", - "tableTo": "users", - "columnsFrom": ["users_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "payload_preferences": { - "name": "payload_preferences", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "key": { - "name": "key", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "value": { - "name": "value", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "updated_at": { - "name": "updated_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - }, - "created_at": { - "name": "created_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - } - }, - "indexes": { - "payload_preferences_key_idx": { - "name": "payload_preferences_key_idx", - "columns": ["key"], - "isUnique": false - }, - "payload_preferences_updated_at_idx": { - "name": "payload_preferences_updated_at_idx", - "columns": ["updated_at"], - "isUnique": false - }, - "payload_preferences_created_at_idx": { - "name": "payload_preferences_created_at_idx", - "columns": ["created_at"], - "isUnique": false - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "payload_preferences_rels": { - "name": "payload_preferences_rels", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": false - }, - "order": { - "name": "order", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "parent_id": { - "name": "parent_id", - "type": "integer", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "path": { - "name": "path", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false - }, - "users_id": { - "name": "users_id", - "type": "integer", - "primaryKey": false, - "notNull": false, - "autoincrement": false - } - }, - "indexes": { - "payload_preferences_rels_order_idx": { - "name": "payload_preferences_rels_order_idx", - "columns": ["order"], - "isUnique": false - }, - "payload_preferences_rels_parent_idx": { - "name": "payload_preferences_rels_parent_idx", - "columns": ["parent_id"], - "isUnique": false - }, - "payload_preferences_rels_path_idx": { - "name": "payload_preferences_rels_path_idx", - "columns": ["path"], - "isUnique": false - }, - "payload_preferences_rels_users_id_idx": { - "name": "payload_preferences_rels_users_id_idx", - "columns": ["users_id"], - "isUnique": false - } - }, - "foreignKeys": { - "payload_preferences_rels_parent_fk": { - "name": "payload_preferences_rels_parent_fk", - "tableFrom": "payload_preferences_rels", - "tableTo": "payload_preferences", - "columnsFrom": ["parent_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "payload_preferences_rels_users_fk": { - "name": "payload_preferences_rels_users_fk", - "tableFrom": "payload_preferences_rels", - "tableTo": "users", - "columnsFrom": ["users_id"], - "columnsTo": ["id"], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - }, - "payload_migrations": { - "name": "payload_migrations", - "columns": { - "id": { - "name": "id", - "type": "integer", - "primaryKey": true, - "notNull": true, - "autoincrement": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "batch": { - "name": "batch", - "type": "numeric", - "primaryKey": false, - "notNull": false, - "autoincrement": false - }, - "updated_at": { - "name": "updated_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - }, - "created_at": { - "name": "created_at", - "type": "text", - "primaryKey": false, - "notNull": true, - "autoincrement": false, - "default": "(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))" - } - }, - "indexes": { - "payload_migrations_updated_at_idx": { - "name": "payload_migrations_updated_at_idx", - "columns": ["updated_at"], - "isUnique": false - }, - "payload_migrations_created_at_idx": { - "name": "payload_migrations_created_at_idx", - "columns": ["created_at"], - "isUnique": false - } - }, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "checkConstraints": {} - } - }, - "views": {}, - "enums": {}, - "_meta": { - "tables": {}, - "columns": {} - }, - "internal": { - "indexes": {} - }, - "id": "2b2f5008-c761-40d0-a858-67d6b4233615", - "prevId": "00000000-0000-0000-0000-000000000000" -} diff --git a/test/database/up-down-migration/migrations/20250328_185055.ts b/test/database/up-down-migration/migrations/20250328_185055.ts deleted file mode 100644 index 3e6ffda86a..0000000000 --- a/test/database/up-down-migration/migrations/20250328_185055.ts +++ /dev/null @@ -1,122 +0,0 @@ -import type { MigrateDownArgs, MigrateUpArgs } from '@payloadcms/db-sqlite' - -import { sql } from '@payloadcms/db-sqlite' - -export async function up({ db, payload, req }: MigrateUpArgs): Promise { - await db.run(sql`CREATE TABLE \`users\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL, - \`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL, - \`email\` text NOT NULL, - \`reset_password_token\` text, - \`reset_password_expiration\` text, - \`salt\` text, - \`hash\` text, - \`login_attempts\` numeric DEFAULT 0, - \`lock_until\` text - ); - `) - await db.run(sql`CREATE INDEX \`users_updated_at_idx\` ON \`users\` (\`updated_at\`);`) - await db.run(sql`CREATE INDEX \`users_created_at_idx\` ON \`users\` (\`created_at\`);`) - await db.run(sql`CREATE UNIQUE INDEX \`users_email_idx\` ON \`users\` (\`email\`);`) - await db.run(sql`CREATE TABLE \`payload_locked_documents\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`global_slug\` text, - \`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL, - \`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL - ); - `) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_global_slug_idx\` ON \`payload_locked_documents\` (\`global_slug\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_updated_at_idx\` ON \`payload_locked_documents\` (\`updated_at\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_created_at_idx\` ON \`payload_locked_documents\` (\`created_at\`);`, - ) - await db.run(sql`CREATE TABLE \`payload_locked_documents_rels\` ( - \`id\` integer PRIMARY KEY NOT NULL, - \`order\` integer, - \`parent_id\` integer NOT NULL, - \`path\` text NOT NULL, - \`users_id\` integer, - FOREIGN KEY (\`parent_id\`) REFERENCES \`payload_locked_documents\`(\`id\`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (\`users_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE cascade - ); - `) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_rels_order_idx\` ON \`payload_locked_documents_rels\` (\`order\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_rels_parent_idx\` ON \`payload_locked_documents_rels\` (\`parent_id\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_rels_path_idx\` ON \`payload_locked_documents_rels\` (\`path\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_locked_documents_rels_users_id_idx\` ON \`payload_locked_documents_rels\` (\`users_id\`);`, - ) - await db.run(sql`CREATE TABLE \`payload_preferences\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`key\` text, - \`value\` text, - \`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL, - \`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL - ); - `) - await db.run( - sql`CREATE INDEX \`payload_preferences_key_idx\` ON \`payload_preferences\` (\`key\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_preferences_updated_at_idx\` ON \`payload_preferences\` (\`updated_at\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_preferences_created_at_idx\` ON \`payload_preferences\` (\`created_at\`);`, - ) - await db.run(sql`CREATE TABLE \`payload_preferences_rels\` ( - \`id\` integer PRIMARY KEY NOT NULL, - \`order\` integer, - \`parent_id\` integer NOT NULL, - \`path\` text NOT NULL, - \`users_id\` integer, - FOREIGN KEY (\`parent_id\`) REFERENCES \`payload_preferences\`(\`id\`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (\`users_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE cascade - ); - `) - await db.run( - sql`CREATE INDEX \`payload_preferences_rels_order_idx\` ON \`payload_preferences_rels\` (\`order\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_preferences_rels_parent_idx\` ON \`payload_preferences_rels\` (\`parent_id\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_preferences_rels_path_idx\` ON \`payload_preferences_rels\` (\`path\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_preferences_rels_users_id_idx\` ON \`payload_preferences_rels\` (\`users_id\`);`, - ) - await db.run(sql`CREATE TABLE \`payload_migrations\` ( - \`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - \`name\` text, - \`batch\` numeric, - \`updated_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL, - \`created_at\` text DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) NOT NULL - ); - `) - await db.run( - sql`CREATE INDEX \`payload_migrations_updated_at_idx\` ON \`payload_migrations\` (\`updated_at\`);`, - ) - await db.run( - sql`CREATE INDEX \`payload_migrations_created_at_idx\` ON \`payload_migrations\` (\`created_at\`);`, - ) -} - -export async function down({ db, payload, req }: MigrateDownArgs): Promise { - await db.run(sql`DROP TABLE \`users\`;`) - await db.run(sql`DROP TABLE \`payload_locked_documents\`;`) - await db.run(sql`DROP TABLE \`payload_locked_documents_rels\`;`) - await db.run(sql`DROP TABLE \`payload_preferences\`;`) - await db.run(sql`DROP TABLE \`payload_preferences_rels\`;`) - await db.run(sql`DROP TABLE \`payload_migrations\`;`) -} diff --git a/test/database/up-down-migration/migrations/20250428_121536.json b/test/database/up-down-migration/migrations/20250428_121536.json new file mode 100644 index 0000000000..739b1f0dc0 --- /dev/null +++ b/test/database/up-down-migration/migrations/20250428_121536.json @@ -0,0 +1,639 @@ +{ + "id": "36a35217-e468-4780-becb-9146c56e3e54", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_updated_at_idx": { + "name": "users_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_created_at_idx": { + "name": "users_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_locked_documents": { + "name": "payload_locked_documents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "global_slug": { + "name": "global_slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_locked_documents_global_slug_idx": { + "name": "payload_locked_documents_global_slug_idx", + "columns": [ + { + "expression": "global_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_updated_at_idx": { + "name": "payload_locked_documents_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_created_at_idx": { + "name": "payload_locked_documents_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_locked_documents_rels": { + "name": "payload_locked_documents_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "payload_locked_documents_rels_order_idx": { + "name": "payload_locked_documents_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_parent_idx": { + "name": "payload_locked_documents_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_path_idx": { + "name": "payload_locked_documents_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_locked_documents_rels_users_id_idx": { + "name": "payload_locked_documents_rels_users_id_idx", + "columns": [ + { + "expression": "users_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payload_locked_documents_rels_parent_fk": { + "name": "payload_locked_documents_rels_parent_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "payload_locked_documents", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_locked_documents_rels_users_fk": { + "name": "payload_locked_documents_rels_users_fk", + "tableFrom": "payload_locked_documents_rels", + "tableTo": "users", + "columnsFrom": ["users_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_preferences": { + "name": "payload_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_preferences_key_idx": { + "name": "payload_preferences_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_updated_at_idx": { + "name": "payload_preferences_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_created_at_idx": { + "name": "payload_preferences_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_preferences_rels": { + "name": "payload_preferences_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "payload_preferences_rels_order_idx": { + "name": "payload_preferences_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_parent_idx": { + "name": "payload_preferences_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_path_idx": { + "name": "payload_preferences_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_users_id_idx": { + "name": "payload_preferences_rels_users_id_idx", + "columns": [ + { + "expression": "users_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payload_preferences_rels_parent_fk": { + "name": "payload_preferences_rels_parent_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "payload_preferences", + "columnsFrom": ["parent_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_users_fk": { + "name": "payload_preferences_rels_users_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "users", + "columnsFrom": ["users_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.payload_migrations": { + "name": "payload_migrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "batch": { + "name": "batch", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_migrations_updated_at_idx": { + "name": "payload_migrations_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_migrations_created_at_idx": { + "name": "payload_migrations_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} diff --git a/test/database/up-down-migration/migrations/20250428_121536.ts b/test/database/up-down-migration/migrations/20250428_121536.ts new file mode 100644 index 0000000000..086047bb65 --- /dev/null +++ b/test/database/up-down-migration/migrations/20250428_121536.ts @@ -0,0 +1,112 @@ +import type { MigrateDownArgs, MigrateUpArgs} from '@payloadcms/db-postgres'; + +import { sql } from '@payloadcms/db-postgres' + +export async function up({ db, payload, req }: MigrateUpArgs): Promise { + await db.execute(sql` + CREATE TABLE IF NOT EXISTS "users" ( + "id" serial PRIMARY KEY NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "email" varchar NOT NULL, + "reset_password_token" varchar, + "reset_password_expiration" timestamp(3) with time zone, + "salt" varchar, + "hash" varchar, + "login_attempts" numeric DEFAULT 0, + "lock_until" timestamp(3) with time zone + ); + + CREATE TABLE IF NOT EXISTS "payload_locked_documents" ( + "id" serial PRIMARY KEY NOT NULL, + "global_slug" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE IF NOT EXISTS "payload_locked_documents_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "users_id" integer + ); + + CREATE TABLE IF NOT EXISTS "payload_preferences" ( + "id" serial PRIMARY KEY NOT NULL, + "key" varchar, + "value" jsonb, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE IF NOT EXISTS "payload_preferences_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "users_id" integer + ); + + CREATE TABLE IF NOT EXISTS "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 + ); + + DO $$ BEGIN + ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_locked_documents"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + DO $$ BEGIN + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; + EXCEPTION + WHEN duplicate_object THEN null; + END $$; + + CREATE INDEX IF NOT EXISTS "users_updated_at_idx" ON "users" USING btree ("updated_at"); + CREATE INDEX IF NOT EXISTS "users_created_at_idx" ON "users" USING btree ("created_at"); + CREATE UNIQUE INDEX IF NOT EXISTS "users_email_idx" ON "users" USING btree ("email"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_global_slug_idx" ON "payload_locked_documents" USING btree ("global_slug"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_updated_at_idx" ON "payload_locked_documents" USING btree ("updated_at"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_created_at_idx" ON "payload_locked_documents" USING btree ("created_at"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_order_idx" ON "payload_locked_documents_rels" USING btree ("order"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_parent_idx" ON "payload_locked_documents_rels" USING btree ("parent_id"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_path_idx" ON "payload_locked_documents_rels" USING btree ("path"); + CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_users_id_idx" ON "payload_locked_documents_rels" USING btree ("users_id"); + CREATE INDEX IF NOT EXISTS "payload_preferences_key_idx" ON "payload_preferences" USING btree ("key"); + CREATE INDEX IF NOT EXISTS "payload_preferences_updated_at_idx" ON "payload_preferences" USING btree ("updated_at"); + CREATE INDEX IF NOT EXISTS "payload_preferences_created_at_idx" ON "payload_preferences" USING btree ("created_at"); + CREATE INDEX IF NOT EXISTS "payload_preferences_rels_order_idx" ON "payload_preferences_rels" USING btree ("order"); + CREATE INDEX IF NOT EXISTS "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" USING btree ("parent_id"); + CREATE INDEX IF NOT EXISTS "payload_preferences_rels_path_idx" ON "payload_preferences_rels" USING btree ("path"); + CREATE INDEX IF NOT EXISTS "payload_preferences_rels_users_id_idx" ON "payload_preferences_rels" USING btree ("users_id"); + CREATE INDEX IF NOT EXISTS "payload_migrations_updated_at_idx" ON "payload_migrations" USING btree ("updated_at"); + CREATE INDEX IF NOT EXISTS "payload_migrations_created_at_idx" ON "payload_migrations" USING btree ("created_at");`) +} + +export async function down({ db, payload, req }: MigrateDownArgs): Promise { + await db.execute(sql` + DROP TABLE "users" CASCADE; + DROP TABLE "payload_locked_documents" CASCADE; + DROP TABLE "payload_locked_documents_rels" CASCADE; + DROP TABLE "payload_preferences" CASCADE; + DROP TABLE "payload_preferences_rels" CASCADE; + DROP TABLE "payload_migrations" CASCADE;`) +} diff --git a/test/database/up-down-migration/migrations/index.ts b/test/database/up-down-migration/migrations/index.ts index c7646c19ea..eaea2e87a0 100644 --- a/test/database/up-down-migration/migrations/index.ts +++ b/test/database/up-down-migration/migrations/index.ts @@ -1,9 +1,9 @@ -import * as migration_20250328_185055 from './20250328_185055.js' +import * as migration_20250428_121536 from './20250428_121536.js' export const migrations = [ { - up: migration_20250328_185055.up, - down: migration_20250328_185055.down, - name: '20250328_185055', + up: migration_20250428_121536.up, + down: migration_20250428_121536.down, + name: '20250428_121536', }, ]