refactoring drivers; fixed some tests

This commit is contained in:
Umed Khudoiberdiev 2017-06-15 15:52:06 +05:00
parent 18d092cdb2
commit d59458b513
23 changed files with 135 additions and 124 deletions

View File

@ -46,6 +46,8 @@ More env variable names you can find in `ConnectionOptionsEnvReader` class.
* `localTimezone` has been removed from the column options
* `skipSchemaSync` in entity options has been renamed to `skipSync`
* `setLimit` and `setOffset` in `QueryBuilder` were renamed into `limit` and `offset`
* `nativeInterface` has been removed from a driver interface and implementations.
Now
### DEPRECATIONS

View File

@ -51,17 +51,17 @@ export interface Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string;
escapeColumn(columnName: string): string;
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string;
escapeAlias(aliasName: string): string;
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string;
escapeTable(tableName: string): string;
/**
* Prepares given value to a value to be persisted, based on its column type and metadata.
@ -74,7 +74,7 @@ export interface Driver {
prepareHydratedValue(value: any, column: ColumnMetadata): any;
/**
* Converts a column type of the metadata to the database column's type.
* Transforms type of the given column to a database column type.
*/
normalizeType(column: ColumnMetadata): string;

View File

@ -134,21 +134,21 @@ export class MongoDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return columnName;
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return aliasName;
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return tableName;
}

View File

@ -81,7 +81,7 @@ export class MysqlDriver implements Driver {
];
/**
* Orm has special columns and we need to know what database column types should be for those types.
* ORM has special columns and we need to know what database column types should be for those columns.
* Column types are driver dependant.
*/
mappedDataTypes: MappedColumnTypes = {
@ -184,21 +184,21 @@ export class MysqlDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return "`" + columnName + "`";
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return "`" + aliasName + "`";
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return "`" + tableName + "`";
}

View File

