Skip to content

Commit

Permalink
fix: Postgres identifier exceeds limit on eager relations (#7508) (#7509
Browse files Browse the repository at this point in the history
)

* fix: issue #7508

* new util DriverUtils.buildAlias

* fix: typo in statement

* wip: run tests with just hash

* fix compile

* added buildAlias options

Co-authored-by: Craig Betterly <craig@closem.ai>
  • Loading branch information
nurdism and Craig Betterly committed Apr 3, 2021
1 parent 8140a91 commit e4ec429
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 37 deletions.
39 changes: 27 additions & 12 deletions src/driver/DriverUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Driver} from "./Driver";
import { hash } from "../util/StringUtils";
import {hash,shorten} from "../util/StringUtils";

/**
* Common driver utility functions.
Expand Down Expand Up @@ -55,26 +55,41 @@ export class DriverUtils {
return Object.assign({}, options);
}


/**
* Builds column alias from given alias name and column name.
* Joins and shortens alias if needed.
*
* If alias length is greater than the limit (if any) allowed by the current
* driver, replaces it with a hashed string.
* If the alias length is greater than the limit allowed by the current
* driver, replaces it with a shortend string, if the shortend string
* is still too long, it will then hash the alias.
*
* @param driver Current `Driver`.
* @param alias Alias part.
* @param column Name of the column to be concatened to `alias`.
* @param buildOptions Optional settings.
* @param alias Alias parts.
*
* @return An alias allowing to select/transform the target `column`.
* @return An alias that is no longer than the divers max alias length.
*/
static buildColumnAlias({ maxAliasLength }: Driver, alias: string, column: string): string {
const columnAliasName = alias + "_" + column;
static buildAlias({ maxAliasLength }: Driver, buildOptions: { shorten?: boolean, joiner?: string } | string, ...alias: string[]): string {
if (typeof buildOptions === "string") {
alias.unshift(buildOptions);
buildOptions = { shorten: false, joiner: "_" };
} else {
buildOptions = Object.assign({ shorten: false, joiner: "_" }, buildOptions);
}

const newAlias = alias.length === 1 ? alias[0] : alias.join(buildOptions.joiner);
if (maxAliasLength && maxAliasLength > 0 && newAlias.length > maxAliasLength) {
if (buildOptions.shorten === true) {
const shortenedAlias = shorten(newAlias);
if (shortenedAlias.length < maxAliasLength) {
return shortenedAlias;
}
}

if (maxAliasLength && maxAliasLength > 0 && columnAliasName.length > maxAliasLength) {
return hash(columnAliasName, { length: maxAliasLength });
return hash(newAlias, { length: maxAliasLength });
}

return columnAliasName;
return newAlias;
}

// -------------------------------------------------------------------------
Expand Down
16 changes: 9 additions & 7 deletions src/find-options/FindOptionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {FindOneOptions} from "./FindOneOptions";
import {SelectQueryBuilder} from "../query-builder/SelectQueryBuilder";
import {FindRelationsNotFoundError} from "../error/FindRelationsNotFoundError";
import {EntityMetadata} from "../metadata/EntityMetadata";
import {shorten} from "../util/StringUtils";
import {DriverUtils} from "../driver/DriverUtils";

/**
* Utilities to work with FindOptions.
Expand Down Expand Up @@ -234,11 +234,7 @@ export class FindOptionsUtils {
matchedBaseRelations.forEach(relation => {

// generate a relation alias
let relationAlias: string = alias + "__" + relation;
// shorten it if needed by the driver
if (qb.connection.driver.maxAliasLength && relationAlias.length > qb.connection.driver.maxAliasLength) {
relationAlias = shorten(relationAlias);
}
let relationAlias: string = DriverUtils.buildAlias(qb.connection.driver, { shorten: true, joiner: "__" }, alias, relation);

// add a join for the found relation
const selection = alias + "." + relation;
Expand All @@ -261,8 +257,14 @@ export class FindOptionsUtils {

public static joinEagerRelations(qb: SelectQueryBuilder<any>, alias: string, metadata: EntityMetadata) {
metadata.eagerRelations.forEach(relation => {
const relationAlias = qb.connection.namingStrategy.eagerJoinRelationAlias(alias, relation.propertyPath);

// generate a relation alias
let relationAlias = DriverUtils.buildAlias(qb.connection.driver, { shorten: true }, qb.connection.namingStrategy.eagerJoinRelationAlias(alias, relation.propertyPath));

// add a join for the relation
qb.leftJoinAndSelect(alias + "." + relation.propertyPath, relationAlias);

// (recursive) join the eager relations
this.joinEagerRelations(qb, relationAlias, relation.inverseEntityMetadata);
});
}
Expand Down
12 changes: 6 additions & 6 deletions src/query-builder/SelectQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1797,7 +1797,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
}
return {
selection: selectionPath,
aliasName: selection && selection.aliasName ? selection.aliasName : DriverUtils.buildColumnAlias(this.connection.driver, aliasName, column.databaseName),
aliasName: selection && selection.aliasName ? selection.aliasName : DriverUtils.buildAlias(this.connection.driver, aliasName, column.databaseName),
// todo: need to keep in mind that custom selection.aliasName breaks hydrator. fix it later!
virtual: selection ? selection.virtual === true : (hasMainAlias ? false : true),
};
Expand Down Expand Up @@ -1940,11 +1940,11 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements

const querySelects = metadata.primaryColumns.map(primaryColumn => {
const distinctAlias = this.escape("distinctAlias");
const columnAlias = this.escape(DriverUtils.buildColumnAlias(this.connection.driver, mainAliasName, primaryColumn.databaseName));
const columnAlias = this.escape(DriverUtils.buildAlias(this.connection.driver, mainAliasName, primaryColumn.databaseName));
if (!orderBys[columnAlias]) // make sure we aren't overriding user-defined order in inverse direction
orderBys[columnAlias] = "ASC";

const alias = DriverUtils.buildColumnAlias(
const alias = DriverUtils.buildAlias(
this.connection.driver,
"ids_" + mainAliasName,
primaryColumn.databaseName
Expand Down Expand Up @@ -1977,7 +1977,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
}).join(" AND ");
}).join(" OR ");
} else {
const alias = DriverUtils.buildColumnAlias(
const alias = DriverUtils.buildAlias(
this.connection.driver,
"ids_" + mainAliasName,
metadata.primaryColumns[0].databaseName
Expand Down Expand Up @@ -2037,7 +2037,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
const propertyPath = criteriaParts.slice(1).join(".");
const alias = this.expressionMap.findAliasByName(aliasName);
const column = alias.metadata.findColumnWithPropertyPath(propertyPath);
return this.escape(parentAlias) + "." + this.escape(DriverUtils.buildColumnAlias(this.connection.driver, aliasName, column!.databaseName));
return this.escape(parentAlias) + "." + this.escape(DriverUtils.buildAlias(this.connection.driver, aliasName, column!.databaseName));
} else {
if (this.expressionMap.selects.find(select => select.selection === orderCriteria || select.aliasName === orderCriteria))
return this.escape(parentAlias) + "." + orderCriteria;
Expand All @@ -2055,7 +2055,7 @@ export class SelectQueryBuilder<Entity> extends QueryBuilder<Entity> implements
const propertyPath = criteriaParts.slice(1).join(".");
const alias = this.expressionMap.findAliasByName(aliasName);
const column = alias.metadata.findColumnWithPropertyPath(propertyPath);
orderByObject[this.escape(parentAlias) + "." + this.escape(DriverUtils.buildColumnAlias(this.connection.driver, aliasName, column!.databaseName))] = orderBys[orderCriteria];
orderByObject[this.escape(parentAlias) + "." + this.escape(DriverUtils.buildAlias(this.connection.driver, aliasName, column!.databaseName))] = orderBys[orderCriteria];
} else {
if (this.expressionMap.selects.find(select => select.selection === orderCriteria || select.aliasName === orderCriteria)) {
orderByObject[this.escape(parentAlias) + "." + orderCriteria] = orderBys[orderCriteria];
Expand Down
8 changes: 4 additions & 4 deletions src/query-builder/relation-id/RelationIdLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ export class RelationIdLoader {
const result: ObjectLiteral = {};
const duplicateParts: Array<string> = [];
relationIdAttr.relation.joinColumns.forEach(joinColumn => {
result[joinColumn.databaseName] = this.connection.driver.prepareHydratedValue(rawEntity[DriverUtils.buildColumnAlias(this.connection.driver, relationIdAttr.parentAlias, joinColumn.databaseName)], joinColumn.referencedColumn!);
result[joinColumn.databaseName] = this.connection.driver.prepareHydratedValue(rawEntity[DriverUtils.buildAlias(this.connection.driver, relationIdAttr.parentAlias, joinColumn.databaseName)], joinColumn.referencedColumn!);
const duplicatePart = `${joinColumn.databaseName}:${result[joinColumn.databaseName]}`;
if (duplicateParts.indexOf(duplicatePart) === -1) {
duplicateParts.push(duplicatePart);
}
});

relationIdAttr.relation.entityMetadata.primaryColumns.forEach(primaryColumn => {
result[primaryColumn.databaseName] = this.connection.driver.prepareHydratedValue(rawEntity[DriverUtils.buildColumnAlias(this.connection.driver, relationIdAttr.parentAlias, primaryColumn.databaseName)], primaryColumn);
result[primaryColumn.databaseName] = this.connection.driver.prepareHydratedValue(rawEntity[DriverUtils.buildAlias(this.connection.driver, relationIdAttr.parentAlias, primaryColumn.databaseName)], primaryColumn);
const duplicatePart = `${primaryColumn.databaseName}:${result[primaryColumn.databaseName]}`;
if (duplicateParts.indexOf(duplicatePart) === -1) {
duplicateParts.push(duplicatePart);
Expand Down Expand Up @@ -84,7 +84,7 @@ export class RelationIdLoader {
const parameterParts: ObjectLiteral = {};
const queryPart = joinColumns.map(joinColumn => {
const parameterName = joinColumn.databaseName + index;
const parameterValue = rawEntity[DriverUtils.buildColumnAlias(this.connection.driver, relationIdAttr.parentAlias, joinColumn.referencedColumn!.databaseName)];
const parameterValue = rawEntity[DriverUtils.buildAlias(this.connection.driver, relationIdAttr.parentAlias, joinColumn.referencedColumn!.databaseName)];
const duplicatePart = `${tableAlias}:${joinColumn.propertyPath}:${parameterValue}`;
if (duplicateParts.indexOf(duplicatePart) !== -1) {
return "";
Expand Down Expand Up @@ -162,7 +162,7 @@ export class RelationIdLoader {

const mappedColumns = rawEntities.map(rawEntity => {
return joinColumns.reduce((map, joinColumn) => {
map[joinColumn.propertyPath] = rawEntity[DriverUtils.buildColumnAlias(this.connection.driver, relationIdAttr.parentAlias, joinColumn.referencedColumn!.databaseName)];
map[joinColumn.propertyPath] = rawEntity[DriverUtils.buildAlias(this.connection.driver, relationIdAttr.parentAlias, joinColumn.referencedColumn!.databaseName)];
return map;
}, {} as ObjectLiteral);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ export class RawSqlResultsToEntityTransformer {
const map = new Map();
const keys: string[] = [];
if (alias.metadata.tableType === "view") {
keys.push(...alias.metadata.columns.map(column => DriverUtils.buildColumnAlias(this.driver, alias.name, column.databaseName)));
keys.push(...alias.metadata.columns.map(column => DriverUtils.buildAlias(this.driver, alias.name, column.databaseName)));
} else {
keys.push(...alias.metadata.primaryColumns.map(column => DriverUtils.buildColumnAlias(this.driver, alias.name, column.databaseName)));
keys.push(...alias.metadata.primaryColumns.map(column => DriverUtils.buildAlias(this.driver, alias.name, column.databaseName)));
}
rawResults.forEach(rawResult => {
const id = keys.map(key => {
Expand Down Expand Up @@ -95,7 +95,7 @@ export class RawSqlResultsToEntityTransformer {
let metadata = alias.metadata;

if (metadata.discriminatorColumn) {
const discriminatorValues = rawResults.map(result => result[DriverUtils.buildColumnAlias(this.driver, alias.name, alias.metadata.discriminatorColumn!.databaseName)]);
const discriminatorValues = rawResults.map(result => result[DriverUtils.buildAlias(this.driver, alias.name, alias.metadata.discriminatorColumn!.databaseName)]);
const discriminatorMetadata = metadata.childEntityMetadatas.find(childEntityMetadata => {
return typeof discriminatorValues.find(value => value === childEntityMetadata.discriminatorValue) !== 'undefined';
});
Expand Down Expand Up @@ -134,7 +134,7 @@ export class RawSqlResultsToEntityTransformer {
if (metadata.childEntityMetadatas.length > 0 && metadata.childEntityMetadatas.map(metadata => metadata.target).indexOf(column.target) !== -1)
return;

const value = rawResults[0][DriverUtils.buildColumnAlias(this.driver, alias.name, column.databaseName)];
const value = rawResults[0][DriverUtils.buildAlias(this.driver, alias.name, column.databaseName)];
if (value === undefined || column.isVirtual)
return;

Expand All @@ -158,7 +158,7 @@ export class RawSqlResultsToEntityTransformer {

// let discriminatorValue: string = "";
// if (metadata.discriminatorColumn)
// discriminatorValue = rawResults[0][DriverUtils.buildColumnAlias(this.connection.driver, alias.name, alias.metadata.discriminatorColumn!.databaseName)];
// discriminatorValue = rawResults[0][DriverUtils.buildAlias(this.connection.driver, alias.name, alias.metadata.discriminatorColumn!.databaseName)];

this.expressionMap.joinAttributes.forEach(join => { // todo: we have problem here - when inner joins are used without selects it still create empty array

Expand Down Expand Up @@ -303,7 +303,7 @@ export class RawSqlResultsToEntityTransformer {
referenceColumnName = relation.isOwning ? relation.joinColumns[0].referencedColumn!.databaseName : relation.inverseRelation!.joinColumns[0].referencedColumn!.databaseName;
}

const referenceColumnValue = rawSqlResults[0][DriverUtils.buildColumnAlias(this.driver, alias.name, referenceColumnName)]; // we use zero index since its grouped data // todo: selection with alias for entity columns wont work
const referenceColumnValue = rawSqlResults[0][DriverUtils.buildAlias(this.driver, alias.name, referenceColumnName)]; // we use zero index since its grouped data // todo: selection with alias for entity columns wont work
if (referenceColumnValue !== undefined && referenceColumnValue !== null) {
entity[rawRelationCountResult.relationCountAttribute.mapToPropertyPropertyName] = 0;
rawRelationCountResult.results
Expand Down Expand Up @@ -334,9 +334,9 @@ export class RawSqlResultsToEntityTransformer {
return columns.reduce((valueMap, column) => {
rawSqlResults.forEach(rawSqlResult => {
if (relation.isManyToOne || relation.isOneToOneOwner) {
valueMap[column.databaseName] = this.driver.prepareHydratedValue(rawSqlResult[DriverUtils.buildColumnAlias(this.driver, parentAlias, column.databaseName)], column);
valueMap[column.databaseName] = this.driver.prepareHydratedValue(rawSqlResult[DriverUtils.buildAlias(this.driver, parentAlias, column.databaseName)], column);
} else {
valueMap[column.databaseName] = this.driver.prepareHydratedValue(rawSqlResult[DriverUtils.buildColumnAlias(this.driver, parentAlias, column.referencedColumn!.databaseName)], column);
valueMap[column.databaseName] = this.driver.prepareHydratedValue(rawSqlResult[DriverUtils.buildAlias(this.driver, parentAlias, column.referencedColumn!.databaseName)], column);
}
});
return valueMap;
Expand Down

0 comments on commit e4ec429

Please sign in to comment.