var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [0, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import { Table } from "./table/Table";
import { TableColumn } from "./table/TableColumn";
import { TableForeignKey } from "./table/TableForeignKey";
import { TableIndex } from "./table/TableIndex";
import { PromiseUtils } from "../util/PromiseUtils";
import { TableUtils } from "./util/TableUtils";
import { MysqlDriver } from "../driver/mysql/MysqlDriver";
import { TableUnique } from "./table/TableUnique";
/**
 * Creates complete tables schemas in the database based on the entity metadatas.
 *
 * Steps how schema is being built:
 * 1. load list of all tables with complete column and keys information from the db
 * 2. drop all (old) foreign keys that exist in the table, but does not exist in the metadata
 * 3. create new tables that does not exist in the db, but exist in the metadata
 * 4. drop all columns exist (left old) in the db table, but does not exist in the metadata
 * 5. add columns from metadata which does not exist in the table
 * 6. update all exist columns which metadata has changed
 * 7. update primary keys - update old and create new primary key from changed columns
 * 8. create foreign keys which does not exist in the table yet
 * 9. create indices which are missing in db yet, and drops indices which exist in the db, but does not exist in the metadata anymore
 */
var RdbmsSchemaBuilder = /** @class */ (function () {
    // -------------------------------------------------------------------------
    // Constructor
    // -------------------------------------------------------------------------
    function RdbmsSchemaBuilder(connection) {
        this.connection = connection;
    }
    // -------------------------------------------------------------------------
    // Public Methods
    // -------------------------------------------------------------------------
    /**
     * Creates complete schemas for the given entity metadatas.
     */
    RdbmsSchemaBuilder.prototype.build = function () {
        return __awaiter(this, void 0, void 0, function () {
            var tablePaths, error_1, rollbackError_1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.queryRunner = this.connection.createQueryRunner("master");
                        return [4 /*yield*/, this.queryRunner.startTransaction()];
                    case 1:
                        _a.sent();
                        _a.label = 2;
                    case 2:
                        _a.trys.push([2, 8, 13, 15]);
                        tablePaths = this.entityToSyncMetadatas.map(function (metadata) { return metadata.tablePath; });
                        return [4 /*yield*/, this.queryRunner.getTables(tablePaths)];
                    case 3:
                        _a.sent();
                        return [4 /*yield*/, this.executeSchemaSyncOperationsInProperOrder()];
                    case 4:
                        _a.sent();
                        if (!this.connection.queryResultCache) return [3 /*break*/, 6];
                        return [4 /*yield*/, this.connection.queryResultCache.synchronize(this.queryRunner)];
                    case 5:
                        _a.sent();
                        _a.label = 6;
                    case 6: return [4 /*yield*/, this.queryRunner.commitTransaction()];
                    case 7:
                        _a.sent();
                        return [3 /*break*/, 15];
                    case 8:
                        error_1 = _a.sent();
                        _a.label = 9;
                    case 9:
                        _a.trys.push([9, 11, , 12]);
                        return [4 /*yield*/, this.queryRunner.rollbackTransaction()];
                    case 10:
                        _a.sent();
                        return [3 /*break*/, 12];
                    case 11:
                        rollbackError_1 = _a.sent();
                        return [3 /*break*/, 12];
                    case 12: throw error_1;
                    case 13: return [4 /*yield*/, this.queryRunner.release()];
                    case 14:
                        _a.sent();
                        return [7 /*endfinally*/];
                    case 15: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Returns sql queries to be executed by schema builder.
     */
    RdbmsSchemaBuilder.prototype.log = function () {
        return __awaiter(this, void 0, void 0, function () {
            var tablePaths;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.queryRunner = this.connection.createQueryRunner("master");
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, , 6, 8]);
                        tablePaths = this.entityToSyncMetadatas.map(function (metadata) { return metadata.tablePath; });
                        return [4 /*yield*/, this.queryRunner.getTables(tablePaths)];
                    case 2:
                        _a.sent();
                        this.queryRunner.enableSqlMemory();
                        return [4 /*yield*/, this.executeSchemaSyncOperationsInProperOrder()];
                    case 3:
                        _a.sent();
                        if (!this.connection.queryResultCache) return [3 /*break*/, 5];
                        return [4 /*yield*/, this.connection.queryResultCache.synchronize(this.queryRunner)];
                    case 4:
                        _a.sent();
                        _a.label = 5;
                    case 5: return [2 /*return*/, this.queryRunner.getMemorySql()];
                    case 6:
                        // its important to disable this mode despite the fact we are release query builder
                        // because there exist drivers which reuse same query runner. Also its important to disable
                        // sql memory after call of getMemorySql() method because last one flushes sql memory.
                        this.queryRunner.disableSqlMemory();
                        return [4 /*yield*/, this.queryRunner.release()];
                    case 7:
                        _a.sent();
                        return [7 /*endfinally*/];
                    case 8: return [2 /*return*/];
                }
            });
        });
    };
    Object.defineProperty(RdbmsSchemaBuilder.prototype, "entityToSyncMetadatas", {
        // -------------------------------------------------------------------------
        // Protected Methods
        // -------------------------------------------------------------------------
        /**
         * Returns only entities that should be synced in the database.
         */
        get: function () {
            return this.connection.entityMetadatas.filter(function (metadata) { return metadata.synchronize && metadata.tableType !== "entity-child"; });
        },
        enumerable: true,
        configurable: true
    });
    /**
     * Executes schema sync operations in a proper order.
     * Order of operations matter here.
     */
    RdbmsSchemaBuilder.prototype.executeSchemaSyncOperationsInProperOrder = function () {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.dropOldForeignKeys()];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, this.dropOldIndices()];
                    case 2:
                        _a.sent();
                        return [4 /*yield*/, this.dropCompositeUniqueConstraints()];
                    case 3:
                        _a.sent();
                        return [4 /*yield*/, this.renameTables()];
                    case 4:
                        _a.sent();
                        return [4 /*yield*/, this.renameColumns()];
                    case 5:
                        _a.sent();
                        return [4 /*yield*/, this.createNewTables()];
                    case 6:
                        _a.sent();
                        return [4 /*yield*/, this.dropRemovedColumns()];
                    case 7:
                        _a.sent();
                        return [4 /*yield*/, this.addNewColumns()];
                    case 8:
                        _a.sent();
                        return [4 /*yield*/, this.updateExistColumns()];
                    case 9:
                        _a.sent();
                        return [4 /*yield*/, this.createNewIndices()];
                    case 10:
                        _a.sent();
                        return [4 /*yield*/, this.createCompositeUniqueConstraints()];
                    case 11:
                        _a.sent();
                        return [4 /*yield*/, this.createForeignKeys()];
                    case 12:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Drops all (old) foreign keys that exist in the tables, but do not exist in the entity metadata.
     */
    RdbmsSchemaBuilder.prototype.dropOldForeignKeys = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
                            var table, tableForeignKeysToDrop;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                                        if (!table)
                                            return [2 /*return*/];
                                        tableForeignKeysToDrop = table.foreignKeys.filter(function (tableForeignKey) {
                                            return !metadata.foreignKeys.find(function (metadataForeignKey) { return metadataForeignKey.name === tableForeignKey.name; });
                                        });
                                        if (tableForeignKeysToDrop.length === 0)
                                            return [2 /*return*/];
                                        this.connection.logger.logSchemaBuild("dropping old foreign keys of " + table.name + ": " + tableForeignKeysToDrop.map(function (dbForeignKey) { return dbForeignKey.name; }).join(", "));
                                        // drop foreign keys from the database
                                        return [4 /*yield*/, this.queryRunner.dropForeignKeys(table, tableForeignKeysToDrop)];
                                    case 1:
                                        // drop foreign keys from the database
                                        _a.sent();
                                        return [2 /*return*/];
                                }
                            });
                        }); })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Rename tables
     */
    RdbmsSchemaBuilder.prototype.renameTables = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
                            return __generator(this, function (_a) {
                                return [2 /*return*/];
                            });
                        }); })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Renames columns.
     * Works if only one column per table was changed.
     * Changes only column name. If something besides name was changed, these changes will be ignored.
     */
    RdbmsSchemaBuilder.prototype.renameColumns = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
                            var _this = this;
                            var table, renamedMetadataColumns, renamedTableColumns, renamedColumn;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                                        if (!table)
                                            return [2 /*return*/];
                                        if (metadata.columns.length !== table.columns.length)
                                            return [2 /*return*/];
                                        renamedMetadataColumns = metadata.columns.filter(function (column) {
                                            return !table.columns.find(function (tableColumn) {
                                                return tableColumn.name === column.databaseName
                                                    && tableColumn.type === _this.connection.driver.normalizeType(column)
                                                    && tableColumn.isNullable === column.isNullable
                                                    && tableColumn.isUnique === _this.connection.driver.normalizeIsUnique(column);
                                            });
                                        });
                                        if (renamedMetadataColumns.length === 0 || renamedMetadataColumns.length > 1)
                                            return [2 /*return*/];
                                        renamedTableColumns = table.columns.filter(function (tableColumn) {
                                            return !metadata.columns.find(function (column) {
                                                return column.databaseName === tableColumn.name
                                                    && _this.connection.driver.normalizeType(column) === tableColumn.type
                                                    && column.isNullable === tableColumn.isNullable
                                                    && _this.connection.driver.normalizeIsUnique(column) === tableColumn.isUnique;
                                            });
                                        });
                                        if (renamedTableColumns.length === 0 || renamedTableColumns.length > 1)
                                            return [2 /*return*/];
                                        renamedColumn = renamedTableColumns[0].clone();
                                        renamedColumn.name = renamedMetadataColumns[0].databaseName;
                                        this.connection.logger.logSchemaBuild("renaming column \"" + renamedTableColumns[0].name + "\" in to \"" + renamedColumn.name + "\"");
                                        return [4 /*yield*/, this.queryRunner.renameColumn(table, renamedTableColumns[0], renamedColumn)];
                                    case 1:
                                        _a.sent();
                                        return [2 /*return*/];
                                }
                            });
                        }); })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    RdbmsSchemaBuilder.prototype.dropOldIndices = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
                            var _this = this;
                            var table, dropQueries;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                                        if (!table)
                                            return [2 /*return*/];
                                        dropQueries = table.indices
                                            .filter(function (tableIndex) {
                                            var indexMetadata = metadata.indices.find(function (index) { return index.name === tableIndex.name; });
                                            if (indexMetadata) {
                                                if (indexMetadata.synchronize === false)
                                                    return false;
                                                if (indexMetadata.isUnique !== tableIndex.isUnique)
                                                    return true;
                                                if (indexMetadata.columns.length !== tableIndex.columnNames.length)
                                                    return true;
                                                return !indexMetadata.columns.every(function (column) { return tableIndex.columnNames.indexOf(column.databaseName) !== -1; });
                                            }
                                            // In MySql all unique constraints stores as indices.
                                            // So if we doesn't find index constraint, we also looking for unique constraints.
                                            // this method need when we drop unique constraint.
                                            if (_this.connection.driver instanceof MysqlDriver) {
                                                // search composite uniques.
                                                var hasUniqueMetadata = metadata.uniques.some(function (unique) {
                                                    return unique.columns.every(function (column) { return tableIndex.columnNames.indexOf(column.databaseName) !== -1; });
                                                });
                                                // search unique column.
                                                var hasUniqueColumn = tableIndex.columnNames.length === 1 && metadata.columns
                                                    .some(function (column) { return column.databaseName === tableIndex.columnNames[0] && column.isUnique; });
                                                return !hasUniqueMetadata && !hasUniqueColumn;
                                            }
                                            return true;
                                        })
                                            .map(function (tableIndex) { return __awaiter(_this, void 0, void 0, function () {
                                            return __generator(this, function (_a) {
                                                switch (_a.label) {
                                                    case 0:
                                                        this.connection.logger.logSchemaBuild("dropping an index: \"" + tableIndex.name + "\" from table " + table.name);
                                                        return [4 /*yield*/, this.queryRunner.dropIndex(table, tableIndex)];
                                                    case 1:
                                                        _a.sent();
                                                        return [2 /*return*/];
                                                }
                                            });
                                        }); });
                                        return [4 /*yield*/, Promise.all(dropQueries)];
                                    case 1:
                                        _a.sent();
                                        return [2 /*return*/];
                                }
                            });
                        }); })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    RdbmsSchemaBuilder.prototype.dropCompositeUniqueConstraints = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
                            var table, compositeIndices, compositeUniques;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                                        if (!table)
                                            return [2 /*return*/];
                                        if (!(this.connection.driver instanceof MysqlDriver)) return [3 /*break*/, 2];
                                        compositeIndices = table.indices.filter(function (tableIndex) {
                                            return tableIndex.columnNames.length > 1 && tableIndex.isUnique === true && !metadata.uniques.find(function (indexMetadata) { return indexMetadata.name === tableIndex.name; });
                                        });
                                        if (compositeIndices.length === 0)
                                            return [2 /*return*/];
                                        this.connection.logger.logSchemaBuild("dropping old index: " + compositeIndices.map(function (index) { return "\"" + index.name + "\""; }).join(", ") + " from table \"" + table.name + "\"");
                                        return [4 /*yield*/, this.queryRunner.dropIndices(table, compositeIndices)];
                                    case 1:
                                        _a.sent();
                                        return [3 /*break*/, 4];
                                    case 2:
                                        compositeUniques = table.uniques.filter(function (tableUnique) {
                                            return tableUnique.columnNames.length > 1 && !metadata.uniques.find(function (uniqueMetadata) { return uniqueMetadata.name === tableUnique.name; });
                                        });
                                        if (compositeUniques.length === 0)
                                            return [2 /*return*/];
                                        this.connection.logger.logSchemaBuild("dropping old unique constraint: " + compositeUniques.map(function (unique) { return "\"" + unique.name + "\""; }).join(", ") + " from table \"" + table.name + "\"");
                                        return [4 /*yield*/, this.queryRunner.dropUniqueConstraints(table, compositeUniques)];
                                    case 3:
                                        _a.sent();
                                        _a.label = 4;
                                    case 4: return [2 /*return*/];
                                }
                            });
                        }); })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Creates tables that do not exist in the database yet.
     * New tables are created without foreign and primary keys.
     * Primary key only can be created in conclusion with auto generated column.
     */
    RdbmsSchemaBuilder.prototype.createNewTables = function () {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
                            var _this = this;
                            var existTable, table;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        existTable = this.queryRunner.loadedTables.find(function (table) {
                                            var database = metadata.database && metadata.database !== _this.connection.driver.database ? metadata.database : undefined;
                                            var schema = metadata.schema || _this.connection.driver.options.schema;
                                            var fullTableName = _this.connection.driver.buildTableName(metadata.tableName, schema, database);
                                            return table.name === fullTableName;
                                        });
                                        if (existTable)
                                            return [2 /*return*/];
                                        this.connection.logger.logSchemaBuild("creating a new table: " + metadata.tablePath);
                                        table = Table.create(metadata, this.connection.driver);
                                        return [4 /*yield*/, this.queryRunner.createTable(table, false, false)];
                                    case 1:
                                        _a.sent();
                                        this.queryRunner.loadedTables.push(table);
                                        return [2 /*return*/];
                                }
                            });
                        }); })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Drops all columns that exist in the table, but does not exist in the metadata (left old).
     * We drop their keys too, since it should be safe.
     */
    RdbmsSchemaBuilder.prototype.dropRemovedColumns = function () {
        var _this = this;
        return PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
            var table, droppedTableColumns;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        droppedTableColumns = table.columns.filter(function (tableColumn) {
                            return !metadata.columns.find(function (columnMetadata) { return columnMetadata.databaseName === tableColumn.name; });
                        });
                        if (droppedTableColumns.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("columns dropped in " + table.name + ": " + droppedTableColumns.map(function (column) { return column.name; }).join(", "));
                        // drop columns from the database
                        return [4 /*yield*/, this.queryRunner.dropColumns(table, droppedTableColumns)];
                    case 1:
                        // drop columns from the database
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
    };
    /**
     * Adds columns from metadata which does not exist in the table.
     * Columns are created without keys.
     */
    RdbmsSchemaBuilder.prototype.addNewColumns = function () {
        var _this = this;
        return PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
            var table, newColumnMetadatas, newTableColumnOptions, newTableColumns;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        newColumnMetadatas = metadata.columns.filter(function (columnMetadata) {
                            return !table.columns.find(function (tableColumn) { return tableColumn.name === columnMetadata.databaseName; });
                        });
                        if (newColumnMetadatas.length === 0)
                            return [2 /*return*/];
                        newTableColumnOptions = this.metadataColumnsToTableColumnOptions(newColumnMetadatas);
                        newTableColumns = newTableColumnOptions.map(function (option) { return new TableColumn(option); });
                        if (newTableColumns.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("new columns added: " + newColumnMetadatas.map(function (column) { return column.databaseName; }).join(", "));
                        return [4 /*yield*/, this.queryRunner.addColumns(table, newTableColumns)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
    };
    /**
     * Update all exist columns which metadata has changed.
     * Still don't create keys. Also we don't touch foreign keys of the changed columns.
     */
    RdbmsSchemaBuilder.prototype.updateExistColumns = function () {
        var _this = this;
        return PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
            var _this = this;
            var table, changedColumns, newAndOldTableColumns;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        changedColumns = this.connection.driver.findChangedColumns(table.columns, metadata.columns);
                        if (changedColumns.length === 0)
                            return [2 /*return*/];
                        // drop all foreign keys that point to this column
                        return [4 /*yield*/, PromiseUtils.runInSequence(changedColumns, function (changedColumn) { return _this.dropColumnReferencedForeignKeys(metadata.tablePath, changedColumn.databaseName); })];
                    case 1:
                        // drop all foreign keys that point to this column
                        _a.sent();
                        // drop all composite indices related to this column
                        return [4 /*yield*/, PromiseUtils.runInSequence(changedColumns, function (changedColumn) { return _this.dropColumnCompositeIndices(metadata.tablePath, changedColumn.databaseName); })];
                    case 2:
                        // drop all composite indices related to this column
                        _a.sent();
                        if (!!(this.connection.driver instanceof MysqlDriver)) return [3 /*break*/, 4];
                        return [4 /*yield*/, PromiseUtils.runInSequence(changedColumns, function (changedColumn) { return _this.dropColumnCompositeUniques(metadata.tablePath, changedColumn.databaseName); })];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4:
                        newAndOldTableColumns = changedColumns.map(function (changedColumn) {
                            var oldTableColumn = table.columns.find(function (column) { return column.name === changedColumn.databaseName; });
                            var newTableColumnOptions = TableUtils.createTableColumnOptions(changedColumn, _this.connection.driver);
                            var newTableColumn = new TableColumn(newTableColumnOptions);
                            return {
                                oldColumn: oldTableColumn,
                                newColumn: newTableColumn
                            };
                        });
                        if (newAndOldTableColumns.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("columns changed in \"" + table.name + "\". updating: " + changedColumns.map(function (column) { return column.databaseName; }).join(", "));
                        return [4 /*yield*/, this.queryRunner.changeColumns(table, newAndOldTableColumns)];
                    case 5:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
    };
    /**
     * Creates composite indices which are missing in db yet.
     */
    RdbmsSchemaBuilder.prototype.createNewIndices = function () {
        var _this = this;
        return PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
            var table, newIndices;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        newIndices = metadata.indices
                            .filter(function (indexMetadata) { return !table.indices.find(function (tableIndex) { return tableIndex.name === indexMetadata.name; }) && indexMetadata.synchronize === true; })
                            .map(function (indexMetadata) { return TableIndex.create(indexMetadata); });
                        if (newIndices.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("adding new index: " + newIndices.map(function (index) { return "\"" + index.name + "\""; }).join(", ") + " in table \"" + table.name + "\"");
                        return [4 /*yield*/, this.queryRunner.createIndices(table, newIndices)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
    };
    /**
     * Creates composite uniques which are missing in db yet.
     */
    RdbmsSchemaBuilder.prototype.createCompositeUniqueConstraints = function () {
        var _this = this;
        return PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
            var table, compositeIndices, compositeUniques;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        if (!(this.connection.driver instanceof MysqlDriver)) return [3 /*break*/, 2];
                        compositeIndices = metadata.uniques
                            .filter(function (uniqueMetadata) { return uniqueMetadata.columns.length > 1 && !table.indices.find(function (tableUnique) { return tableUnique.name === uniqueMetadata.name; }); })
                            .map(function (uniqueMetadata) {
                            return new TableIndex({
                                name: uniqueMetadata.name,
                                columnNames: uniqueMetadata.columns.map(function (column) { return column.databaseName; }),
                                isUnique: true
                            });
                        });
                        if (compositeIndices.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("adding new index: " + compositeIndices.map(function (index) { return "\"" + index.name + "\""; }).join(", ") + " in table \"" + table.name + "\"");
                        return [4 /*yield*/, this.queryRunner.createIndices(table, compositeIndices)];
                    case 1:
                        _a.sent();
                        return [3 /*break*/, 4];
                    case 2:
                        compositeUniques = metadata.uniques
                            .filter(function (uniqueMetadata) { return uniqueMetadata.columns.length > 1 && !table.uniques.find(function (tableUnique) { return tableUnique.name === uniqueMetadata.name; }); })
                            .map(function (uniqueMetadata) { return TableUnique.create(uniqueMetadata); });
                        if (compositeUniques.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("adding new unique constraint: " + compositeUniques.map(function (unique) { return "\"" + unique.name + "\""; }).join(", ") + " in table \"" + table.name + "\"");
                        return [4 /*yield*/, this.queryRunner.createUniqueConstraints(table, compositeUniques)];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4: return [2 /*return*/];
                }
            });
        }); });
    };
    /**
     * Creates foreign keys which does not exist in the table yet.
     */
    RdbmsSchemaBuilder.prototype.createForeignKeys = function () {
        var _this = this;
        return PromiseUtils.runInSequence(this.entityToSyncMetadatas, function (metadata) { return __awaiter(_this, void 0, void 0, function () {
            var table, newKeys, dbForeignKeys;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === metadata.tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        newKeys = metadata.foreignKeys.filter(function (foreignKey) {
                            return !table.foreignKeys.find(function (dbForeignKey) { return dbForeignKey.name === foreignKey.name; });
                        });
                        if (newKeys.length === 0)
                            return [2 /*return*/];
                        dbForeignKeys = newKeys.map(function (foreignKeyMetadata) { return TableForeignKey.create(foreignKeyMetadata); });
                        this.connection.logger.logSchemaBuild("creating a foreign keys: " + newKeys.map(function (key) { return key.name; }).join(", ") + " on table \"" + table.name + "\"");
                        return [4 /*yield*/, this.queryRunner.createForeignKeys(table, dbForeignKeys)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
    };
    /**
     * Drops all foreign keys where given column of the given table is being used.
     */
    RdbmsSchemaBuilder.prototype.dropColumnReferencedForeignKeys = function (tablePath, columnName) {
        return __awaiter(this, void 0, void 0, function () {
            var _this = this;
            var table, tablesWithFK, columnForeignKey, clonedTable;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        tablesWithFK = [];
                        columnForeignKey = table.foreignKeys.find(function (foreignKey) { return foreignKey.columnNames.indexOf(columnName) !== -1; });
                        if (columnForeignKey) {
                            clonedTable = table.clone();
                            clonedTable.foreignKeys = [columnForeignKey];
                            tablesWithFK.push(clonedTable);
                            table.removeForeignKey(columnForeignKey);
                        }
                        this.queryRunner.loadedTables.forEach(function (loadedTable) {
                            var dependForeignKeys = loadedTable.foreignKeys.filter(function (foreignKey) {
                                return foreignKey.referencedTableName === tablePath && foreignKey.referencedColumnNames.indexOf(columnName) !== -1;
                            });
                            if (dependForeignKeys.length > 0) {
                                var clonedTable = loadedTable.clone();
                                clonedTable.foreignKeys = dependForeignKeys;
                                tablesWithFK.push(clonedTable);
                                dependForeignKeys.forEach(function (dependForeignKey) { return loadedTable.removeForeignKey(dependForeignKey); });
                            }
                        });
                        if (!(tablesWithFK.length > 0)) return [3 /*break*/, 2];
                        return [4 /*yield*/, PromiseUtils.runInSequence(tablesWithFK, function (tableWithFK) {
                                _this.connection.logger.logSchemaBuild("dropping related foreign keys of " + tableWithFK.name + ": " + tableWithFK.foreignKeys.map(function (foreignKey) { return foreignKey.name; }).join(", "));
                                return _this.queryRunner.dropForeignKeys(tableWithFK, tableWithFK.foreignKeys);
                            })];
                    case 1:
                        _a.sent();
                        _a.label = 2;
                    case 2: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Drops all composite indices, related to given column.
     */
    RdbmsSchemaBuilder.prototype.dropColumnCompositeIndices = function (tablePath, columnName) {
        return __awaiter(this, void 0, void 0, function () {
            var table, relatedIndices;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        relatedIndices = table.indices.filter(function (index) { return index.columnNames.length > 1 && index.columnNames.indexOf(columnName) !== -1; });
                        if (relatedIndices.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("dropping related indices of \"" + tablePath + "\".\"" + columnName + "\": " + relatedIndices.map(function (index) { return index.name; }).join(", "));
                        return [4 /*yield*/, this.queryRunner.dropIndices(table, relatedIndices)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Drops all composite uniques, related to given column.
     */
    RdbmsSchemaBuilder.prototype.dropColumnCompositeUniques = function (tablePath, columnName) {
        return __awaiter(this, void 0, void 0, function () {
            var table, relatedUniques;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        table = this.queryRunner.loadedTables.find(function (table) { return table.name === tablePath; });
                        if (!table)
                            return [2 /*return*/];
                        relatedUniques = table.uniques.filter(function (unique) { return unique.columnNames.length > 1 && unique.columnNames.indexOf(columnName) !== -1; });
                        if (relatedUniques.length === 0)
                            return [2 /*return*/];
                        this.connection.logger.logSchemaBuild("dropping related unique constraints of \"" + tablePath + "\".\"" + columnName + "\": " + relatedUniques.map(function (unique) { return unique.name; }).join(", "));
                        return [4 /*yield*/, this.queryRunner.dropUniqueConstraints(table, relatedUniques)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Creates new columns from the given column metadatas.
     */
    RdbmsSchemaBuilder.prototype.metadataColumnsToTableColumnOptions = function (columns) {
        var _this = this;
        return columns.map(function (columnMetadata) { return TableUtils.createTableColumnOptions(columnMetadata, _this.connection.driver); });
    };
    return RdbmsSchemaBuilder;
}());
export { RdbmsSchemaBuilder };

//# sourceMappingURL=RdbmsSchemaBuilder.js.map
