diff --git a/src/decorator/Index.ts b/src/decorator/Index.ts index c4c9aa007..f6781c318 100644 --- a/src/decorator/Index.ts +++ b/src/decorator/Index.ts @@ -49,7 +49,8 @@ export function Index(nameOrFieldsOrOptions?: string|string[]|((object: any) => target: propertyName ? clsOrObject.constructor : clsOrObject as Function, name: name, columns: propertyName ? [propertyName] : fields, - unique: options && options.unique ? true : false + unique: options && options.unique ? true : false, + sparse: options && options.sparse ? true : false }; getMetadataArgsStorage().indices.push(args); }; diff --git a/src/driver/mysql/MysqlQueryRunner.ts b/src/driver/mysql/MysqlQueryRunner.ts index 9eecac155..9380c69f9 100644 --- a/src/driver/mysql/MysqlQueryRunner.ts +++ b/src/driver/mysql/MysqlQueryRunner.ts @@ -327,7 +327,7 @@ export class MysqlQueryRunner implements QueryRunner { const tableNamesString = tableNames.map(tableName => `'${tableName}'`).join(", "); const tablesSql = `SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME IN (${tableNamesString})`; const columnsSql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}'`; - const indicesSql = `SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = '${this.dbName}' AND INDEX_NAME != 'PRIMARY'`; + const indicesSql = `SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = '${this.dbName}' AND INDEX_NAME != 'PRIMARY' ORDER BY SEQ_IN_INDEX`; const foreignKeysSql = `SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = '${this.dbName}' AND REFERENCED_COLUMN_NAME IS NOT NULL`; const [dbTables, dbColumns, dbIndices, dbForeignKeys]: ObjectLiteral[][] = await Promise.all([ this.query(tablesSql), @@ -425,7 +425,7 @@ export class MysqlQueryRunner implements QueryRunner { } } - return new IndexSchema(dbTable["TABLE_NAME"], dbIndexName, columnNames, false /* todo: uniqueness */); + return new IndexSchema(dbTable["TABLE_NAME"], dbIndexName, columnNames, currentDbIndices[0]["NON_UNIQUE"] === 0); }) .filter(index => !!index) as IndexSchema[]; // remove empty returns diff --git a/src/driver/postgres/PostgresQueryRunner.ts b/src/driver/postgres/PostgresQueryRunner.ts index 1c91a71bd..6bb2816b0 100644 --- a/src/driver/postgres/PostgresQueryRunner.ts +++ b/src/driver/postgres/PostgresQueryRunner.ts @@ -336,7 +336,7 @@ export class PostgresQueryRunner implements QueryRunner { const tableNamesString = tableNames.map(name => "'" + name + "'").join(", "); const tablesSql = `SELECT * FROM information_schema.tables WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}' AND table_name IN (${tableNamesString})`; const columnsSql = `SELECT * FROM information_schema.columns WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}'`; - const indicesSql = `SELECT t.relname AS table_name, i.relname AS index_name, a.attname AS column_name FROM pg_class t, pg_class i, pg_index ix, pg_attribute a, pg_namespace ns + const indicesSql = `SELECT t.relname AS table_name, i.relname AS index_name, a.attname AS column_name, ix.indisunique AS is_unique, a.attnum, ix.indkey FROM pg_class t, pg_class i, pg_index ix, pg_attribute a, pg_namespace ns WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) AND t.relkind = 'r' AND t.relname IN (${tableNamesString}) AND t.relnamespace = ns.OID AND ns.nspname ='${this.schemaName}' ORDER BY t.relname, i.relname`; const foreignKeysSql = `SELECT table_name, constraint_name FROM information_schema.table_constraints WHERE table_catalog = '${this.dbName}' AND table_schema = '${this.schemaName}' AND constraint_type = 'FOREIGN KEY'`; @@ -427,11 +427,14 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}' .map(dbIndex => dbIndex["index_name"]) .filter((value, index, self) => self.indexOf(value) === index) // unqiue .map(dbIndexName => { - const columnNames = dbIndices - .filter(dbIndex => dbIndex["table_name"] === tableSchema.name && dbIndex["index_name"] === dbIndexName) - .map(dbIndex => dbIndex["column_name"]); + const dbIndicesInfos = dbIndices + .filter(dbIndex => dbIndex["table_name"] === tableSchema.name && dbIndex["index_name"] === dbIndexName); + const columnPositions = dbIndicesInfos[0]["indkey"].split(" ") + .map((x: string) => parseInt(x)); + const columnNames = columnPositions + .map((pos: number) => dbIndicesInfos.find(idx => idx.attnum === pos)!["column_name"]); - return new IndexSchema(dbTable["TABLE_NAME"], dbIndexName, columnNames, false /* todo: uniqueness */); + return new IndexSchema(dbTable["TABLE_NAME"], dbIndexName, columnNames, dbIndicesInfos[0]["is_unique"]); }); return tableSchema; diff --git a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts index 645386767..aed976382 100644 --- a/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts +++ b/src/driver/sqlite-abstract/AbstractSqliteQueryRunner.ts @@ -300,7 +300,9 @@ export class AbstractSqliteQueryRunner implements QueryRunner { .map(async dbIndexName => { const dbIndex = dbIndices.find(dbIndex => dbIndex["name"] === dbIndexName); const indexInfos: ObjectLiteral[] = await this.query(`PRAGMA index_info("${dbIndex!["name"]}")`); - const indexColumns = indexInfos.map(indexInfo => indexInfo["name"]); + const indexColumns = indexInfos + .sort((indexInfo1, indexInfo2) => parseInt(indexInfo1["seqno"]) - parseInt(indexInfo2["seqno"])) + .map(indexInfo => indexInfo["name"]); // check if db index is generated by sqlite itself and has special use case if (dbIndex!["name"].substr(0, "sqlite_autoindex".length) === "sqlite_autoindex") { @@ -316,7 +318,8 @@ export class AbstractSqliteQueryRunner implements QueryRunner { return Promise.resolve(undefined); } else { - return new IndexSchema(dbTable["name"], dbIndex!["name"], indexColumns, dbIndex!["unique"] === "1"); + const isUnique = dbIndex!["unique"] === "1" || dbIndex!["unique"] === 1; + return new IndexSchema(dbTable["name"], dbIndex!["name"], indexColumns, isUnique); } }); diff --git a/src/driver/sqlserver/SqlServerQueryRunner.ts b/src/driver/sqlserver/SqlServerQueryRunner.ts index b7185ee49..a50134747 100644 --- a/src/driver/sqlserver/SqlServerQueryRunner.ts +++ b/src/driver/sqlserver/SqlServerQueryRunner.ts @@ -497,9 +497,9 @@ export class SqlServerQueryRunner implements QueryRunner { `LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tableConstraints ON tableConstraints.CONSTRAINT_NAME = columnUsages.CONSTRAINT_NAME ` + `WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_CATALOG = '${this.dbName}'`; const identityColumnsSql = `SELECT COLUMN_NAME, TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = '${this.dbName}' AND COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1;`; - const indicesSql = `SELECT TABLE_NAME = t.name, INDEX_NAME = ind.name, IndexId = ind.index_id, ColumnId = ic.index_column_id, COLUMN_NAME = col.name, ind.*, ic.*, col.* ` + + const indicesSql = `SELECT TABLE_NAME = t.name, INDEX_NAME = ind.name, IndexId = ind.index_id, ColumnId = ic.index_column_id, COLUMN_NAME = col.name, IS_UNIQUE = ind.is_unique, ind.*, ic.*, col.* ` + `FROM sys.indexes ind INNER JOIN sys.index_columns ic ON ind.object_id = ic.object_id and ind.index_id = ic.index_id INNER JOIN sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id ` + -`INNER JOIN sys.tables t ON ind.object_id = t.object_id WHERE ind.is_primary_key = 0 AND ind.is_unique = 0 AND ind.is_unique_constraint = 0 AND t.is_ms_shipped = 0 ORDER BY t.name, ind.name, ind.index_id, ic.index_column_id`; +`INNER JOIN sys.tables t ON ind.object_id = t.object_id WHERE ind.is_primary_key = 0 AND ind.is_unique_constraint = 0 AND t.is_ms_shipped = 0 ORDER BY t.name, ind.name, ind.index_id, ic.index_column_id`; const [dbTables, dbColumns, dbConstraints, dbIdentityColumns, dbIndices]: ObjectLiteral[][] = await Promise.all([ this.query(tablesSql), this.query(columnsSql), @@ -590,7 +590,8 @@ export class SqlServerQueryRunner implements QueryRunner { .filter(dbIndex => dbIndex["TABLE_NAME"] === tableSchema.name && dbIndex["INDEX_NAME"] === dbIndexName) .map(dbIndex => dbIndex["COLUMN_NAME"]); - return new IndexSchema(dbTable["TABLE_NAME"], dbIndexName, columnNames, false /* todo: uniqueness? */); + const isUnique = !!dbIndices.find(dbIndex => dbIndex["TABLE_NAME"] === tableSchema.name && dbIndex["INDEX_NAME"] === dbIndexName && dbIndex["IS_UNIQUE"] === true); + return new IndexSchema(dbTable["TABLE_NAME"], dbIndexName, columnNames, isUnique); }); return tableSchema; diff --git a/src/entity-schema/EntitySchema.ts b/src/entity-schema/EntitySchema.ts index 325b013dc..456d0a6f1 100644 --- a/src/entity-schema/EntitySchema.ts +++ b/src/entity-schema/EntitySchema.ts @@ -247,4 +247,29 @@ export interface EntitySchema { // todo: make it-to-date }; }; + /** + * Entity indices options. + */ + indices: { + [indexName: string]: { + + /** + * Index column names. + */ + columns: string[]; + + /** + * Indicates if this index must be unique or not. + */ + unique: boolean; + + /** + * If true, the index only references documents with the specified field. + * These indexes use less space but behave differently in some situations (particularly sorts). + * This option is only supported for mongodb database. + */ + sparse?: boolean; + }; + }; + } \ No newline at end of file diff --git a/src/entity-schema/EntitySchemaTransformer.ts b/src/entity-schema/EntitySchemaTransformer.ts index e89cc9e55..5745f218b 100644 --- a/src/entity-schema/EntitySchemaTransformer.ts +++ b/src/entity-schema/EntitySchemaTransformer.ts @@ -2,6 +2,7 @@ import {EntitySchema} from "./EntitySchema"; import {MetadataArgsStorage} from "../metadata-args/MetadataArgsStorage"; import {TableMetadataArgs} from "../metadata-args/TableMetadataArgs"; import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs"; +import {IndexMetadataArgs} from "../metadata-args/IndexMetadataArgs"; import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs"; import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs"; import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs"; @@ -147,6 +148,22 @@ export class EntitySchemaTransformer { } }); } + + // add relation metadata args from the schema + if (schema.indices) { + Object.keys(schema.indices).forEach(indexName => { + const indexSchema = schema.indices[indexName]; + const indexAgrs: IndexMetadataArgs = { + target: schema.target || schema.name, + name: indexName, + unique: indexSchema.unique, + sparse: indexSchema.sparse, + columns: indexSchema.columns + }; + metadataArgsStorage.indices.push(indexAgrs); + }); + } + }); return metadataArgsStorage; diff --git a/src/metadata-args/IndexMetadataArgs.ts b/src/metadata-args/IndexMetadataArgs.ts index 65b64cd8f..c0d6e7366 100644 --- a/src/metadata-args/IndexMetadataArgs.ts +++ b/src/metadata-args/IndexMetadataArgs.ts @@ -23,4 +23,10 @@ export interface IndexMetadataArgs { */ unique: boolean; + /** + * If true, the index only references documents with the specified field. + * These indexes use less space but behave differently in some situations (particularly sorts). + * This option is only supported for mongodb database. + */ + sparse?: boolean; } diff --git a/src/metadata/IndexMetadata.ts b/src/metadata/IndexMetadata.ts index 6fe8a4950..175705058 100644 --- a/src/metadata/IndexMetadata.ts +++ b/src/metadata/IndexMetadata.ts @@ -22,6 +22,13 @@ export class IndexMetadata { */ isUnique: boolean = false; + /** + * If true, the index only references documents with the specified field. + * These indexes use less space but behave differently in some situations (particularly sorts). + * This option is only supported for mongodb database. + */ + isSparse?: boolean; + /** * Target class to which metadata is applied. */ @@ -76,6 +83,7 @@ export class IndexMetadata { if (options.args) { this.target = options.args.target; this.isUnique = options.args.unique; + this.isSparse = options.args.sparse; this.givenName = options.args.name; this.givenColumnNames = options.args.columns; } diff --git a/src/schema-builder/MongoSchemaBuilder.ts b/src/schema-builder/MongoSchemaBuilder.ts index 74ae59aed..f6727236d 100644 --- a/src/schema-builder/MongoSchemaBuilder.ts +++ b/src/schema-builder/MongoSchemaBuilder.ts @@ -37,7 +37,7 @@ export class MongoSchemaBuilder implements SchemaBuilder { const promises: Promise[] = []; this.connection.entityMetadatas.forEach(metadata => { metadata.indices.forEach(index => { - const options = { name: index.name, unique: index.isUnique }; + const options = { name: index.name, unique: index.isUnique, sparse: index.isSparse }; promises.push(queryRunner.createCollectionIndex(metadata.tableName, index.columnNamesWithOrderingMap, options)); }); }); diff --git a/src/schema-builder/RdbmsSchemaBuilder.ts b/src/schema-builder/RdbmsSchemaBuilder.ts index 5e4cea493..6a0c4f7c6 100644 --- a/src/schema-builder/RdbmsSchemaBuilder.ts +++ b/src/schema-builder/RdbmsSchemaBuilder.ts @@ -357,13 +357,27 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { // drop all indices that exist in the table, but does not exist in the given composite indices const dropQueries = tableSchema.indices - .filter(indexSchema => !metadata.indices.find(indexMetadata => indexMetadata.name === indexSchema.name)) + .filter(indexSchema => { + const metadataIndex = metadata.indices.find(indexMetadata => indexMetadata.name === indexSchema.name); + if (!metadataIndex) + return true; + if (metadataIndex.isUnique !== indexSchema.isUnique) + return true; + if (metadataIndex.columns.length !== indexSchema.columnNames.length) + return true; + if (metadataIndex.columns.findIndex((col, i) => col.databaseName !== indexSchema.columnNames[i]) !== -1) + return true; + + return false; + }) .map(async indexSchema => { this.connection.logger.logSchemaBuild(`dropping an index: ${indexSchema.name}`); tableSchema.removeIndex(indexSchema); await this.queryRunner.dropIndex(metadata.tableName, indexSchema.name); }); + await Promise.all(dropQueries); + // then create table indices for all composite indices we have const addQueries = metadata.indices .filter(indexMetadata => !tableSchema.indices.find(indexSchema => indexSchema.name === indexMetadata.name)) @@ -374,7 +388,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder { await this.queryRunner.createIndex(indexSchema.tableName, indexSchema); }); - await Promise.all(dropQueries.concat(addQueries)); + await Promise.all(addQueries); }); } diff --git a/test/functional/database-schema/indices/entity/Person.ts b/test/functional/database-schema/indices/entity/Person.ts new file mode 100644 index 000000000..646bad4df --- /dev/null +++ b/test/functional/database-schema/indices/entity/Person.ts @@ -0,0 +1,19 @@ +import {Entity} from "../../../../../src/decorator/entity/Entity"; +import {PrimaryGeneratedColumn} from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"; +import {Column} from "../../../../../src/decorator/columns/Column"; +import {Index} from "../../../../../src/decorator/Index"; + +@Entity() +@Index("IDX_TEST", ["firstname", "lastname"]) +export class Person { + + @PrimaryGeneratedColumn() + id: number; + + @Column() + firstname: string; + + @Column() + lastname: string; + +} \ No newline at end of file diff --git a/test/functional/database-schema/indices/indices-create-modify.ts b/test/functional/database-schema/indices/indices-create-modify.ts new file mode 100644 index 000000000..e24b55be3 --- /dev/null +++ b/test/functional/database-schema/indices/indices-create-modify.ts @@ -0,0 +1,90 @@ +import "reflect-metadata"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; +import {Connection} from "../../../../src/connection/Connection"; +import {expect} from "chai"; +import {EntityMetadata} from "../../../../src/metadata/EntityMetadata"; +import {IndexMetadata} from "../../../../src/metadata/IndexMetadata"; + +import {Person} from "./entity/Person"; + +describe("indices > reading index from entity schema and updating database", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entities: [Person], + schemaCreate: true, + dropSchema: true + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + describe("create index", function() { + + it("should create a non unique index with 2 columns", () => Promise.all(connections.map(async connection => { + + const queryRunner = connection.createQueryRunner(); + const tableSchema = await queryRunner.loadTableSchema("person"); + await queryRunner.release(); + + expect(tableSchema!.indices.length).to.be.equal(1); + expect(tableSchema!.indices[0].name).to.be.equal("IDX_TEST"); + expect(tableSchema!.indices[0].isUnique).to.be.false; + expect(tableSchema!.indices[0].columnNames.length).to.be.equal(2); + expect(tableSchema!.indices[0].columnNames[0]).to.be.equal("firstname"); + expect(tableSchema!.indices[0].columnNames[1]).to.be.equal("lastname"); + + }))); + + it("should update the index to be unique", () => Promise.all(connections.map(async connection => { + + const entityMetadata = connection.entityMetadatas.find(x => x.name === "Person"); + const indexMetadata = entityMetadata!.indices.find(x => x.name === "IDX_TEST"); + indexMetadata!.isUnique = true; + + await connection.synchronize(false); + + const queryRunner = connection.createQueryRunner(); + const tableSchema = await queryRunner.loadTableSchema("person"); + await queryRunner.release(); + + expect(tableSchema!.indices.length).to.be.equal(1); + expect(tableSchema!.indices[0].name).to.be.equal("IDX_TEST"); + expect(tableSchema!.indices[0].isUnique).to.be.true; + expect(tableSchema!.indices[0].columnNames.length).to.be.equal(2); + expect(tableSchema!.indices[0].columnNames[0]).to.be.equal("firstname"); + expect(tableSchema!.indices[0].columnNames[1]).to.be.equal("lastname"); + + }))); + + it("should update the index swaping the 2 columns", () => Promise.all(connections.map(async connection => { + + const entityMetadata = connection.entityMetadatas.find(x => x.name === "Person"); + entityMetadata!.indices = [new IndexMetadata({ + entityMetadata: entityMetadata, + args: { + target: Person, + name: "IDX_TEST", + columns: ["lastname", "firstname"], + unique: false + } + })]; + entityMetadata!.indices.forEach(index => index.build(connection.namingStrategy)); + + await connection.synchronize(false); + + const queryRunner = connection.createQueryRunner(); + const tableSchema = await queryRunner.loadTableSchema("person"); + await queryRunner.release(); + + expect(tableSchema!.indices.length).to.be.equal(1); + expect(tableSchema!.indices[0].name).to.be.equal("IDX_TEST"); + expect(tableSchema!.indices[0].isUnique).to.be.false; + expect(tableSchema!.indices[0].columnNames.length).to.be.equal(2); + expect(tableSchema!.indices[0].columnNames[0]).to.be.equal("lastname"); + expect(tableSchema!.indices[0].columnNames[1]).to.be.equal("firstname"); + + }))); + + }); + +}); diff --git a/test/functional/entity-schema/indices/entity/Person.ts b/test/functional/entity-schema/indices/entity/Person.ts new file mode 100644 index 000000000..9e5a68e46 --- /dev/null +++ b/test/functional/entity-schema/indices/entity/Person.ts @@ -0,0 +1,29 @@ +export const PersonSchema = { + name: "Person", + columns: { + Id: { + primary: true, + type: "int", + generated: "increment" + }, + FirstName: { + type: String, + length: 30 + }, + LastName: { + type: String, + length: 50, + nullable: false + } + }, + relations: {}, + indices: { + IDX_TEST: { + unique: false, + columns: [ + "FirstName", + "LastName" + ] + } + } +}; \ No newline at end of file diff --git a/test/functional/entity-schema/indices/indices-test.ts b/test/functional/entity-schema/indices/indices-test.ts new file mode 100644 index 000000000..37436331e --- /dev/null +++ b/test/functional/entity-schema/indices/indices-test.ts @@ -0,0 +1,90 @@ +import "reflect-metadata"; +import {closeTestingConnections, createTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils"; +import {Connection} from "../../../../src/connection/Connection"; +import {EntityMetadata} from "../../../../src/metadata/EntityMetadata"; +import {IndexMetadata} from "../../../../src/metadata/IndexMetadata"; +import {expect} from "chai"; + +import {PersonSchema} from "./entity/Person"; + +describe("indices > reading index from entity schema and updating database", () => { + + let connections: Connection[]; + before(async () => connections = await createTestingConnections({ + entitySchemas: [PersonSchema], + schemaCreate: true, + dropSchema: true + })); + beforeEach(() => reloadTestingDatabases(connections)); + after(() => closeTestingConnections(connections)); + + describe("create index", function() { + + it("should create a non unique index with 2 columns", () => Promise.all(connections.map(async connection => { + + const queryRunner = connection.createQueryRunner(); + const tableSchema = await queryRunner.loadTableSchema("person"); + await queryRunner.release(); + + expect(tableSchema!.indices.length).to.be.equal(1); + expect(tableSchema!.indices[0].name).to.be.equal("IDX_TEST"); + expect(tableSchema!.indices[0].isUnique).to.be.false; + expect(tableSchema!.indices[0].columnNames.length).to.be.equal(2); + expect(tableSchema!.indices[0].columnNames[0]).to.be.equal("FirstName"); + expect(tableSchema!.indices[0].columnNames[1]).to.be.equal("LastName"); + + }))); + + it("should update the index to be unique", () => Promise.all(connections.map(async connection => { + + const entityMetadata = connection.entityMetadatas.find(x => x.name === "Person"); + const indexMetadata = entityMetadata!.indices.find(x => x.name === "IDX_TEST"); + indexMetadata!.isUnique = true; + + await connection.synchronize(false); + + const queryRunner = connection.createQueryRunner(); + const tableSchema = await queryRunner.loadTableSchema("person"); + await queryRunner.release(); + + expect(tableSchema!.indices.length).to.be.equal(1); + expect(tableSchema!.indices[0].name).to.be.equal("IDX_TEST"); + expect(tableSchema!.indices[0].isUnique).to.be.true; + expect(tableSchema!.indices[0].columnNames.length).to.be.equal(2); + expect(tableSchema!.indices[0].columnNames[0]).to.be.equal("FirstName"); + expect(tableSchema!.indices[0].columnNames[1]).to.be.equal("LastName"); + + }))); + + it("should update the index swaping the 2 columns", () => Promise.all(connections.map(async connection => { + + const entityMetadata = connection.entityMetadatas.find(x => x.name === "Person"); + entityMetadata!.indices = [new IndexMetadata({ + entityMetadata: entityMetadata, + args: { + target: entityMetadata!.target, + name: "IDX_TEST", + columns: ["LastName", "FirstName"], + unique: false + } + })]; + entityMetadata!.indices.forEach(index => index.build(connection.namingStrategy)); + + await connection.synchronize(false); + + const queryRunner = connection.createQueryRunner(); + const tableSchema = await queryRunner.loadTableSchema("person"); + await queryRunner.release(); + + expect(tableSchema!.indices.length).to.be.equal(1); + expect(tableSchema!.indices[0].name).to.be.equal("IDX_TEST"); + expect(tableSchema!.indices[0].isUnique).to.be.false; + expect(tableSchema!.indices[0].columnNames.length).to.be.equal(2); + expect(tableSchema!.indices[0].columnNames[0]).to.be.equal("LastName"); + expect(tableSchema!.indices[0].columnNames[1]).to.be.equal("FirstName"); + + }))); + + }); + +}); diff --git a/test/utils/test-utils.ts b/test/utils/test-utils.ts index 62c58e49d..9f2a3ceba 100644 --- a/test/utils/test-utils.ts +++ b/test/utils/test-utils.ts @@ -50,7 +50,7 @@ export interface TestingOptions { /** * Entity schemas needs to be included in the connection for the given test suite. */ - entitySchemas?: EntitySchema[]; + entitySchemas?: string[]|EntitySchema[]; /** * Indicates if schema sync should be performed or not. @@ -141,8 +141,8 @@ export function setupTestingConnections(options?: TestingOptions): ConnectionOpt entities: options && options.entities ? options.entities : [], subscribers: options && options.subscribers ? options.subscribers : [], entitySchemas: options && options.entitySchemas ? options.entitySchemas : [], - autoSchemaSync: options && options.entities ? options.schemaCreate : false, - dropSchema: options && options.entities ? options.dropSchema : false, + autoSchemaSync: options && (options.entities || options.entitySchemas) ? options.schemaCreate : false, + dropSchema: options && (options.entities || options.entitySchemas) ? options.dropSchema : false, schema: options && options.schema ? options.schema : undefined, }); });