Skip to content

Commit e62978c

Browse files
415mccSvetlozar
authored and
Svetlozar
committed
fix: change InsertQueryBuilder.values() with an empty array into a no-op (typeorm#6584)
Change InsertQueryBuilder.values() with an empty array into a no-op instead of the current behavior of inserting a row of DEFAULT values. Closes: typeorm#3111
1 parent 689ddd1 commit e62978c

File tree

4 files changed

+59
-15
lines changed

4 files changed

+59
-15
lines changed

src/entity-manager/EntityManager.ts

-11
Original file line numberDiff line numberDiff line change
@@ -581,17 +581,6 @@ export class EntityManager {
581581
* You can execute bulk inserts using this method.
582582
*/
583583
async insert<Entity>(target: ObjectType<Entity>|EntitySchema<Entity>|string, entity: QueryDeepPartialEntity<Entity>|(QueryDeepPartialEntity<Entity>[])): Promise<InsertResult> {
584-
585-
// If user passed empty array of entities then we don't need to do
586-
// anything.
587-
//
588-
// Fixes GitHub issue #5734. If we were to let this through we would
589-
// run into problems downstream, like subscribers getting invoked with
590-
// the empty array where they expect an entity, and SQL queries with an
591-
// empty VALUES clause.
592-
if (Array.isArray(entity) && entity.length === 0)
593-
return Promise.resolve(new InsertResult());
594-
595584
// TODO: Oracle does not support multiple values. Need to create another nice solution.
596585
if (this.connection.driver instanceof OracleDriver && Array.isArray(entity)) {
597586
const results = await Promise.all(entity.map(entity => this.insert(target, entity)));

src/query-builder/InsertQueryBuilder.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
4141
* Executes sql generated by query builder and returns raw database results.
4242
*/
4343
async execute(): Promise<InsertResult> {
44+
// console.time(".value sets");
45+
const valueSets: ObjectLiteral[] = this.getValueSets();
46+
// console.timeEnd(".value sets");
47+
48+
// If user passed empty array of entities then we don't need to do
49+
// anything.
50+
//
51+
// Fixes GitHub issues #3111 and #5734. If we were to let this through
52+
// we would run into problems downstream, like subscribers getting
53+
// invoked with the empty array where they expect an entity, and SQL
54+
// queries with an empty VALUES clause.
55+
if (valueSets.length === 0)
56+
return new InsertResult();
57+
4458
// console.time("QueryBuilder.execute");
4559
// console.time(".database stuff");
4660
const queryRunner = this.obtainQueryRunner();
@@ -55,9 +69,6 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
5569
}
5670

5771
// console.timeEnd(".database stuff");
58-
// console.time(".value sets");
59-
const valueSets: ObjectLiteral[] = this.getValueSets();
60-
// console.timeEnd(".value sets");
6172

6273
// call before insertion methods in listeners and subscribers
6374
if (this.expressionMap.callListeners === true && this.expressionMap.mainAlias!.hasMetadata) {
@@ -579,7 +590,7 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
579590
* Gets array of values need to be inserted into the target table.
580591
*/
581592
protected getValueSets(): ObjectLiteral[] {
582-
if (Array.isArray(this.expressionMap.valuesSet) && this.expressionMap.valuesSet.length > 0)
593+
if (Array.isArray(this.expressionMap.valuesSet))
583594
return this.expressionMap.valuesSet;
584595

585596
if (this.expressionMap.valuesSet instanceof Object)
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {Column} from "../../../../src/decorator/columns/Column";
2+
import {Entity} from "../../../../src/decorator/entity/Entity";
3+
import {PrimaryGeneratedColumn} from "../../../../src/decorator/columns/PrimaryGeneratedColumn";
4+
5+
export const DEFAULT_VALUE = "default-value";
6+
7+
@Entity()
8+
export class Test {
9+
@PrimaryGeneratedColumn()
10+
id: number;
11+
12+
@Column({default: DEFAULT_VALUE})
13+
value: string;
14+
}

test/github-issues/3111/issue-3111.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import "reflect-metadata";
2+
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../utils/test-utils";
3+
import {Connection} from "../../../src/connection/Connection";
4+
import {expect} from "chai";
5+
import {InsertValuesMissingError} from "../../../src/error/InsertValuesMissingError";
6+
import {Test, DEFAULT_VALUE} from "./entity/Test";
7+
8+
describe("github issues > #3111 Inserting with query builder attempts to insert a default row when values is empty array", () => {
9+
10+
let connections: Connection[];
11+
before(async () => connections = await createTestingConnections({
12+
entities: [__dirname + "/entity/*{.js,.ts}"],
13+
schemaCreate: true,
14+
dropSchema: true,
15+
}));
16+
beforeEach(() => reloadTestingDatabases(connections));
17+
after(() => closeTestingConnections(connections));
18+
19+
it("should not insert with default values on .values([])", () => Promise.all(connections.map(async connection => {
20+
const repo = connection.getRepository(Test);
21+
await repo.createQueryBuilder().insert().values([]).execute();
22+
const rowsWithDefaultValue = await repo.find({ where: {value: DEFAULT_VALUE}});
23+
expect(rowsWithDefaultValue).to.have.lengthOf(0);
24+
})));
25+
26+
it("should still error on missing .values()", () => Promise.all(connections.map(async connection => {
27+
const repo = connection.getRepository(Test);
28+
await repo.createQueryBuilder().insert().execute().should.be.rejectedWith(InsertValuesMissingError);
29+
})));
30+
});

0 commit comments

Comments
 (0)