added extra methods to query runner and implemented them in mysql driver

This commit is contained in:
Umed Khudoiberdiev 2016-12-10 16:20:42 +05:00
parent 45a4cff5c8
commit c55094810d
10 changed files with 158 additions and 48 deletions

View File

@ -14,6 +14,7 @@ import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
import {toASCII} from "punycode";
/**
* Runs queries on a single mysql database connection.
@ -59,6 +60,8 @@ export class MysqlQueryRunner implements QueryRunner {
/**
* Removes all tables from the currently connected database.
* Be careful with using this method and avoid using it in production or migrations
* (because it can clear all your database).
*/
async clearDatabase(): Promise<void> {
if (this.isReleased)
@ -234,10 +237,18 @@ export class MysqlQueryRunner implements QueryRunner {
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
}
/**
* Loads given table's data from the database.
*/
async loadTableSchema(tableName: string, namingStrategy: NamingStrategyInterface): Promise<TableSchema|undefined> {
const tableSchemas = await this.loadTableSchemas([tableName], namingStrategy);
return tableSchemas.length > 0 ? tableSchemas[0] : undefined;
}
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
async loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
@ -323,7 +334,16 @@ export class MysqlQueryRunner implements QueryRunner {
}
/**
* Creates a new table from the given table metadata and column metadatas.
* Checks if table with the given name exist in the database.
*/
async hasTable(table: TableSchema): Promise<boolean> {
const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME = '${table.name}'`;
const result = await this.query(sql);
return result.length ? true : false;
}
/**
* Creates a new table from the given table schema and column schemas inside it.
*/
async createTable(table: TableSchema): Promise<void> {
if (this.isReleased)
@ -340,19 +360,49 @@ export class MysqlQueryRunner implements QueryRunner {
}
/**
* Creates a new column from the column metadata in the table.
* Checks if column with the given name exist in the given table.
*/
async createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
async hasColumn(table: TableSchema, columnName: string): Promise<boolean> {
const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME = '${table.name}' AND COLUMN_NAME = '${columnName}'`;
const result = await this.query(sql);
return result.length ? true : false;
}
/**
* Creates a new column from the column schema in the table.
*/
async addColumn(tableSchema: TableSchema, column: ColumnSchema): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
const queries = columns.map(column => {
const sql = `ALTER TABLE \`${tableSchema.name}\` ADD ${this.buildCreateColumnSql(column, false)}`;
return this.query(sql);
});
const sql = `ALTER TABLE \`${tableSchema.name}\` ADD ${this.buildCreateColumnSql(column, false)}`;
return this.query(sql);
}
/**
* Creates a new columns from the column schema in the table.
*/
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
const queries = columns.map(column => this.addColumn(tableSchema, column));
await Promise.all(queries);
}
/**
* Changes a column in the table.
*/
async changeColumn(tableSchema: TableSchema, newColumn: ColumnSchema, oldColumn: ColumnSchema): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
if (newColumn.isUnique === false && oldColumn.isUnique === true)
await this.query(`ALTER TABLE \`${tableSchema.name}\` DROP INDEX \`${oldColumn.name}\``);
return this.query(`ALTER TABLE \`${tableSchema.name}\` CHANGE \`${oldColumn.name}\` ${this.buildCreateColumnSql(newColumn, oldColumn.isPrimary)}`);
}
/**
* Changes a column in the table.
*/
@ -361,17 +411,19 @@ export class MysqlQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const updatePromises = changedColumns.map(async changedColumn => {
const sql = `ALTER TABLE \`${tableSchema.name}\` CHANGE \`${changedColumn.oldColumn.name}\` ${this.buildCreateColumnSql(changedColumn.newColumn, changedColumn.oldColumn.isPrimary)}`; // todo: CHANGE OR MODIFY COLUMN ????
if (changedColumn.newColumn.isUnique === false && changedColumn.oldColumn.isUnique === true)
await this.query(`ALTER TABLE \`${tableSchema.name}\` DROP INDEX \`${changedColumn.oldColumn.name}\``);
return this.query(sql);
return this.changeColumn(tableSchema, changedColumn.newColumn, changedColumn.oldColumn);
});
await Promise.all(updatePromises);
}
/**
* Drops column in the table.
*/
async dropColumn(dbTable: TableSchema, column: ColumnSchema): Promise<void> {
return this.query(`ALTER TABLE \`${dbTable.name}\` DROP \`${column.name}\``);
}
/**
* Drops the columns in the table.
*/
@ -379,10 +431,7 @@ export class MysqlQueryRunner implements QueryRunner {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
const dropPromises = columns.map(column => {
return this.query(`ALTER TABLE \`${dbTable.name}\` DROP \`${column.name}\``);
});
const dropPromises = columns.map(column => this.dropColumn(dbTable, column));
await Promise.all(dropPromises);
}
@ -401,6 +450,22 @@ export class MysqlQueryRunner implements QueryRunner {
await this.query(`ALTER TABLE ${tableSchema.name} ADD PRIMARY KEY (${primaryColumnNames.join(", ")})`);
}
/**
* Creates a new foreign key.
*/
async createForeignKey(dbTable: TableSchema, foreignKey: ForeignKeySchema): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
const columnNames = foreignKey.columnNames.map(column => "`" + column + "`").join(", ");
const referencedColumnNames = foreignKey.referencedColumnNames.map(column => "`" + column + "`").join(",");
let sql = `ALTER TABLE ${dbTable.name} ADD CONSTRAINT \`${foreignKey.name}\` ` +
`FOREIGN KEY (${columnNames}) ` +
`REFERENCES \`${foreignKey.referencedTableName}\`(${referencedColumnNames})`;
if (foreignKey.onDelete) sql += " ON DELETE " + foreignKey.onDelete;
return this.query(sql);
}
/**
* Creates a new foreign keys.
*/
@ -408,19 +473,20 @@ export class MysqlQueryRunner implements QueryRunner {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
const promises = foreignKeys.map(foreignKey => {
const columnNames = foreignKey.columnNames.map(column => "`" + column + "`").join(", ");
const referencedColumnNames = foreignKey.referencedColumnNames.map(column => "`" + column + "`").join(",");
let sql = `ALTER TABLE ${dbTable.name} ADD CONSTRAINT \`${foreignKey.name}\` ` +
`FOREIGN KEY (${columnNames}) ` +
`REFERENCES \`${foreignKey.referencedTableName}\`(${referencedColumnNames})`;
if (foreignKey.onDelete) sql += " ON DELETE " + foreignKey.onDelete;
return this.query(sql);
});
const promises = foreignKeys.map(foreignKey => this.createForeignKey(dbTable, foreignKey));
await Promise.all(promises);
}
/**
* Drops a foreign key from the table.
*/
async dropForeignKey(tableSchema: TableSchema, foreignKey: ForeignKeySchema): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
return this.query(`ALTER TABLE \`${tableSchema.name}\` DROP FOREIGN KEY \`${foreignKey.name}\``);
}
/**
* Drops a foreign keys from the table.
*/
@ -428,11 +494,7 @@ export class MysqlQueryRunner implements QueryRunner {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
const promises = foreignKeys.map(foreignKey => {
const sql = `ALTER TABLE \`${tableSchema.name}\` DROP FOREIGN KEY \`${foreignKey.name}\``;
return this.query(sql);
});
const promises = foreignKeys.map(foreignKey => this.dropForeignKey(tableSchema, foreignKey));
await Promise.all(promises);
}

