Skip to content

Commit 5fce501

Browse files
authored
fix(db-postgres): dbName in arrays regression with long generated drizzle relation names (#12237)
Fixes #12136 which caused by regression from #11995 The previous PR solved an issue where the generated drizzle relation name was too long because of Payload field names, for example ``` { name: 'thisIsALongFieldNameThatWillCauseAPostgresErrorEvenThoughWeSetAShorterDBName', dbName: 'shortname', type: 'array', fields: [ { name: 'nested_field_1', type: 'array', dbName: 'short_nested_1', fields: [], }, { name: 'nested_field_2', type: 'text', }, ], }, ``` But it caused regression, when custom `dbName` vice versa caused long relation names: ``` export const Header: GlobalConfig = { 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', }, ], }, ], }, ], }, ], }, ], } ``` Notice if you calculate the generated relation name for `itemsLvl4` you get: `header__header_items_lvl1__header_items_lvl2__header_items_lvl3_header_items_lvl4` - 81 characters, Drizzle, for joining shrink the alias to 63 characters -`header__header_items_lvl1__header_items_lvl2__header_items_lvl3` and Postgres throws: ``` error: table name "header__header_items_lvl1__header_items_lvl2__header_items_lvl3" specified more than once ```
1 parent 3e7db30 commit 5fce501

File tree

12 files changed

+915
-594
lines changed

12 files changed

+915
-594
lines changed

packages/drizzle/src/find/traverseFields.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type { Result } from './buildFindManyArgs.js'
2222
import buildQuery from '../queries/buildQuery.js'
2323
import { getTableAlias } from '../queries/getTableAlias.js'
2424
import { operatorMap } from '../queries/operatorMap.js'
25+
import { getArrayRelationName } from '../utilities/getArrayRelationName.js'
2526
import { getNameFromDrizzleTable } from '../utilities/getNameFromDrizzleTable.js'
2627
import { jsonAggBuildObject } from '../utilities/json.js'
2728
import { rawConstraint } from '../utilities/rawConstraint.js'
@@ -196,7 +197,12 @@ export const traverseFields = ({
196197
}
197198
}
198199

199-
const relationName = field.dbName ? `_${arrayTableName}` : `${path}${field.name}`
200+
const relationName = getArrayRelationName({
201+
field,
202+
path: `${path}${field.name}`,
203+
tableName: arrayTableName,
204+
})
205+
200206
currentArgs.with[relationName] = withArray
201207

202208
traverseFields({

packages/drizzle/src/schema/traverseFields.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type {
2323

2424
import { createTableName } from '../createTableName.js'
2525
import { buildIndexName } from '../utilities/buildIndexName.js'
26+
import { getArrayRelationName } from '../utilities/getArrayRelationName.js'
2627
import { hasLocalesTable } from '../utilities/hasLocalesTable.js'
2728
import { validateExistingBlockIsIdentical } from '../utilities/validateExistingBlockIsIdentical.js'
2829
import { buildTable } from './build.js'
@@ -288,7 +289,11 @@ export const traverseFields = ({
288289
}
289290
}
290291

291-
const relationName = field.dbName ? `_${arrayTableName}` : fieldName
292+
const relationName = getArrayRelationName({
293+
field,
294+
path: fieldName,
295+
tableName: arrayTableName,
296+
})
292297

293298
relationsToBuild.set(relationName, {
294299
type: 'many',

packages/drizzle/src/transform/read/traverseFields.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import toSnakeCase from 'to-snake-case'
66
import type { DrizzleAdapter } from '../../types.js'
77
import type { BlocksMap } from '../../utilities/createBlocksMap.js'
88

9+
import { getArrayRelationName } from '../../utilities/getArrayRelationName.js'
910
import { transformHasManyNumber } from './hasManyNumber.js'
1011
import { transformHasManyText } from './hasManyText.js'
1112
import { transformRelationship } from './relationship.js'
@@ -121,9 +122,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
121122
`${currentTableName}_${tablePath}${toSnakeCase(field.name)}`,
122123
)
123124

124-
if (field.dbName) {
125-
fieldData = table[`_${arrayTableName}`]
126-
}
125+
fieldData = table[getArrayRelationName({ field, path: fieldName, tableName: arrayTableName })]
127126

128127
if (Array.isArray(fieldData)) {
129128
if (isLocalized) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { ArrayField } from 'payload'
2+
3+
export const getArrayRelationName = ({
4+
field,
5+
path,
6+
tableName,
7+
}: {
8+
field: ArrayField
9+
path: string
10+
tableName: string
11+
}) => {
12+
if (field.dbName && path.length > 63) {
13+
return `_${tableName}`
14+
}
15+
16+
return path
17+
}

test/database/config.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,55 @@ export default buildConfigWithDefaults({
700700
},
701701
],
702702
globals: [
703+
{
704+
slug: 'header',
705+
fields: [
706+
{
707+
name: 'itemsLvl1',
708+
type: 'array',
709+
dbName: 'header_items_lvl1',
710+
fields: [
711+
{
712+
name: 'label',
713+
type: 'text',
714+
},
715+
{
716+
name: 'itemsLvl2',
717+
type: 'array',
718+
dbName: 'header_items_lvl2',
719+
fields: [
720+
{
721+
name: 'label',
722+
type: 'text',
723+
},
724+
{
725+
name: 'itemsLvl3',
726+
type: 'array',
727+
dbName: 'header_items_lvl3',
728+
fields: [
729+
{
730+
name: 'label',
731+
type: 'text',
732+
},
733+
{
734+
name: 'itemsLvl4',
735+
type: 'array',
736+
dbName: 'header_items_lvl4',
737+
fields: [
738+
{
739+
name: 'label',
740+
type: 'text',
741+
},
742+
],
743+
},
744+
],
745+
},
746+
],
747+
},
748+
],
749+
},
750+
],
751+
},
703752
{
704753
slug: 'global',
705754
dbName: 'customGlobal',

test/database/int.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,6 +2452,17 @@ describe('database', () => {
24522452
expect(res.docs[0].id).toBe(customID.id)
24532453
})
24542454

2455+
it('deep nested arrays', async () => {
2456+
await payload.updateGlobal({
2457+
slug: 'header',
2458+
data: { itemsLvl1: [{ itemsLvl2: [{ itemsLvl3: [{ itemsLvl4: [{ label: 'label' }] }] }] }] },
2459+
})
2460+
2461+
const header = await payload.findGlobal({ slug: 'header' })
2462+
2463+
expect(header.itemsLvl1[0]?.itemsLvl2[0]?.itemsLvl3[0]?.itemsLvl4[0]?.label).toBe('label')
2464+
})
2465+
24552466
it('should count with a query that contains subqueries', async () => {
24562467
const category = await payload.create({
24572468
collection: 'categories',

test/database/payload-types.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,14 @@ export interface Config {
115115
defaultIDType: string;
116116
};
117117
globals: {
118+
header: Header;
118119
global: Global;
119120
'global-2': Global2;
120121
'global-3': Global3;
121122
'virtual-relation-global': VirtualRelationGlobal;
122123
};
123124
globalsSelect: {
125+
header: HeaderSelect<false> | HeaderSelect<true>;
124126
global: GlobalSelect<false> | GlobalSelect<true>;
125127
'global-2': Global2Select<false> | Global2Select<true>;
126128
'global-3': Global3Select<false> | Global3Select<true>;
@@ -977,6 +979,39 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
977979
updatedAt?: T;
978980
createdAt?: T;
979981
}
982+
/**
983+
* This interface was referenced by `Config`'s JSON-Schema
984+
* via the `definition` "header".
985+
*/
986+
export interface Header {
987+
id: string;
988+
itemsLvl1?:
989+
| {
990+
label?: string | null;
991+
itemsLvl2?:
992+
| {
993+
label?: string | null;
994+
itemsLvl3?:
995+
| {
996+
label?: string | null;
997+
itemsLvl4?:
998+
| {
999+
label?: string | null;
1000+
id?: string | null;
1001+
}[]
1002+
| null;
1003+
id?: string | null;
1004+
}[]
1005+
| null;
1006+
id?: string | null;
1007+
}[]
1008+
| null;
1009+
id?: string | null;
1010+
}[]
1011+
| null;
1012+
updatedAt?: string | null;
1013+
createdAt?: string | null;
1014+
}
9801015
/**
9811016
* This interface was referenced by `Config`'s JSON-Schema
9821017
* via the `definition` "global".
@@ -1018,6 +1053,39 @@ export interface VirtualRelationGlobal {
10181053
updatedAt?: string | null;
10191054
createdAt?: string | null;
10201055
}
1056+
/**
1057+
* This interface was referenced by `Config`'s JSON-Schema
1058+
* via the `definition` "header_select".
1059+
*/
1060+
export interface HeaderSelect<T extends boolean = true> {
1061+
itemsLvl1?:
1062+
| T
1063+
| {
1064+
label?: T;
1065+
itemsLvl2?:
1066+
| T
1067+
| {
1068+
label?: T;
1069+
itemsLvl3?:
1070+
| T
1071+
| {
1072+
label?: T;
1073+
itemsLvl4?:
1074+
| T
1075+
| {
1076+
label?: T;
1077+
id?: T;
1078+
};
1079+
id?: T;
1080+
};
1081+
id?: T;
1082+
};
1083+
id?: T;
1084+
};
1085+
updatedAt?: T;
1086+
createdAt?: T;
1087+
globalType?: T;
1088+
}
10211089
/**
10221090
* This interface was referenced by `Config`'s JSON-Schema
10231091
* via the `definition` "global_select".

0 commit comments

Comments
 (0)