fix(db-postgres): alias already in use in this query (#8823)

Fixes https://github.com/payloadcms/payload/issues/8517

Reuses existing joins instead of adding new, duplicate ones which
causes:
```
Error: Alias "" is already used in this query.
```
This commit is contained in:
Sasha
2024-10-22 17:14:38 +03:00
committed by GitHub
parent 69125504af
commit 4c396c720e
7 changed files with 101 additions and 22 deletions

View File

@@ -0,0 +1,25 @@
import type { SQL } from 'drizzle-orm'
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
import type { GenericTable } from '../types.js'
import type { BuildQueryJoinAliases } from './buildQuery.js'
import { getNameFromDrizzleTable } from '../utilities/getNameFromDrizzleTable.js'
export const addJoinTable = ({
type,
condition,
joins,
table,
}: {
condition: SQL
joins: BuildQueryJoinAliases
table: GenericTable | PgTableWithColumns<any>
type?: 'innerJoin' | 'leftJoin' | 'rightJoin'
}) => {
const name = getNameFromDrizzleTable(table)
if (!joins.some((eachJoin) => getNameFromDrizzleTable(eachJoin.table) === name)) {
joins.push({ type, condition, table })
}
}

View File

@@ -55,7 +55,6 @@ export const buildOrderBy = ({
pathSegments: sortPath.replace(/__/g, '.').split('.'), pathSegments: sortPath.replace(/__/g, '.').split('.'),
selectFields, selectFields,
tableName, tableName,
useAlias: true,
value: sortPath, value: sortPath,
}) })
orderBy.column = sortTable?.[sortTableColumnName] orderBy.column = sortTable?.[sortTableColumnName]

View File