View File

@ -16,6 +16,8 @@ import {PlatformTools} from "../../platform/PlatformTools";
/**
* Organizes communication with Oracle DBMS.
*
* todo: this driver is not 100% finished yet, need to fix all issues that are left
*/
export class OracleDriver implements Driver {

View File

@ -17,6 +17,8 @@ import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInter
/**
* Runs queries on a single mysql database connection.
*
* todo: this driver is not 100% finished yet, need to fix all issues that are left
*/
export class OracleQueryRunner implements QueryRunner {
@ -253,7 +255,7 @@ export class OracleQueryRunner implements QueryRunner {
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
async loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
@ -373,7 +375,7 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
/**
* Creates a new column from the column metadata in the table.
*/
async createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();

View File

@ -235,7 +235,7 @@ export class PostgresQueryRunner implements QueryRunner {
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
async loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
@ -354,7 +354,7 @@ where constraint_type = 'PRIMARY KEY' and tc.table_catalog = '${this.dbName}'`;
/**
* Creates a new column from the column metadata in the table.
*/
async createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();

View File

@ -247,7 +247,7 @@ export class SqliteQueryRunner implements QueryRunner {
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
async loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
@ -383,7 +383,7 @@ export class SqliteQueryRunner implements QueryRunner {
/**
* Creates a new column from the column metadata in the table.
*/
async createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> { // todo: remove column metadata returning
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> { // todo: remove column metadata returning
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();

View File

@ -300,7 +300,7 @@ export class SqlServerQueryRunner implements QueryRunner {
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
async loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
@ -429,7 +429,7 @@ export class SqlServerQueryRunner implements QueryRunner {
/**
* Creates a new column from the column metadata in the table.
*/
async createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();

View File

@ -258,7 +258,7 @@ export class WebsqlQueryRunner implements QueryRunner {
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
async loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]> {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
@ -394,7 +394,7 @@ export class WebsqlQueryRunner implements QueryRunner {
/**
* Creates a new column from the column metadata in the table.
*/
async createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> { // todo: remove column metadata returning
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void> { // todo: remove column metadata returning
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();

View File

@ -19,6 +19,8 @@ export interface QueryRunner {
/**
* Removes all tables from the currently connected database.
* Be careful with using this method and avoid using it in production or migrations
* (because it can clear all your database).
*/
clearDatabase(): Promise<void>;
@ -80,23 +82,55 @@ export interface QueryRunner {
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
loadSchemaTables(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]>;
loadTableSchema(tableName: string, namingStrategy: NamingStrategyInterface): Promise<TableSchema|undefined>;
/**
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
loadTableSchemas(tableNames: string[], namingStrategy: NamingStrategyInterface): Promise<TableSchema[]>;
/**
* Checks if table with the given name exist in the database.
*/
hasTable(table: TableSchema): Promise<boolean>;
/**
* Creates a new table from the given table metadata and column metadatas.
*/
createTable(table: TableSchema): Promise<void>;
/**
* Checks if column with the given name exist in the given table.
*/
hasColumn(table: TableSchema, columnName: string): Promise<boolean>;
/**
* Creates a new column in the table.
*/
addColumn(tableSchema: TableSchema, column: ColumnSchema): Promise<void>;
/**
* Creates new columns in the table.
*/
createColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void>;
addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void>;
// todo: renameColumn ?
/**
* Changes a column in the table.
*/
changeColumn(tableSchema: TableSchema, newColumn: ColumnSchema, oldColumn: ColumnSchema): Promise<void>;
/**
* Changes a columns in the table.
*/
changeColumns(tableSchema: TableSchema, changedColumns: { newColumn: ColumnSchema, oldColumn: ColumnSchema }[]): Promise<void>;
/**
* Drops the column in the table.
*/
dropColumn(dbTable: TableSchema, column: ColumnSchema): Promise<void>;
/**
* Drops the columns in the table.
*/
@ -107,11 +141,21 @@ export interface QueryRunner {
*/
updatePrimaryKeys(dbTable: TableSchema): Promise<void>;
/**
* Creates a new foreign key.
*/
createForeignKey(dbTable: TableSchema, foreignKey: ForeignKeySchema): Promise<void>;
/**
* Creates a new foreign keys.
*/
createForeignKeys(dbTable: TableSchema, foreignKeys: ForeignKeySchema[]): Promise<void>;
/**
* Drops a foreign keys from the table.
*/
dropForeignKey(tableSchema: TableSchema, foreignKey: ForeignKeySchema): Promise<void>;
/**
* Drops a foreign keys from the table.
*/

View File

@ -104,7 +104,7 @@ export class SchemaBuilder {
*/
protected loadTableSchemas(): Promise<TableSchema[]> {
const tableNames = this.entityToSyncMetadatas.map(metadata => metadata.table.name);
return this.queryRunner.loadSchemaTables(tableNames, this.namingStrategy);
return this.queryRunner.loadTableSchemas(tableNames, this.namingStrategy);
}
/**
@ -214,7 +214,7 @@ export class SchemaBuilder {
// create columns in the database
const newColumnSchemas = this.metadataColumnsToColumnSchemas(newColumnMetadatas);
tableSchema.addColumns(newColumnSchemas);
await this.queryRunner.createColumns(tableSchema, newColumnSchemas);
await this.queryRunner.addColumns(tableSchema, newColumnSchemas);
}));
}

View File

@ -354,7 +354,7 @@ describe("Connection", () => {
it("database should be empty after schema sync", () => Promise.all(connections.map(async connection => {
await connection.syncSchema(true);
const queryRunner = await connection.driver.createQueryRunner();
let schema = await queryRunner.loadSchemaTables(["view"], new DefaultNamingStrategy());
let schema = await queryRunner.loadTableSchemas(["view"], new DefaultNamingStrategy());
expect(schema.some(table => table.name === "view")).to.be.false;
})));