Skip to content

generate:db-schema declares the locale enum as enum__locales, but the adapter creates _locales #17736

Description

@EugenieF

Summary

generate:db-schema declares the locale enum with the SQL name enum__locales, but the adapter creates it in Postgres as _locales. The generated schema therefore references a type that has never existed in any database.

Two hardcoded strings that should match. A PR is attached.

Where

The adapter, which is what actually reaches the database — the SQL type name is _locales, and enum__locales is only the key in the enums map:

https://github.com/payloadcms/payload/blob/v3.85.2/packages/drizzle/src/postgres/init.ts#L22-L27

if (this.payload.config.localization) {
  this.enums.enum__locales = this.pgSchema.enum(
    '_locales',
    this.payload.config.localization.locales.map(({ code }) => code) as [string, ...string[]],
  )
}

The generator, which passes that map key where a SQL type name belongs:

https://github.com/payloadcms/payload/blob/v3.85.2/packages/drizzle/src/utilities/createSchemaGenerator.ts#L96-L98

if (this.payload.config.localization && enumImport) {
  addEnum('enum__locales', this.payload.config.localization.localeCodes)
}

addEnum uses its name argument for both the exported const and the SQL name:

https://github.com/payloadcms/payload/blob/v3.85.2/packages/drizzle/src/utilities/createSchemaGenerator.ts#L86-L94

const addEnum = (name: string, options: string[]) => {
  if (enumsList.some((each) => each === name)) {
    return
  }
  enumsList.push(name)
  enumDeclarations.push(
    `export const ${name} = ${enumFn}('${name}', [${options.map((option) => `'${option}'`).join(', ')}])`,
  )
}

That contract holds for the only other caller, which passes a real SQL type name:

https://github.com/payloadcms/payload/blob/v3.85.2/packages/drizzle/src/postgres/columnToCodeConverter.ts#L19

addEnum(column.enumName, column.options)

So the locale call site is the single place where the two diverge.

Reproducing it

This repo already contains a reproduction — the committed fixture at
https://github.com/payloadcms/payload/blob/v3.85.2/test/relationships/payload-generated-schema.ts#L26

export const enum__locales = pgEnum('enum__locales', ['en', 'de'])

while init.ts creates that type as _locales.

In any localized Postgres project, after payload generate:db-schema:

SELECT DISTINCT udt_name FROM information_schema.columns
WHERE table_schema = 'public' AND column_name = '_locale';
-- _locales

SELECT typname FROM pg_type WHERE typname = 'enum__locales';
-- 0 rows

Impact

Every _locale column in the generated schema is typed against a type that does not exist — around 20 columns in our project. With push: false the generated file is never executed, so nothing surfaces the mismatch; it is documented as the schema mirror, so it is what people read when hand-writing migrations, and DDL written from it references a nonexistent type.

Expected

pgEnum('_locales', …), matching init.ts. The exported const should keep the name enum__locales, since columnToCodeConverter.ts#L17 emits enum__locales(…) as the column builder. Only the first argument to pgEnum is wrong.

Version

Found on 3.85.2. The cited code is byte-identical on main at the time of writing (createSchemaGenerator.ts L86/L97, init.ts L23–L25 — same lines, same content), so this is current.

Related

Two other cases where generate:db-schema output disagrees with what Payload itself creates, both concerning _status, are filed separately in #17738 — they need a design decision, whereas this one does not.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions