Skip to content

Commit

Permalink
fix: upsert should find unique index created by one-to-one relation (#…
Browse files Browse the repository at this point in the history
…8618)

* fix: upsert should find unique index created by one-to-one relation

* fix: do not enforce column constraint exists for upsert

* fix: remove OrmUtils reference and OrmUtils#flatten that is not needed

* fix: leave the db engine to enforce upsert constraints as it sees fit
  • Loading branch information
joeflateau committed Feb 14, 2022
1 parent f224f24 commit c8c00ba
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 24 deletions.
15 changes: 0 additions & 15 deletions src/entity-manager/EntityManager.ts
Expand Up @@ -495,21 +495,6 @@ export class EntityManager {
options = conflictPathsOrOptions;
}

const uniqueColumnConstraints = [
metadata.primaryColumns,
...metadata.indices.filter(ix => ix.isUnique).map(ix => ix.columns),
...metadata.uniques.map(uq => uq.columns)
];

const useIndex = uniqueColumnConstraints.find((ix) =>
ix.length === options.conflictPaths.length &&
options.conflictPaths.every((conflictPropertyPath) => ix.some((col) => col.propertyPath === conflictPropertyPath))
);

if (useIndex == null) {
throw new TypeORMError(`An upsert requires conditions that have a unique constraint but none was found for conflict properties: ${options.conflictPaths.join(", ")}`);
}

let entities: QueryDeepPartialEntity<Entity>[];

if (!Array.isArray(entityOrEntities)) {
Expand Down
@@ -0,0 +1,15 @@
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "../../../../../src";
import { Category } from "./Category";

@Entity()
export class OneToOneRelationEntity {
@PrimaryGeneratedColumn()
id: number;

@OneToOne(() => Category)
@JoinColumn()
category: Category;

@Column()
order: number;
}
Expand Up @@ -14,6 +14,7 @@ import { ExternalIdPrimaryKeyEntity } from "./entity/ExternalIdPrimaryKeyEntity"
import { EmbeddedUniqueConstraintEntity } from "./entity/EmbeddedUniqueConstraintEntity";
import { RelationAsPrimaryKey } from "./entity/RelationAsPrimaryKey";
import { TwoUniqueColumnsEntity } from "./entity/TwoUniqueColumns";
import { OneToOneRelationEntity } from "./entity/OneToOneRelation";

describe("repository > basic methods", () => {

Expand Down Expand Up @@ -41,7 +42,8 @@ describe("repository > basic methods", () => {
ExternalIdPrimaryKeyEntity,
EmbeddedUniqueConstraintEntity,
RelationAsPrimaryKey,
TwoUniqueColumnsEntity
TwoUniqueColumnsEntity,
OneToOneRelationEntity
],
}))
);
Expand Down Expand Up @@ -501,6 +503,31 @@ describe("repository > basic methods", () => {
await embeddedConstraintObjects.upsert({ embedded: { id: "bar1", value: "foo 2" } }, ["embedded.id"]);
(await embeddedConstraintObjects.findOneOrFail({ embedded: { id: "bar1" } })).embedded.value.should.be.equal("foo 2");
})));
it("should upsert on one-to-one relation", () => Promise.all(connections.map(async (connection) => {
if (connection.driver.supportedUpsertType == null) return;

const oneToOneRepository = connection.getRepository(OneToOneRelationEntity);
const categoryRepository = connection.getRepository(Category);

const category = await categoryRepository.save({
name: "Category"
});

await oneToOneRepository.upsert({
category,
order: 1
}, ["category.id"]);

(await oneToOneRepository.findOneOrFail({ category }))!.order.should.be.equal(1);

await oneToOneRepository.upsert({
category,
order: 2
}, ["category.id"]);

(await oneToOneRepository.findOneOrFail({ category }))!.order.should.be.equal(2);

})));
it("should bulk upsert with embedded columns", () => Promise.all(connections.map(async (connection) => {
if (connection.driver.supportedUpsertType == null) return;

Expand All @@ -524,14 +551,6 @@ describe("repository > basic methods", () => {
(await embeddedConstraintObjects.findOneOrFail({ embedded: { id: "bar2" } })).embedded.value.should.be.equal("value2 2");
(await embeddedConstraintObjects.findOneOrFail({ embedded: { id: "bar3" } })).embedded.value.should.be.equal("value3 2");
})));
it("should throw if attempting to conflict on properties with no unique constraint", () => Promise.all(connections.map(async (connection) => {
if (connection.driver.supportedUpsertType == null) return;

const externalIdObjects = connection.getRepository(ExternalIdPrimaryKeyEntity);
// cannot conflict on a column with no unique index
await externalIdObjects.upsert({ title: "foo"}, ["title"])
.should.be.rejectedWith(TypeORMError);
})));
it("should throw if using an unsupported driver", () => Promise.all(connections.map(async (connection) => {
if (connection.driver.supportedUpsertType != null) return;

Expand Down

0 comments on commit c8c00ba

Please sign in to comment.