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:
25
packages/drizzle/src/queries/addJoinTable.ts
Normal file
25
packages/drizzle/src/queries/addJoinTable.ts
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
}
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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', () => {
|
||||||
|
|||||||
@@ -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?:
|
||||||
| {
|
| {
|
||||||
|
|||||||
Reference in New Issue
Block a user