diff --git a/src/decorator/entity/Entity.ts b/src/decorator/entity/Entity.ts index 085ca73e65..bb2af675d0 100644 --- a/src/decorator/entity/Entity.ts +++ b/src/decorator/entity/Entity.ts @@ -30,7 +30,8 @@ export function Entity(nameOrOptions?: string|EntityOptions, maybeOptions?: Enti engine: options.engine ? options.engine : undefined, database: options.database ? options.database : undefined, schema: options.schema ? options.schema : undefined, - synchronize: options.synchronize + synchronize: options.synchronize, + withoutRowid: options.withoutRowid } as TableMetadataArgs); }; } diff --git a/src/decorator/options/EntityOptions.ts b/src/decorator/options/EntityOptions.ts index 532119726a..be2b6007d6 100644 --- a/src/decorator/options/EntityOptions.ts +++ b/src/decorator/options/EntityOptions.ts @@ -40,4 +40,11 @@ export interface EntityOptions { * By default schema synchronization is enabled for all entities. */ synchronize?: boolean; + + /** + * If set to 'true' this option disables Sqlite's default behaviour of secretly creating + * an integer primary key column named 'rowid' on table creation. + * @see https://www.sqlite.org/withoutrowid.html. + */ + withoutRowid?: boolean; } diff --git a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts index e7587ecb92..c361695c2c 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts @@ -997,6 +997,11 @@ export abstract class AbstractSqliteQueryRunner extends BaseQueryRunner implemen sql += `)`; + const tableMetadata = this.connection.entityMetadatas.find(metadata => metadata.tableName === table.name); + if (tableMetadata && tableMetadata.withoutRowid) { + sql += " WITHOUT ROWID"; + } + return new Query(sql); } diff --git a/src/metadata-args/TableMetadataArgs.ts b/src/metadata-args/TableMetadataArgs.ts index 7cb02b1064..ac768d0c00 100644 --- a/src/metadata-args/TableMetadataArgs.ts +++ b/src/metadata-args/TableMetadataArgs.ts @@ -59,7 +59,11 @@ export interface TableMetadataArgs { /** * Indicates if view is materialized */ - materialized?: boolean; + /** + * If set to 'true' this option disables Sqlite's default behaviour of secretly creating + * an integer primary key column named 'rowid' on table creation. + */ + withoutRowid?: boolean; } diff --git a/src/metadata/EntityMetadata.ts b/src/metadata/EntityMetadata.ts index 0d297fee00..f582164214 100644 --- a/src/metadata/EntityMetadata.ts +++ b/src/metadata/EntityMetadata.ts @@ -101,6 +101,11 @@ export class EntityMetadata { */ expression?: string|((connection: Connection) => SelectQueryBuilder); + /** + * Enables Sqlite "WITHOUT ROWID" modifier for the "CREATE TABLE" statement + */ + withoutRowid?: boolean = false; + /** * Original user-given table name (taken from schema or @Entity(tableName) decorator). * If user haven't specified a table name this property will be undefined. @@ -496,6 +501,7 @@ export class EntityMetadata { this.target = this.tableMetadataArgs.target; this.tableType = this.tableMetadataArgs.type; this.expression = this.tableMetadataArgs.expression; + this.withoutRowid = this.tableMetadataArgs.withoutRowid; } // ------------------------------------------------------------------------- @@ -794,6 +800,7 @@ export class EntityMetadata { this.target = this.target ? this.target : this.tableName; this.name = this.targetName ? this.targetName : this.tableName; this.expression = this.tableMetadataArgs.expression; + this.withoutRowid = this.tableMetadataArgs.withoutRowid === true ? true : false; this.tablePath = this.buildTablePath(); this.schemaPath = this.buildSchemaPath(); this.orderBy = (this.tableMetadataArgs.orderBy instanceof Function) ? this.tableMetadataArgs.orderBy(this.propertiesMap) : this.tableMetadataArgs.orderBy; // todo: is propertiesMap available here? Looks like its not diff --git a/test/functional/query-runner/create-table.ts b/test/functional/query-runner/create-table.ts index d4bb8608c1..7f65f34e3b 100644 --- a/test/functional/query-runner/create-table.ts +++ b/test/functional/query-runner/create-table.ts @@ -10,6 +10,8 @@ import {MysqlDriver} from "../../../src/driver/mysql/MysqlDriver"; import {AbstractSqliteDriver} from "../../../src/driver/sqlite-abstract/AbstractSqliteDriver"; import {OracleDriver} from "../../../src/driver/oracle/OracleDriver"; import {Photo} from "./entity/Photo"; +import {Book2, Book} from "./entity/Book"; +import {SqliteDriver} from "../../../src/driver/sqlite/SqliteDriver"; describe("query runner > create table", () => { @@ -327,4 +329,43 @@ describe("query runner > create table", () => { await queryRunner.release(); }))); + it("should correctly create table with different `withoutRowid` definitions", () => Promise.all(connections.map(async connection => { + + if (connection.driver instanceof SqliteDriver) { + const queryRunner = connection.createQueryRunner(); + + // the table 'book' must contain a 'rowid' column + const metadataBook = connection.getMetadata(Book); + const newTableBook = Table.create(metadataBook, connection.driver); + await queryRunner.createTable(newTableBook); + const aBook = new Book(); + aBook.ean = "asdf"; + await connection.manager.save(aBook); + + const desc = await connection.manager.query("SELECT rowid FROM book WHERE ean = 'asdf'"); + expect(desc[0].rowid).equals(1); + + await queryRunner.dropTable("book"); + const bookTableIsGone = await queryRunner.getTable("book"); + expect(bookTableIsGone).to.be.undefined; + + // the table 'book2' must NOT contain a 'rowid' column + const metadataBook2 = connection.getMetadata(Book2); + const newTableBook2 = Table.create(metadataBook2, connection.driver); + await queryRunner.createTable(newTableBook2); + + try { + await connection.manager.query("SELECT rowid FROM book2"); + } catch (e) { + expect(e.message).equal("SQLITE_ERROR: no such column: rowid"); + } + + await queryRunner.dropTable("book2"); + const book2TableIsGone = await queryRunner.getTable("book2"); + expect(book2TableIsGone).to.be.undefined; + + await queryRunner.release(); + } + }))); + }); diff --git a/test/functional/query-runner/entity/Book.ts b/test/functional/query-runner/entity/Book.ts new file mode 100644 index 0000000000..8ddd0c5954 --- /dev/null +++ b/test/functional/query-runner/entity/Book.ts @@ -0,0 +1,19 @@ +import { PrimaryColumn } from "../../../../src/decorator/columns/PrimaryColumn"; +import { Entity } from "../../../../src/decorator/entity/Entity"; + +@Entity() +export class Book { + + @PrimaryColumn() + ean: string; + +} + +@Entity({ withoutRowid: true }) +export class Book2 { + + @PrimaryColumn() + ean: string; + +} +