From aede8499425d430bc65f0ee84849abbc0bb178c2 Mon Sep 17 00:00:00 2001 From: Jiwon Choi <1890mah@gmail.com> Date: Fri, 4 Sep 2020 12:28:08 +0900 Subject: [PATCH 1/4] fix: (sqlite) get correct increment primary key for mutiple entities inserted Closes: #2131 --- src/driver/Driver.ts | 2 +- src/driver/sqlite-abstract/AbstractSqliteDriver.ts | 4 ++-- src/query-builder/ReturningResultsEntityUpdator.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/driver/Driver.ts b/src/driver/Driver.ts index 4041f58693..f3da179f95 100644 --- a/src/driver/Driver.ts +++ b/src/driver/Driver.ts @@ -175,7 +175,7 @@ export interface Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertResult: any): ObjectLiteral|undefined; + createGeneratedMap(metadata: EntityMetadata, insertResult: any, entityIndex?: number, entityNum?: number): ObjectLiteral|undefined; /** * Differentiate columns of this table and columns from the given column metadatas columns diff --git a/src/driver/sqlite-abstract/AbstractSqliteDriver.ts b/src/driver/sqlite-abstract/AbstractSqliteDriver.ts index 09cb044e17..849393bf78 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteDriver.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteDriver.ts @@ -507,11 +507,11 @@ export abstract class AbstractSqliteDriver implements Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertResult: any) { + createGeneratedMap(metadata: EntityMetadata, insertResult: any, entityIndex: number, entityNum: number) { const generatedMap = metadata.generatedColumns.reduce((map, generatedColumn) => { let value: any; if (generatedColumn.generationStrategy === "increment" && insertResult) { - value = insertResult; + value = insertResult - entityNum + entityIndex + 1; // } else if (generatedColumn.generationStrategy === "uuid") { // value = insertValue[generatedColumn.databaseName]; } diff --git a/src/query-builder/ReturningResultsEntityUpdator.ts b/src/query-builder/ReturningResultsEntityUpdator.ts index 7dcd9f100d..c12e4794b5 100644 --- a/src/query-builder/ReturningResultsEntityUpdator.ts +++ b/src/query-builder/ReturningResultsEntityUpdator.ts @@ -93,7 +93,7 @@ export class ReturningResultsEntityUpdator { } // get all values generated by a database for us const result = Array.isArray(insertResult.raw) ? insertResult.raw[entityIndex] : insertResult.raw; - const generatedMap = this.queryRunner.connection.driver.createGeneratedMap(metadata, result) || {}; + const generatedMap = this.queryRunner.connection.driver.createGeneratedMap(metadata, result, entityIndex, entities.length) || {}; // if database does not support uuid generation we need to get uuid values // generated by orm and set them to the generatedMap From 29ee43d64ec1613dc9d452ef2d42a117bc710ae1 Mon Sep 17 00:00:00 2001 From: Jiwon Choi <1890mah@gmail.com> Date: Fri, 4 Sep 2020 12:28:26 +0900 Subject: [PATCH 2/4] fix: (mysql) get correct increment primary key for mutiple entities inserted Closes: #5973 --- src/driver/mysql/MysqlDriver.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/driver/mysql/MysqlDriver.ts b/src/driver/mysql/MysqlDriver.ts index 291beaa68f..c9a9a79af9 100644 --- a/src/driver/mysql/MysqlDriver.ts +++ b/src/driver/mysql/MysqlDriver.ts @@ -709,11 +709,13 @@ export class MysqlDriver implements Driver { /** * Creates generated map of values generated or returned by database after INSERT query. */ - createGeneratedMap(metadata: EntityMetadata, insertResult: any) { + createGeneratedMap(metadata: EntityMetadata, insertResult: any, entityIndex: number) { const generatedMap = metadata.generatedColumns.reduce((map, generatedColumn) => { let value: any; if (generatedColumn.generationStrategy === "increment" && insertResult.insertId) { - value = insertResult.insertId; + // When multiple rows is inserted by a single INSERT statement, + // `insertId` is the value generated for the first inserted row only. + value = insertResult.insertId + entityIndex; // } else if (generatedColumn.generationStrategy === "uuid") { // console.log("getting db value:", generatedColumn.databaseName); // value = generatedColumn.getEntityValue(uuidMap); From 277a0dec655deaeaec82bde245839bd73bc01fb2 Mon Sep 17 00:00:00 2001 From: Jiwon Choi <1890mah@gmail.com> Date: Fri, 4 Sep 2020 12:32:30 +0900 Subject: [PATCH 3/4] test: add test case for fix of #2131 --- test/github-issues/2131/entity/Post.ts | 12 ++++++ test/github-issues/2131/issue-2131.ts | 53 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/github-issues/2131/entity/Post.ts create mode 100644 test/github-issues/2131/issue-2131.ts diff --git a/test/github-issues/2131/entity/Post.ts b/test/github-issues/2131/entity/Post.ts new file mode 100644 index 0000000000..2004a60966 --- /dev/null +++ b/test/github-issues/2131/entity/Post.ts @@ -0,0 +1,12 @@ +import { PrimaryGeneratedColumn, Entity, Column } from "../../../../src"; + +@Entity() +export class Post { + + @PrimaryGeneratedColumn() + id: number | null; + + @Column() + title: string; + +} diff --git a/test/github-issues/2131/issue-2131.ts b/test/github-issues/2131/issue-2131.ts new file mode 100644 index 0000000000..6d67a4aaf8 --- /dev/null +++ b/test/github-issues/2131/issue-2131.ts @@ -0,0 +1,53 @@ +import "reflect-metadata"; +import { + closeTestingConnections, + createTestingConnections, + reloadTestingDatabases, +} from "../../utils/test-utils"; +import { Connection } from "../../../src/connection/Connection"; +import { expect } from "chai"; +import { Post } from "./entity/Post"; + +describe("github issues > #2131 InsertResult return the same primary key", () => { + let connections: Connection[]; + const posts: Post[] = [{ + id: null, + title: "Post 1", + }, { + id: null, + title: "Post 2", + }, { + id: null, + title: "Post 3", + }, { + id: null, + title: "Post 4", + }]; + + before( + async () => + (connections = await createTestingConnections({ + entities: [__dirname + "/entity/*{.js,.ts}"], + enabledDrivers: ["sqlite", "mysql"], + })) + ); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + it("should get correct insert ids for multiple entities inserted", () => + Promise.all( + connections.map(async (connection) => { + await connection + .createQueryBuilder() + .insert() + .into(Post) + .values(posts) + .execute(); + + expect(posts[0].id).to.equal(1); + expect(posts[1].id).to.equal(2); + expect(posts[2].id).to.equal(3); + expect(posts[3].id).to.equal(4); + }) + )); +}); From 1a6eb26094397f816fedddbe07b52f59ed2d6003 Mon Sep 17 00:00:00 2001 From: Jiwon Choi <1890mah@gmail.com> Date: Fri, 4 Sep 2020 17:42:47 +0900 Subject: [PATCH 4/4] docs: update note about sqlite lastID --- src/driver/mysql/MysqlDriver.ts | 2 +- src/driver/sqlite-abstract/AbstractSqliteDriver.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/driver/mysql/MysqlDriver.ts b/src/driver/mysql/MysqlDriver.ts index c9a9a79af9..8d64db58c8 100644 --- a/src/driver/mysql/MysqlDriver.ts +++ b/src/driver/mysql/MysqlDriver.ts @@ -713,7 +713,7 @@ export class MysqlDriver implements Driver { const generatedMap = metadata.generatedColumns.reduce((map, generatedColumn) => { let value: any; if (generatedColumn.generationStrategy === "increment" && insertResult.insertId) { - // When multiple rows is inserted by a single INSERT statement, + // NOTE: When multiple rows is inserted by a single INSERT statement, // `insertId` is the value generated for the first inserted row only. value = insertResult.insertId + entityIndex; // } else if (generatedColumn.generationStrategy === "uuid") { diff --git a/src/driver/sqlite-abstract/AbstractSqliteDriver.ts b/src/driver/sqlite-abstract/AbstractSqliteDriver.ts index 849393bf78..5cd7eed7bf 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteDriver.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteDriver.ts @@ -511,6 +511,8 @@ export abstract class AbstractSqliteDriver implements Driver { const generatedMap = metadata.generatedColumns.reduce((map, generatedColumn) => { let value: any; if (generatedColumn.generationStrategy === "increment" && insertResult) { + // NOTE: When INSERT statement is successfully completed, the last inserted row ID is returned. + // see also: SqliteQueryRunner.query() value = insertResult - entityNum + entityIndex + 1; // } else if (generatedColumn.generationStrategy === "uuid") { // value = insertValue[generatedColumn.databaseName];