@ -194,11 +194,10 @@ export class MysqlQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const keys = Object.keys(keyValues);
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
const columns = keys.map(key => `\`${key}\``).join(", ");
const values = keys.map(key => "?").join(",");
const parameters = keys.map(key => keyValues[key]);
const escapedTableName = this.driver.escapeTableName(tableName);
const sql = `INSERT INTO ${escapedTableName}(${columns}) VALUES (${values})`;
const sql = `INSERT INTO \`${tableName}\`(${columns}) VALUES (${values})`;
const result = await this.query(sql, parameters);
return generatedColumn ? result.insertId : undefined;
}
@ -212,8 +211,7 @@ export class MysqlQueryRunner implements QueryRunner {
const updateValues = this.parametrize(valuesMap).join(", ");
const conditionString = this.parametrize(conditions).join(" AND ");
const escapedTableName = this.driver.escapeTableName(tableName);
const sql = `UPDATE ${escapedTableName} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const sql = `UPDATE \`${tableName}\` SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
const allParameters = updateParams.concat(conditionParams);
@ -230,7 +228,7 @@ export class MysqlQueryRunner implements QueryRunner {
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
const sql = `DELETE FROM \`${tableName}\` WHERE ${conditionString}`;
await this.query(sql, parameters);
}
@ -244,18 +242,18 @@ export class MysqlQueryRunner implements QueryRunner {
// todo: escape column names as well
if (hasLevel) {
await this.query(
`INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
`INSERT INTO \`${tableName}\`(\`ancestor\`, \`descendant\`, \`level\`) ` +
`SELECT \`ancestor\`, ${newEntityId}, \`level\` + 1 FROM \`${tableName}\` WHERE \`descendant\` = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`
);
} else {
await this.query(
`INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
`INSERT INTO \`${tableName}\`(\`ancestor\`, \`descendant\`) ` +
`SELECT \`ancestor\`, ${newEntityId} FROM \`${tableName}\` WHERE \`descendant\` = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`
);
}
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
const results: ObjectLiteral[] = await this.query(`SELECT MAX(\`level\`) as \`level\` FROM \`${tableName}\` WHERE \`descendant\` = ${parentId}`);
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
}
@ -616,7 +614,7 @@ export class MysqlQueryRunner implements QueryRunner {
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
await this.query(`TRUNCATE TABLE \`${tableName}\``);
}
// -------------------------------------------------------------------------
@ -634,7 +632,7 @@ export class MysqlQueryRunner implements QueryRunner {
* Parametrizes given object of values. Used to create column=value queries.
*/
protected parametrize(objectLiteral: ObjectLiteral): string[] {
return Object.keys(objectLiteral).map(key => this.driver.escapeColumnName(key) + "=?");
return Object.keys(objectLiteral).map(key => `\`${key}\`=?`);
}
/**

View File

@ -204,21 +204,21 @@ export class OracleDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return `"${columnName}"`;
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return `"${aliasName}"`;
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return `"${tableName}"`;
}

View File

@ -208,13 +208,13 @@ export class OracleQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const keys = Object.keys(keyValues);
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
const columns = keys.map(key => `"${key}"`).join(", ");
const values = keys.map(key => ":" + key).join(", ");
const parameters = keys.map(key => keyValues[key]);
const insertSql = columns.length > 0
? `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`
: `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES`;
? `INSERT INTO "${tableName}" (${columns}) VALUES (${values})`
: `INSERT INTO "${tableName}" DEFAULT VALUES`;
if (generatedColumn) {
const sql2 = `declare lastId number; begin ${insertSql} returning "id" into lastId; dbms_output.enable; dbms_output.put_line(lastId); dbms_output.get_line(:ln, :st); end;`;
const saveResult = await this.query(sql2, parameters.concat([
@ -236,7 +236,7 @@ export class OracleQueryRunner implements QueryRunner {
const updateValues = this.parametrize(valuesMap).join(", ");
const conditionString = this.parametrize(conditions).join(" AND ");
const sql = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const sql = `UPDATE "${tableName}" SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
const allParameters = updateParams.concat(conditionParams);
@ -263,7 +263,7 @@ export class OracleQueryRunner implements QueryRunner {
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
const sql = `DELETE FROM "${tableName}" WHERE ${conditionString}`;
await this.query(sql, parameters);
}
@ -276,16 +276,16 @@ export class OracleQueryRunner implements QueryRunner {
let sql = "";
if (hasLevel) {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant", "level") ` +
`SELECT "ancestor", ${newEntityId}, "level" + 1 FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
} else {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}" ("ancestor", "descendant") ` +
`SELECT "ancestor", ${newEntityId} FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
}
await this.query(sql);
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
const results: ObjectLiteral[] = await this.query(`SELECT MAX("level") as "level" FROM "${tableName}" WHERE "descendant" = ${parentId}`);
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
}
@ -787,7 +787,7 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
await this.query(`TRUNCATE TABLE "${tableName}"`);
}
// -------------------------------------------------------------------------
@ -805,7 +805,7 @@ AND cons.constraint_name = cols.constraint_name AND cons.owner = cols.owner ORDE
* Parametrizes given object of values. Used to create column=value queries.
*/
protected parametrize(objectLiteral: ObjectLiteral): string[] {
return Object.keys(objectLiteral).map(key => this.driver.escapeColumnName(key) + "=:" + key);
return Object.keys(objectLiteral).map(key => `"${key}"=:${key}`);
}
/**

View File

@ -13,6 +13,7 @@ import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import {PostgresConnectionOptions} from "./PostgresConnectionOptions";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {QueryRunner} from "../../query-runner/QueryRunner";
/**
* Organizes communication with PostgreSQL DBMS.
@ -43,6 +44,11 @@ export class PostgresDriver implements Driver {
*/
pool: any;
/**
* We store all created query runners because we need to release them.
*/
connectedQueryRunners: QueryRunner[] = [];
// -------------------------------------------------------------------------
// Public Implemented Properties
// -------------------------------------------------------------------------
@ -167,15 +173,11 @@ export class PostgresDriver implements Driver {
if (!this.pool)
return Promise.reject(new ConnectionIsNotSetError("postgres"));
return new Promise<void>((ok, fail) => {
return new Promise<void>(async (ok, fail) => {
const handler = (err: any) => err ? fail(err) : ok();
// todo: do we really needed this code:
/*this.databaseConnectionPool.forEach(dbConnection => {
if (dbConnection && dbConnection.releaseCallback) {
dbConnection.releaseCallback();
}
});*/
// this is checked fact that postgres.pool.end do not release all non released connections
// await Promise.all(this.connectedQueryRunners.map(queryRunner => queryRunner.release()));
this.pool.end(handler);
this.pool = undefined;
ok();
@ -279,21 +281,21 @@ export class PostgresDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return "\"" + columnName + "\"";
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return "\"" + aliasName + "\"";
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return "\"" + tableName + "\"";
}

View File

@ -74,6 +74,7 @@ export class PostgresQueryRunner implements QueryRunner {
this.databaseConnectionPromise = new Promise((ok, fail) => {
this.driver.pool.connect((err: any, connection: any, release: Function) => {
this.driver.connectedQueryRunners.push(this);
this.databaseConnection = connection;
this.releaseCallback = release;
@ -101,6 +102,10 @@ export class PostgresQueryRunner implements QueryRunner {
this.isReleased = true;
if (this.releaseCallback)
this.releaseCallback();
const index = this.driver.connectedQueryRunners.indexOf(this);
if (index !== -1) this.driver.connectedQueryRunners.splice(index);
return Promise.resolve();
}
@ -202,11 +207,11 @@ export class PostgresQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const keys = Object.keys(keyValues);
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
const columns = keys.map(key => `"${key}"`).join(", ");
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
const sql = columns.length > 0
? `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values}) ${ generatedColumn ? " RETURNING " + this.driver.escapeColumnName(generatedColumn.databaseName) : "" }`
: `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES ${ generatedColumn ? " RETURNING " + this.driver.escapeColumnName(generatedColumn.databaseName) : "" }`;
? `INSERT INTO "${tableName}"(${columns}) VALUES (${values}) ${ generatedColumn ? ` RETURNING "${generatedColumn.databaseName}"` : "" }`
: `INSERT INTO "${tableName}" DEFAULT VALUES ${ generatedColumn ? ` RETURNING "${generatedColumn.databaseName}"` : "" }`;
const parameters = keys.map(key => keyValues[key]);
const result: ObjectLiteral[] = await this.query(sql, parameters);
if (generatedColumn)
@ -224,7 +229,7 @@ export class PostgresQueryRunner implements QueryRunner {
const updateValues = this.parametrize(valuesMap).join(", ");
const conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
const query = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const query = `UPDATE "${tableName}" SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
const allParameters = updateParams.concat(conditionParams);
@ -251,7 +256,7 @@ export class PostgresQueryRunner implements QueryRunner {
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
const sql = `DELETE FROM "${tableName}" WHERE ${conditionString}`;
await this.query(sql, parameters);
}
@ -264,12 +269,12 @@ export class PostgresQueryRunner implements QueryRunner {
let sql = "";
if (hasLevel) {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant", "level") ` +
`SELECT "ancestor", ${newEntityId}, "level" + 1 FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
} else {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant") ` +
`SELECT "ancestor", ${newEntityId} FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
}
await this.query(sql);
@ -796,7 +801,7 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}'
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
await this.query(`TRUNCATE TABLE "${tableName}"`);
}
// -------------------------------------------------------------------------
@ -821,7 +826,7 @@ where constraint_type = 'PRIMARY KEY' AND c.table_schema = '${this.schemaName}'
* Parametrizes given object of values. Used to create column=value queries.
*/
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
return Object.keys(objectLiteral).map((key, index) => this.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
return Object.keys(objectLiteral).map((key, index) => "\"" + key + "\"=$" + (startIndex + index + 1));
}
/**

View File

@ -229,21 +229,21 @@ export class SqliteDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return "\"" + columnName + "\"";
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return "\"" + aliasName + "\"";
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return "\"" + tableName + "\"";
}

View File

@ -202,9 +202,9 @@ export class SqliteQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const keys = Object.keys(keyValues);
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
const columns = keys.map(key => `"${key}"`).join(", ");
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
const sql = columns.length > 0 ? (`INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`) : `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES`;
const sql = columns.length > 0 ? (`INSERT INTO "${tableName}"(${columns}) VALUES (${values})`) : `INSERT INTO "${tableName}" DEFAULT VALUES`;
const parameters = keys.map(key => keyValues[key]);
return new Promise<any[]>(async (ok, fail) => {
@ -235,7 +235,7 @@ export class SqliteQueryRunner implements QueryRunner {
const updateValues = this.parametrize(valuesMap).join(", ");
const conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
const query = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const query = `UPDATE "${tableName}" SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
const allParameters = updateParams.concat(conditionParams);
@ -262,7 +262,7 @@ export class SqliteQueryRunner implements QueryRunner {
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
const sql = `DELETE FROM "${tableName}" WHERE ${conditionString}`;
await this.query(sql, parameters);
}
@ -275,12 +275,12 @@ export class SqliteQueryRunner implements QueryRunner {
let sql = "";
if (hasLevel) {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant", "level") ` +
`SELECT "ancestor", ${newEntityId}, "level" + 1 FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
} else {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant") ` +
`SELECT "ancestor", ${newEntityId} FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
}
await this.query(sql);
@ -783,7 +783,7 @@ export class SqliteQueryRunner implements QueryRunner {
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
await this.query(`DELETE FROM ${this.driver.escapeTableName(tableName)}`);
await this.query(`DELETE FROM "${tableName}"`);
}
// -------------------------------------------------------------------------
@ -794,7 +794,7 @@ export class SqliteQueryRunner implements QueryRunner {
* Parametrizes given object of values. Used to create column=value queries.
*/
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
return Object.keys(objectLiteral).map((key, index) => this.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
return Object.keys(objectLiteral).map((key, index) => `"${key}"` + "=$" + (startIndex + index + 1));
}
/**

View File

@ -209,21 +209,21 @@ export class SqlServerDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return `"${columnName}"`;
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return `"${aliasName}"`;
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return `"${tableName}"`;
}

View File

@ -255,13 +255,13 @@ export class SqlServerQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const keys = Object.keys(keyValues);
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
const columns = keys.map(key => `"${key}"`).join(", ");
const values = keys.map((key, index) => "@" + index).join(",");
const parameters = keys.map(key => keyValues[key]);
const sql = columns.length > 0
? `INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }VALUES (${values})`
: `INSERT INTO ${this.driver.escapeTableName(tableName)} ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }DEFAULT VALUES `;
? `INSERT INTO "${tableName}"(${columns}) ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }VALUES (${values})`
: `INSERT INTO "${tableName}" ${ generatedColumn ? "OUTPUT INSERTED." + generatedColumn.databaseName + " " : "" }DEFAULT VALUES `;
const result = await this.query(sql, parameters);
return generatedColumn ? result instanceof Array ? result[0][generatedColumn.databaseName] : result[generatedColumn.databaseName] : undefined;
@ -280,7 +280,7 @@ export class SqlServerQueryRunner implements QueryRunner {
const updateValues = this.parametrize(valuesMap).join(", ");
const conditionString = this.parametrize(conditions, updateParams.length).join(" AND ");
const sql = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const sql = `UPDATE "${tableName}" SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
await this.query(sql, allParameters);
}
@ -305,7 +305,7 @@ export class SqlServerQueryRunner implements QueryRunner {
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
const sql = `DELETE FROM "${tableName}" WHERE ${conditionString}`;
await this.query(sql, parameters);
}
@ -318,16 +318,16 @@ export class SqlServerQueryRunner implements QueryRunner {
let sql = "";
if (hasLevel) {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
sql = `INSERT INTO "${tableName}"("ancestor", "descendant", "level") ` +
`SELECT "ancestor", ${newEntityId}, "level" + 1 FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
} else {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
sql = `INSERT INTO "${tableName}"("ancestor", "descendant") ` +
`SELECT "ancestor", ${newEntityId} FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
}
await this.query(sql);
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId}`);
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM "${tableName}" WHERE descendant = ${parentId}`);
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
}
@ -833,7 +833,7 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
await this.query(`TRUNCATE TABLE ${this.driver.escapeTableName(tableName)}`);
await this.query(`TRUNCATE TABLE "${tableName}"`);
}
// -------------------------------------------------------------------------
@ -852,7 +852,7 @@ WHERE columnUsages.TABLE_CATALOG = '${this.dbName}' AND tableConstraints.TABLE_C
*/
protected parametrize(objectLiteral: ObjectLiteral, startFrom: number = 0): string[] {
return Object.keys(objectLiteral).map((key, index) => {
return this.driver.escapeColumnName(key) + "=@" + (startFrom + index);
return `"${key}"` + "=@" + (startFrom + index);
});
}

View File

@ -165,21 +165,21 @@ export class WebsqlDriver implements Driver {
/**
* Escapes a column name.
*/
escapeColumnName(columnName: string): string {
escapeColumn(columnName: string): string {
return columnName; // "`" + columnName + "`";
}
/**
* Escapes an alias.
*/
escapeAliasName(aliasName: string): string {
escapeAlias(aliasName: string): string {
return aliasName; // "`" + aliasName + "`";
}
/**
* Escapes a table name.
*/
escapeTableName(tableName: string): string {
escapeTable(tableName: string): string {
return tableName; // "`" + tableName + "`";
}

View File

@ -205,9 +205,9 @@ export class WebsqlQueryRunner implements QueryRunner {
throw new QueryRunnerAlreadyReleasedError();
const keys = Object.keys(keyValues);
const columns = keys.map(key => this.driver.escapeColumnName(key)).join(", ");
const columns = keys.map(key => `"${key}"`).join(", ");
const values = keys.map((key, index) => "$" + (index + 1)).join(",");
const sql = columns.length > 0 ? (`INSERT INTO ${this.driver.escapeTableName(tableName)}(${columns}) VALUES (${values})`) : `INSERT INTO ${this.driver.escapeTableName(tableName)} DEFAULT VALUES`;
const sql = columns.length > 0 ? (`INSERT INTO "${tableName}"(${columns}) VALUES (${values})`) : `INSERT INTO "${tableName}" DEFAULT VALUES`;
const parameters = keys.map(key => keyValues[key]);
return new Promise<any[]>(async (ok, fail) => {
@ -239,7 +239,7 @@ export class WebsqlQueryRunner implements QueryRunner {
const updateValues = this.parametrize(valuesMap).join(", ");
const conditionString = this.parametrize(conditions, Object.keys(valuesMap).length).join(" AND ");
const query = `UPDATE ${this.driver.escapeTableName(tableName)} SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const query = `UPDATE "${tableName}" SET ${updateValues} ${conditionString ? (" WHERE " + conditionString) : ""}`;
const updateParams = Object.keys(valuesMap).map(key => valuesMap[key]);
const conditionParams = Object.keys(conditions).map(key => conditions[key]);
const allParameters = updateParams.concat(conditionParams);
@ -266,7 +266,7 @@ export class WebsqlQueryRunner implements QueryRunner {
const conditionString = typeof conditions === "string" ? conditions : this.parametrize(conditions).join(" AND ");
const parameters = conditions instanceof Object ? Object.keys(conditions).map(key => (conditions as ObjectLiteral)[key]) : maybeParameters;
const sql = `DELETE FROM ${this.driver.escapeTableName(tableName)} WHERE ${conditionString}`;
const sql = `DELETE FROM "${tableName}" WHERE ${conditionString}`;
await this.query(sql, parameters);
}
@ -279,16 +279,16 @@ export class WebsqlQueryRunner implements QueryRunner {
let sql = "";
if (hasLevel) {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant, level) ` +
`SELECT ancestor, ${newEntityId}, level + 1 FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant", "level") ` +
`SELECT "ancestor", ${newEntityId}, "level" + 1 FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}, 1`;
} else {
sql = `INSERT INTO ${this.driver.escapeTableName(tableName)}(ancestor, descendant) ` +
`SELECT ancestor, ${newEntityId} FROM ${this.driver.escapeTableName(tableName)} WHERE descendant = ${parentId} ` +
sql = `INSERT INTO "${tableName}"("ancestor", "descendant") ` +
`SELECT "ancestor", ${newEntityId} FROM "${tableName}" WHERE "descendant" = ${parentId} ` +
`UNION ALL SELECT ${newEntityId}, ${newEntityId}`;
}
await this.query(sql);
const results: ObjectLiteral[] = await this.query(`SELECT MAX(level) as level FROM ${tableName} WHERE descendant = ${parentId}`);
const results: ObjectLiteral[] = await this.query(`SELECT MAX("level") as "level" FROM ${tableName} WHERE "descendant" = ${parentId}`);
return results && results[0] && results[0]["level"] ? parseInt(results[0]["level"]) + 1 : 1;
}
@ -784,7 +784,7 @@ export class WebsqlQueryRunner implements QueryRunner {
* Truncates table.
*/
async truncate(tableName: string): Promise<void> {
await this.query(`DELETE FROM ${this.driver.escapeTableName(tableName)}`);
await this.query(`DELETE FROM "${tableName}"`);
}
// -------------------------------------------------------------------------
@ -795,7 +795,7 @@ export class WebsqlQueryRunner implements QueryRunner {
* Parametrizes given object of values. Used to create column=value queries.
*/
protected parametrize(objectLiteral: ObjectLiteral, startIndex: number = 0): string[] {
return Object.keys(objectLiteral).map((key, index) => this.driver.escapeColumnName(key) + "=$" + (startIndex + index + 1));
return Object.keys(objectLiteral).map((key, index) => `"${key}"` + "=$" + (startIndex + index + 1));
}
/**

View File

@ -551,8 +551,8 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
let databaseEntities: ObjectLiteral[] = [];
// create shortcuts for better readability
const ea = (alias: string) => this.connection.driver.escapeAliasName(alias);
const ec = (column: string) => this.connection.driver.escapeColumnName(column);
const ea = (alias: string) => this.connection.driver.escapeAlias(alias);
const ec = (column: string) => this.connection.driver.escapeColumn(column);
if (relation.isManyToManyOwner) {

View File

@ -1161,7 +1161,7 @@ export class QueryBuilder<Entity> {
escapeAlias(name: string) {
if (!this.expressionMap.disableEscaping)
return name;
return this.connection.driver.escapeAliasName(name);
return this.connection.driver.escapeAlias(name);
}
/**
@ -1170,7 +1170,7 @@ export class QueryBuilder<Entity> {
escapeColumn(name: string) {
if (!this.expressionMap.disableEscaping)
return name;
return this.connection.driver.escapeColumnName(name);
return this.connection.driver.escapeColumn(name);
}
/**
@ -1179,7 +1179,7 @@ export class QueryBuilder<Entity> {
escapeTable(name: string) {
if (!this.expressionMap.disableEscaping)
return name;
return this.connection.driver.escapeTableName(name);
return this.connection.driver.escapeTable(name);
}
/**

View File

@ -254,8 +254,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
.delete()
.from(relation.junctionEntityMetadata!.tableName, "junctionEntity");
const firstColumnName = this.connection.driver.escapeColumnName(relation.isOwning ? relation.junctionEntityMetadata!.columns[0].databaseName : relation.junctionEntityMetadata!.columns[1].databaseName);
const secondColumnName = this.connection.driver.escapeColumnName(relation.isOwning ? relation.junctionEntityMetadata!.columns[1].databaseName : relation.junctionEntityMetadata!.columns[0].databaseName);
const firstColumnName = this.connection.driver.escapeColumn(relation.isOwning ? relation.junctionEntityMetadata!.columns[0].databaseName : relation.junctionEntityMetadata!.columns[1].databaseName);
const secondColumnName = this.connection.driver.escapeColumn(relation.isOwning ? relation.junctionEntityMetadata!.columns[1].databaseName : relation.junctionEntityMetadata!.columns[0].databaseName);
relatedEntityIds.forEach((relatedEntityId, index) => {
qb.orWhere(`(${firstColumnName}=:entityId AND ${secondColumnName}=:relatedEntity_${index})`)
@ -446,8 +446,8 @@ export class SpecificRepository<Entity extends ObjectLiteral> {
return [];
// create shortcuts for better readability
const ea = (alias: string) => this.connection.driver.escapeAliasName(alias);
const ec = (column: string) => this.connection.driver.escapeColumnName(column);
const ea = (alias: string) => this.connection.driver.escapeAlias(alias);
const ec = (column: string) => this.connection.driver.escapeColumn(column);
let ids: any[] = [];
// console.log("entityOrEntities:", entityOrEntities);

View File

@ -44,8 +44,8 @@ export class TreeRepository<Entity> extends Repository<Entity> {
createDescendantsQueryBuilder(alias: string, closureTableAlias: string, entity: Entity): QueryBuilder<Entity> {
// create shortcuts for better readability
const escapeAlias = (alias: string) => this.manager.connection.driver.escapeAliasName(alias);
const escapeColumn = (column: string) => this.manager.connection.driver.escapeColumnName(column);
const escapeAlias = (alias: string) => this.manager.connection.driver.escapeAlias(alias);
const escapeColumn = (column: string) => this.manager.connection.driver.escapeColumn(column);
const joinCondition = `${escapeAlias(alias)}.${escapeColumn(this.metadata.primaryColumns[0].databaseName)}=${escapeAlias(closureTableAlias)}.${escapeColumn("descendant")}`;
return this.createQueryBuilder(alias)
@ -92,8 +92,8 @@ export class TreeRepository<Entity> extends Repository<Entity> {
createAncestorsQueryBuilder(alias: string, closureTableAlias: string, entity: Entity): QueryBuilder<Entity> {
// create shortcuts for better readability
const escapeAlias = (alias: string) => this.manager.connection.driver.escapeAliasName(alias);
const escapeColumn = (column: string) => this.manager.connection.driver.escapeColumnName(column);
const escapeAlias = (alias: string) => this.manager.connection.driver.escapeAlias(alias);
const escapeColumn = (column: string) => this.manager.connection.driver.escapeColumn(column);
const joinCondition = `${escapeAlias(alias)}.${escapeColumn(this.metadata.primaryColumns[0].databaseName)}=${escapeAlias(closureTableAlias)}.${escapeColumn("ancestor")}`;
return this.createQueryBuilder(alias)

View File

@ -220,8 +220,9 @@ describe("Connection", () => {
afterEach(() => closeTestingConnections(connections));
it("database should be empty after schema sync", () => Promise.all(connections.map(async connection => {
await connection.syncSchema(true);
const queryRunner = await connection.driver.createQueryRunner();
const queryRunner = connection.driver.createQueryRunner();
let schema = await queryRunner.loadTableSchemas(["view"]);
await queryRunner.release();
expect(schema.some(table => table.name === "view")).to.be.false;
})));
@ -303,8 +304,9 @@ describe("Connection", () => {
const commentRepo = connection.getRepository(CommentV1);
await commentRepo.save(comment);
const query = await connection.driver.createQueryRunner();
const rows = await query.query(`select * from "${schemaName}"."comment" where id = $1`, [comment.id]);
const queryRunner = connection.driver.createQueryRunner();
const rows = await queryRunner.query(`select * from "${schemaName}"."comment" where id = $1`, [comment.id]);
await queryRunner.release();
expect(rows[0]["context"]).to.be.eq(comment.context);
}));

View File

@ -16,8 +16,9 @@ describe("jsonb type", () => {
it("should make correct schema with Postgres' jsonb type", () => Promise.all(connections.map(async connection => {
await connection.syncSchema(true);
const queryRunner = await connection.driver.createQueryRunner();
const queryRunner = connection.driver.createQueryRunner();
let schema = await queryRunner.loadTableSchema("record");
await queryRunner.release();
expect(schema).not.to.be.empty;
expect(schema!.columns.find(columnSchema => columnSchema.name === "config" && columnSchema.type === "json")).to.be.not.empty;
expect(schema!.columns.find(columnSchema => columnSchema.name === "data" && columnSchema.type === "jsonb")).to.be.not.empty;

View File

@ -22,8 +22,9 @@ describe("uuid type", () => {
it("should make correct schema with Postgres' uuid type", () => Promise.all(connections.map(async connection => {
await connection.syncSchema(true);
const queryRunner = await connection.driver.createQueryRunner();
const queryRunner = connection.driver.createQueryRunner();
let schema = await queryRunner.loadTableSchema("record");
await queryRunner.release();
expect(schema).not.to.be.empty;
expect(schema!.columns.find(columnSchema => columnSchema.name === "id" && columnSchema.type === "uuid" && columnSchema.isGenerated)).to.be.not.empty;
})));

View File

@ -17,14 +17,14 @@ describe("github issues > #512 Table name escaping in UPDATE in QueryBuilder", (
it("should escape table name using driver's escape function in UPDATE", () => Promise.all(connections.map(async connection => {
const driver = connection.driver;
const queryBuilder = connection.entityManager.createQueryBuilder(Post, "post");
const queryBuilder = connection.manager.createQueryBuilder(Post, "post");
const query = queryBuilder
.update({
title: "Some Title",
})
.getSql();
return query.should.contain(driver.escapeTableName("Posts"));
return query.should.contain(driver.escapeTable("Posts"));
})));
});