version bump

This commit is contained in:
Umed Khudoiberdiev 2017-06-20 12:34:50 +05:00
parent e7a18d050b
commit 9a1cc0d842
16 changed files with 144 additions and 71 deletions

View File

@ -1,7 +1,7 @@
{
"name": "typeorm",
"private": true,
"version": "0.1.0-alpha.17",
"version": "0.1.0-alpha.18",
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL, MongoDB databases.",
"license": "MIT",
"readmeFilename": "README.md",

View File

@ -52,32 +52,47 @@ export class MigrationGenerateCommand {
let connection: Connection|undefined = undefined;
try {
process.env.LOGGER_CLI_SCHEMA_SYNC = false;
process.env.SKIP_SCHEMA_CREATION = true;
const connectionOptionsReader = new ConnectionOptionsReader({ root: process.cwd(), configName: argv.config });
const connectionOptions = await connectionOptionsReader.get(argv.connection);
Object.assign(connectionOptions, {
dropSchemaOnConnection: false,
autoSchemaSync: false,
autoMigrationsRun: false,
logging: { logQueries: false, logFailedQueryError: false, logSchemaCreation: false }
});
connection = await createConnection(connectionOptions);
const sqlQueries = await connection.logSyncSchema();
let contentSqls: string[] = [];
const upSqls: string[] = [], downSqls: string[] = [];
// mysql is exceptional here because it uses ` character in to escape names in queries, thats why for mysql
// we are using simple quoted string instead of template string sytax
if (connection.driver instanceof MysqlDriver) {
contentSqls = sqlQueries.map(query => " await queryRunner.query(\"" + query.replace(new RegExp(`"`, "g"), `\\"`) + "\");");
sqlQueries.forEach(query => {
const queryString = typeof query === "string" ? query : query.up;
upSqls.push(" await queryRunner.query(\"" + queryString.replace(new RegExp(`"`, "g"), `\\"`) + "\");");
if (typeof query !== "string" && query.down)
downSqls.push(" await queryRunner.query(\"" + query.down.replace(new RegExp(`"`, "g"), `\\"`) + "\");");
});
} else {
contentSqls = sqlQueries.map(query => " await queryRunner.query(`" + query.replace(new RegExp("`", "g"), "\\`") + "`);");
sqlQueries.forEach(query => {
const queryString = typeof query === "string" ? query : query.up;
upSqls.push(" await queryRunner.query(`" + queryString.replace(new RegExp("`", "g"), "\\`") + "`);");
if (typeof query !== "string" && query.down)
downSqls.push(" await queryRunner.query(`" + query.down.replace(new RegExp("`", "g"), "\\`") + "`);");
});
}
const fileContent = MigrationGenerateCommand.getTemplate(argv.name, timestamp, contentSqls);
const fileContent = MigrationGenerateCommand.getTemplate(argv.name, timestamp, upSqls, downSqls);
const path = process.cwd() + "/" + (directory ? (directory + "/") : "") + filename;
await CommandUtils.createFile(path, fileContent);
console.log(`Migration ${path} has been generated successfully.`);
if (!upSqls.length) {
console.log(`Migration ${path} has been generated successfully.`);
} else {
console.error(`No changes in database schema were found - cannot generate a migration. To create a new empty migration use "typeorm migrations:create" command`);
}
} catch (err) {
if (connection)
(connection as Connection).logger.log("error", err);
throw err;
console.error(err);
} finally {
if (connection)
@ -92,17 +107,19 @@ export class MigrationGenerateCommand {
/**
* Gets contents of the migration file.
*/
protected static getTemplate(name: string, timestamp: number, sqls: string[]): string {
protected static getTemplate(name: string, timestamp: number, upSqls: string[], downSqls: string[]): string {
return `import {Connection, EntityManager, MigrationInterface, QueryRunner} from "typeorm";
export class ${name}${timestamp} implements MigrationInterface {
public async up(queryRunner: QueryRunner, connection: Connection, entityManager?: EntityManager): Promise<any> {
${sqls.join(`
${upSqls.join(`
`)}
}
public async down(queryRunner: QueryRunner, connection: Connection, entityManager?: EntityManager): Promise<any> {
${downSqls.join(`
`)}
}
}

View File

@ -35,7 +35,13 @@ export class SchemaSyncLogCommand {
const connectionOptions = await connectionOptionsReader.get(argv.connection);
connection = await createConnection(connectionOptions);
const sqls = await connection.logSyncSchema();
sqls.forEach(sql => console.log(sql));
sqls.forEach(sql => {
if (typeof sql === "string") {
console.log(sql);
} else {
console.log(sql.up);
}
});
} catch (err) {
if (connection)

View File

@ -11,8 +11,10 @@ export interface BaseConnectionOptions {
/**
* Database type. This value is required.
*
* "?" is temporary.
*/
readonly type: DatabaseType;
readonly type?: DatabaseType;
/**
* Connection name. If connection name is not given then it will be called "default".

View File

@ -203,7 +203,7 @@ export class Connection {
/**
* Returns sql queries generated by schema builder.
*/
async logSyncSchema(): Promise<string[]> {
async logSyncSchema(): Promise<(string|{ up: string, down: string })[]> {
if (!this.isConnected)
throw new CannotExecuteNotConnectedError(this.name);

View File

@ -582,7 +582,7 @@ export class MongoQueryRunner implements QueryRunner {
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
throw new Error(`This operation is not supported by MongoDB driver.`);
}

View File

@ -53,7 +53,7 @@ export class MysqlQueryRunner implements QueryRunner {
/**
* Sql-s stored if "sql in memory" mode is enabled.
*/
protected sqlsInMemory: string[] = [];
protected sqlsInMemory: (string|{ up: string, down: string })[] = [];
// -------------------------------------------------------------------------
// Constructor
@ -140,12 +140,6 @@ export class MysqlQueryRunner implements QueryRunner {
if (this.isReleased)
throw new QueryRunnerAlreadyReleasedError();
// if sql-in-memory mode is enabled then simply store sql in memory and return
if (this.sqlMemoryMode === true) {
this.sqlsInMemory.push(query);
return Promise.resolve() as Promise<any>;
}
return new Promise(async (ok, fail) => {
this.driver.connection.logger.logQuery(query, parameters);
const databaseConnection = await this.connect();
@ -233,6 +227,9 @@ export class MysqlQueryRunner implements QueryRunner {
* Loads all tables (with given names) from the database and creates a TableSchema from them.
*/
async loadTableSchemas(tableNames: string[]): Promise<TableSchema[]> {
if (this.sqlMemoryMode)
throw new Error(`Loading table schema is not supported in sql memory mode`);
// if no tables given then no need to proceed
if (!tableNames || !tableNames.length)
return [];
@ -318,12 +315,24 @@ export class MysqlQueryRunner implements QueryRunner {
/**
* Checks if table with the given name exist in the database.
*/
async hasTable(tableName: string): Promise<boolean> {
async hasTable(table: TableSchema|string): Promise<boolean> {
const tableName = table instanceof TableSchema ? table.name : table;
const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME = '${tableName}'`;
const result = await this.query(sql);
return result.length ? true : false;
}
/**
* Checks if column with the given name exist in the given table.
*/
async hasColumn(table: TableSchema|string, column: ColumnSchema|string): Promise<boolean> {
const tableName = table instanceof TableSchema ? table.name : table;
const columnName = column instanceof ColumnSchema ? column.name : column;
const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME = '${tableName}' AND COLUMN_NAME = '${columnName}'`;
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.
*/
@ -333,26 +342,19 @@ export class MysqlQueryRunner implements QueryRunner {
const primaryKeyColumns = table.columns.filter(column => column.isPrimary && !column.isGenerated);
if (primaryKeyColumns.length > 0)
sql += `, PRIMARY KEY(${primaryKeyColumns.map(column => `\`${column.name}\``).join(", ")})`;
sql += `) ENGINE=${table.engine || "InnoDB"};`;
sql += `) ENGINE=${table.engine || "InnoDB"}`;
await this.query(sql);
const revertSql = `DROP TABLE \`${table.name}\``;
return this.schemaQuery(sql, revertSql);
}
/**
* Drop the table.
*/
async dropTable(tableName: String): Promise<void> {
let sql = `DROP TABLE \`${tableName}\``;
await this.query(sql);
}
/**
* Checks if column with the given name exist in the given table.
*/
async hasColumn(tableName: string, columnName: string): Promise<boolean> {
const sql = `SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${this.dbName}' AND TABLE_NAME = '${tableName}' AND COLUMN_NAME = '${columnName}'`;
const result = await this.query(sql);
return result.length ? true : false;
async dropTable(table: TableSchema|string): Promise<void> {
const tableName = table instanceof TableSchema ? table.name : table;
const sql = `DROP TABLE \`${tableName}\``;
return this.query(sql);
}
/**
@ -361,7 +363,8 @@ export class MysqlQueryRunner implements QueryRunner {
async addColumn(tableSchemaOrName: TableSchema|string, column: ColumnSchema): Promise<void> {
const tableName = tableSchemaOrName instanceof TableSchema ? tableSchemaOrName.name : tableSchemaOrName;
const sql = `ALTER TABLE \`${tableName}\` ADD ${this.buildCreateColumnSql(column, false)}`;
return this.query(sql);
const revertSql = `ALTER TABLE \`${tableName}\` DROP \`${column.name}\``;
return this.schemaQuery(sql, revertSql);
}
/**
@ -381,12 +384,11 @@ export class MysqlQueryRunner implements QueryRunner {
if (tableSchemaOrName instanceof TableSchema) {
tableSchema = tableSchemaOrName;
} else {
tableSchema = await this.loadTableSchema(tableSchemaOrName);
tableSchema = await this.loadTableSchema(tableSchemaOrName); // todo: throw exception, this wont work because of sql memory enabled. remove support by table name
if (!tableSchema)
throw new Error(`Table ${tableSchemaOrName} was not found.`);
}
if (!tableSchema)
throw new Error(`Table ${tableSchemaOrName} was not found.`);
let oldColumn: ColumnSchema|undefined = undefined;
if (oldColumnSchemaOrName instanceof ColumnSchema) {
oldColumn = oldColumnSchemaOrName;
@ -433,17 +435,19 @@ export class MysqlQueryRunner implements QueryRunner {
throw new Error(`Column "${oldColumnSchemaOrName}" was not found in the "${tableSchemaOrName}" table.`);
if (newColumn.isUnique === false && oldColumn.isUnique === true)
await this.query(`ALTER TABLE \`${tableSchema.name}\` DROP INDEX \`${oldColumn.name}\``);
await this.query(`ALTER TABLE \`${tableSchema.name}\` DROP INDEX \`${oldColumn.name}\``); // todo: add revert code
return this.query(`ALTER TABLE \`${tableSchema.name}\` CHANGE \`${oldColumn.name}\` ${this.buildCreateColumnSql(newColumn, oldColumn.isPrimary)}`);
const sql = `ALTER TABLE \`${tableSchema.name}\` CHANGE \`${oldColumn.name}\` ${this.buildCreateColumnSql(newColumn, oldColumn.isPrimary)}`;
const revertSql = `ALTER TABLE \`${tableSchema.name}\` CHANGE \`${oldColumn.name}\` ${this.buildCreateColumnSql(oldColumn, oldColumn.isPrimary)}`;
return this.schemaQuery(sql, revertSql);
}
/**
* Changes a column in the table.
*/
async changeColumns(tableSchema: TableSchema, changedColumns: { newColumn: ColumnSchema, oldColumn: ColumnSchema }[]): Promise<void> {
async changeColumns(table: TableSchema, changedColumns: { newColumn: ColumnSchema, oldColumn: ColumnSchema }[]): Promise<void> {
const updatePromises = changedColumns.map(async changedColumn => {
return this.changeColumn(tableSchema, changedColumn.oldColumn, changedColumn.newColumn);
return this.changeColumn(table, changedColumn.oldColumn, changedColumn.newColumn);
});
await Promise.all(updatePromises);
@ -453,7 +457,9 @@ export class MysqlQueryRunner implements QueryRunner {
* Drops column in the table.
*/
async dropColumn(table: TableSchema, column: ColumnSchema): Promise<void> {
return this.query(`ALTER TABLE \`${table.name}\` DROP \`${column.name}\``);
const sql = `ALTER TABLE \`${table.name}\` DROP \`${column.name}\``;
const revertSql = `ALTER TABLE \`${table.name}\` ADD ${this.buildCreateColumnSql(column, false)}`;
return this.schemaQuery(sql, revertSql);
}
/**
@ -471,9 +477,14 @@ export class MysqlQueryRunner implements QueryRunner {
if (!tableSchema.hasGeneratedColumn)
await this.query(`ALTER TABLE \`${tableSchema.name}\` DROP PRIMARY KEY`);
const primaryColumnNames = tableSchema.columns.filter(column => column.isPrimary && !column.isGenerated).map(column => "`" + column.name + "`");
if (primaryColumnNames.length > 0)
await this.query(`ALTER TABLE \`${tableSchema.name}\` ADD PRIMARY KEY (${primaryColumnNames.join(", ")})`);
const primaryColumnNames = tableSchema.columns
.filter(column => column.isPrimary && !column.isGenerated)
.map(column => "`" + column.name + "`");
if (primaryColumnNames.length > 0) {
const sql = `ALTER TABLE \`${tableSchema.name}\` ADD PRIMARY KEY (${primaryColumnNames.join(", ")})`;
const revertSql = `ALTER TABLE \`${tableSchema.name}\` DROP PRIMARY KEY`;
return this.schemaQuery(sql, revertSql);
}
}
/**
@ -487,7 +498,8 @@ export class MysqlQueryRunner implements QueryRunner {
`FOREIGN KEY (${columnNames}) ` +
`REFERENCES \`${foreignKey.referencedTableName}\`(${referencedColumnNames})`;
if (foreignKey.onDelete) sql += " ON DELETE " + foreignKey.onDelete;
return this.query(sql);
const revertSql = `ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${foreignKey.name}\``;
return this.schemaQuery(sql, revertSql);
}
/**
@ -503,7 +515,16 @@ export class MysqlQueryRunner implements QueryRunner {
*/
async dropForeignKey(tableSchemaOrName: TableSchema|string, foreignKey: ForeignKeySchema): Promise<void> {
const tableName = tableSchemaOrName instanceof TableSchema ? tableSchemaOrName.name : tableSchemaOrName;
return this.query(`ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${foreignKey.name}\``);
const sql = `ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${foreignKey.name}\``;
const columnNames = foreignKey.columnNames.map(column => "`" + column + "`").join(", ");
const referencedColumnNames = foreignKey.referencedColumnNames.map(column => "`" + column + "`").join(",");
let revertSql = `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${foreignKey.name}\` ` +
`FOREIGN KEY (${columnNames}) ` +
`REFERENCES \`${foreignKey.referencedTableName}\`(${referencedColumnNames})`;
if (foreignKey.onDelete) revertSql += " ON DELETE " + foreignKey.onDelete;
return this.schemaQuery(sql, revertSql);
}
/**
@ -517,24 +538,37 @@ export class MysqlQueryRunner implements QueryRunner {
/**
* Creates a new index.
*/
async createIndex(tableName: string, index: IndexSchema): Promise<void> {
async createIndex(table: TableSchema|string, index: IndexSchema): Promise<void> {
const tableName = table instanceof TableSchema ? table.name : table;
const columns = index.columnNames.map(columnName => "`" + columnName + "`").join(", ");
const sql = `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX \`${index.name}\` ON \`${tableName}\`(${columns})`;
await this.query(sql);
const revertSql = `ALTER TABLE \`${tableName}\` DROP INDEX \`${index.name}\``;
await this.schemaQuery(sql, revertSql);
}
/**
* Drops an index from the table.
*/
async dropIndex(tableName: string, indexName: string): Promise<void> {
async dropIndex(table: TableSchema|string, index: IndexSchema|string): Promise<void> {
const tableName = table instanceof TableSchema ? table.name : table;
const indexName = index instanceof IndexSchema ? index.name : index;
const sql = `ALTER TABLE \`${tableName}\` DROP INDEX \`${indexName}\``;
await this.query(sql);
if (index instanceof IndexSchema) {
const columns = index.columnNames.map(columnName => "`" + columnName + "`").join(", ");
const revertSql = `CREATE ${index.isUnique ? "UNIQUE " : ""}INDEX \`${index.name}\` ON \`${tableName}\`(${columns})`;
await this.schemaQuery(sql, revertSql);
} else {
await this.query(sql);
}
}
/**
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
async truncate(table: TableSchema|string): Promise<void> {
const tableName = table instanceof TableSchema ? table.name : table;
await this.query(`TRUNCATE TABLE \`${tableName}\``);
}
@ -588,7 +622,7 @@ export class MysqlQueryRunner implements QueryRunner {
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
return this.sqlsInMemory;
}
@ -596,6 +630,20 @@ export class MysqlQueryRunner implements QueryRunner {
// Protected Methods
// -------------------------------------------------------------------------
/**
* Executes sql used special for schema build.
*/
protected async schemaQuery(upQuery: string, downQuery: string): Promise<void> {
// if sql-in-memory mode is enabled then simply store sql in memory and return
if (this.sqlMemoryMode === true) {
this.sqlsInMemory.push({ up: upQuery, down: downQuery });
return Promise.resolve() as Promise<any>;
}
await this.query(upQuery);
}
/**
* Database name shortcut.
*/

View File

@ -648,7 +648,7 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
return this.sqlsInMemory;
}

View File

@ -674,7 +674,7 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}'
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
return this.sqlsInMemory;
}

View File

@ -594,7 +594,7 @@ export class SqliteQueryRunner implements QueryRunner {
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
return this.sqlsInMemory;
}

View File

@ -699,7 +699,7 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
return this.sqlsInMemory;
}

View File

@ -641,7 +641,7 @@ export class WebsqlQueryRunner implements QueryRunner {
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[] {
getMemorySql(): (string|{ up: string, down: string })[] {
return this.sqlsInMemory;
}

View File

@ -222,7 +222,7 @@ export interface QueryRunner {
/**
* Drops an index from the table.
*/
dropIndex(tableName: string, indexName: string): Promise<void>;
dropIndex(table: TableSchema|string, index: IndexSchema|string): Promise<void>;
/**
* Truncates table.
@ -249,6 +249,6 @@ export interface QueryRunner {
/**
* Gets sql stored in the memory. Parameters in the sql are already replaced.
*/
getMemorySql(): string[];
getMemorySql(): (string|{ up: string, down: string })[];
}

View File

@ -48,7 +48,7 @@ export class MongoSchemaBuilder implements SchemaBuilder {
/**
* Returns query to be executed by schema builder.
*/
log(): Promise<string[]> {
log(): Promise<(string|{ up: string, down: string })[]> {
return Promise.resolve([]);
}

View File

@ -80,7 +80,7 @@ export class RdbmsSchemaBuilder implements SchemaBuilder {
/**
* Returns sql queries to be executed by schema builder.
*/
async log(): Promise<string[]> {
async log(): Promise<(string|{ up: string, down: string })[]> {
this.queryRunner = await this.connection.createQueryRunner();
try {
this.tableSchemas = await this.loadTableSchemas();

View File

@ -11,6 +11,6 @@ export interface SchemaBuilder {
/**
* Returns queries to be executed by schema builder.
*/
log(): Promise<string[]>;
log(): Promise<(string|{ up: string, down: string })[]>;
}