@@ -13,6 +13,7 @@ import type { DrizzleAdapter, GenericColumn } from '../types.js'
import type { BuildQueryJoinAliases } from './buildQuery.js' import type { BuildQueryJoinAliases } from './buildQuery.js'
import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js' import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js'
import { addJoinTable } from './addJoinTable.js'
import { getTableAlias } from './getTableAlias.js' import { getTableAlias } from './getTableAlias.js'
type Constraint = { type Constraint = {
@@ -53,7 +54,6 @@ type Args = {
* If creating a new table name for arrays and blocks, this suffix should be appended to the table name * If creating a new table name for arrays and blocks, this suffix should be appended to the table name
*/ */
tableNameSuffix?: string tableNameSuffix?: string
useAlias?: boolean
/** /**
* The raw value of the query before sanitization * The raw value of the query before sanitization
*/ */
@@ -79,7 +79,6 @@ export const getTableColumnFromPath = ({
selectFields, selectFields,
tableName, tableName,
tableNameSuffix = '', tableNameSuffix = '',
useAlias,
value, value,
}: Args): TableColumn => { }: Args): TableColumn => {
const fieldPath = incomingSegments[0] const fieldPath = incomingSegments[0]
@@ -141,7 +140,6 @@ export const getTableColumnFromPath = ({
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
tableNameSuffix, tableNameSuffix,
useAlias,
value, value,
}) })
} }
@@ -162,7 +160,6 @@ export const getTableColumnFromPath = ({
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`, tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
useAlias,
value, value,
}) })
} }
@@ -181,7 +178,6 @@ export const getTableColumnFromPath = ({
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
tableNameSuffix, tableNameSuffix,
useAlias,
value, value,
}) })
} }
@@ -190,8 +186,9 @@ export const getTableColumnFromPath = ({
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
newTableName = `${tableName}${adapter.localesSuffix}` newTableName = `${tableName}${adapter.localesSuffix}`
joins.push({ addJoinTable({
condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID), condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
if (locale !== 'all') { if (locale !== 'all') {
@@ -217,7 +214,6 @@ export const getTableColumnFromPath = ({
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`, tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
useAlias,
value, value,
}) })
} }
@@ -229,11 +225,12 @@ export const getTableColumnFromPath = ({
) )
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins.push({ addJoinTable({
condition: and( condition: and(
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent), eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
eq(adapter.tables[newTableName]._locale, locale), eq(adapter.tables[newTableName]._locale, locale),
), ),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
if (locale !== 'all') { if (locale !== 'all') {
@@ -244,8 +241,9 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins.push({ addJoinTable({
condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent), condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
} }
@@ -276,8 +274,9 @@ export const getTableColumnFromPath = ({
] ]
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins.push({ addJoinTable({
condition: and(...joinConstraints, eq(adapter.tables[newTableName]._locale, locale)), condition: and(...joinConstraints, eq(adapter.tables[newTableName]._locale, locale)),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
if (locale !== 'all') { if (locale !== 'all') {
@@ -288,8 +287,9 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins.push({ addJoinTable({
condition: and(...joinConstraints), condition: and(...joinConstraints),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
} }
@@ -313,11 +313,12 @@ export const getTableColumnFromPath = ({
constraintPath = `${constraintPath}${field.name}.%.` constraintPath = `${constraintPath}${field.name}.%.`
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins.push({ addJoinTable({
condition: and( condition: and(
eq(arrayParentTable.id, adapter.tables[newTableName]._parentID), eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
eq(adapter.tables[newTableName]._locale, locale), eq(adapter.tables[newTableName]._locale, locale),
), ),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
if (locale !== 'all') { if (locale !== 'all') {
@@ -328,8 +329,9 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins.push({ addJoinTable({
condition: eq(arrayParentTable.id, adapter.tables[newTableName]._parentID), condition: eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
joins,
table: adapter.tables[newTableName], table: adapter.tables[newTableName],
}) })
} }
@@ -345,7 +347,6 @@ export const getTableColumnFromPath = ({
rootTableName, rootTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
useAlias,
value, value,
}) })
} }
@@ -404,7 +405,6 @@ export const getTableColumnFromPath = ({
rootTableName, rootTableName,
selectFields: blockSelectFields, selectFields: blockSelectFields,
tableName: newTableName, tableName: newTableName,
useAlias,
value, value,
}) })
} catch (error) { } catch (error) {
@@ -641,7 +641,6 @@ export const getTableColumnFromPath = ({
rootTableName: newTableName, rootTableName: newTableName,
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
useAlias,
value, value,
}) })
} else if ( } else if (
@@ -694,7 +693,6 @@ export const getTableColumnFromPath = ({
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
selectFields, selectFields,
tableName: newTableName, tableName: newTableName,
useAlias,
value, value,
}) })
} }
@@ -716,12 +714,11 @@ export const getTableColumnFromPath = ({
const parentTable = aliasTable || adapter.tables[tableName] const parentTable = aliasTable || adapter.tables[tableName]
newTableName = `${tableName}${adapter.localesSuffix}` newTableName = `${tableName}${adapter.localesSuffix}`
newTable = useAlias newTable = adapter.tables[newTableName]
? getTableAlias({ adapter, tableName: newTableName }).newAliasTable
: adapter.tables[newTableName]
joins.push({ addJoinTable({
condition: eq(parentTable.id, newTable._parentID), condition: eq(parentTable.id, newTable._parentID),
joins,
table: newTable, table: newTable,
}) })

View File

@@ -0,0 +1,9 @@
import type { Table } from 'drizzle-orm'
export const getNameFromDrizzleTable = (table: Table): string => {
const symbol = Object.getOwnPropertySymbols(table).find((symb) =>
symb.description.includes('Name'),
)
return table[symbol]
}

View File

@@ -23,6 +23,10 @@ const ArrayFields: CollectionConfig = {
type: 'text', type: 'text',
required: true, required: true,
}, },
{
name: 'anotherText',
type: 'text',
},
{ {
name: 'localizedText', name: 'localizedText',
type: 'text', type: 'text',

View File

@@ -1546,6 +1546,50 @@ describe('Fields', () => {
expect(allLocales.localized.en[0].text).toStrictEqual(enText) expect(allLocales.localized.en[0].text).toStrictEqual(enText)
expect(allLocales.localized.es[0].text).toStrictEqual(esText) expect(allLocales.localized.es[0].text).toStrictEqual(esText)
}) })
it('should query by the same array', async () => {
const doc = await payload.create({
collection,
data: {
items: [
{
localizedText: 'test',
text: 'required',
anotherText: 'another',
},
],
localized: [{ text: 'a' }],
},
})
// left join collection_items + left join collection_items_locales
const {
docs: [res],
} = await payload.find({
collection,
where: {
and: [
{
'items.localizedText': {
equals: 'test',
},
},
{
'items.anotherText': {
equals: 'another',
},
},
{
'items.text': {
equals: 'required',
},
},
],
},
})
expect(res.id).toBe(doc.id)
})
}) })
describe('group', () => { describe('group', () => {

View File

@@ -353,6 +353,7 @@ export interface ArrayField {
title?: string | null; title?: string | null;
items: { items: {
text: string; text: string;
anotherText?: string | null;
localizedText?: string | null; localizedText?: string | null;
subArray?: subArray?:
| { | {