Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Feb 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions src/entity-manager/EntityManager.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
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