Skip to content

Commit

Permalink
feat: support for CTE expressions - code review remarks
Browse files Browse the repository at this point in the history
  • Loading branch information
Ginden committed Feb 26, 2022
1 parent d06e5e8 commit b5a962d
Show file tree
Hide file tree
Showing 16 changed files with 189 additions and 120 deletions.
11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ services:
MYSQL_PASSWORD: "test"
MYSQL_DATABASE: "test"

mysql8:
image: "mysql:8.0.28"
container_name: "typeorm-mysql8"
ports:
- "3308:3306"
environment:
MYSQL_ROOT_PASSWORD: "admin"
MYSQL_USER: "test"
MYSQL_PASSWORD: "test"
MYSQL_DATABASE: "test"

# mariadb
mariadb:
image: "mariadb:10.5.13"
Expand Down
12 changes: 12 additions & 0 deletions ormconfig.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
"database": "test",
"logging": false
},
{
"skip": false,
"name": "mysql8",
"connectorPackage": "mysql2",
"type": "mysql",
"host": "localhost",
"port": 3308,
"username": "root",
"password": "admin",
"database": "test",
"logging": false
},
{
"skip": false,
"name": "mariadb",
Expand Down
4 changes: 3 additions & 1 deletion src/driver/aurora-data-api/AuroraDataApiDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,9 @@ export class AuroraDataApiDriver implements Driver {
*/
maxAliasLength = 63;

cteCapabilities: CteCapabilities = {};
cteCapabilities: CteCapabilities = {
enabled: true,
};

// -------------------------------------------------------------------------
// Constructor
Expand Down
7 changes: 6 additions & 1 deletion src/driver/cockroachdb/CockroachDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,12 @@ export class CockroachDriver implements Driver {
*/
maxAliasLength?: number;

cteCapabilities: CteCapabilities = { writable: true, materializedHint: true, requiresRecursiveHint: true };
cteCapabilities: CteCapabilities = {
enabled: true,
writable: true,
materializedHint: true,
requiresRecursiveHint: true
};

// -------------------------------------------------------------------------
// Constructor
Expand Down
4 changes: 3 additions & 1 deletion src/driver/mongodb/MongoDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,9 @@ export class MongoDriver implements Driver {
"retryWrites"
];

cteCapabilities: CteCapabilities = {};
cteCapabilities: CteCapabilities = {
enabled: false,
};

// -------------------------------------------------------------------------
// Constructor
Expand Down
5 changes: 5 additions & 0 deletions src/driver/mysql/MysqlConnectionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,9 @@ export interface MysqlConnectionOptions extends BaseConnectionOptions, MysqlConn

};

/**
* TypeORM will automatically use package found in your node_modules, prioritizing mysql over mysql2,
* but you can specify it manually
*/
readonly connectorPackage?: "mysql" | "mysql2";
}
20 changes: 15 additions & 5 deletions src/driver/mysql/MysqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ export class MysqlDriver implements Driver {
};

cteCapabilities: CteCapabilities = {
enabled: false,
requiresRecursiveHint: true,
};

Expand Down Expand Up @@ -381,17 +382,25 @@ export class MysqlDriver implements Driver {
await queryRunner.release();
}

if (this.options.type === "mariadb") {
const result = await this.createQueryRunner("master")
.query(`SELECT VERSION() AS \`version\``) as { version: string; }[];
const dbVersion = result[0].version;
const result = await this.createQueryRunner("master")
.query(`SELECT VERSION() AS \`version\``) as { version: string; }[];
const dbVersion = result[0].version;

if (this.options.type === "mariadb") {
if (VersionUtils.isGreaterOrEqual(dbVersion, "10.0.5")) {
this._isReturningSqlSupported.delete = true;
}
if (VersionUtils.isGreaterOrEqual(dbVersion, "10.5.0")) {
this._isReturningSqlSupported.insert = true;
}
if (VersionUtils.isGreaterOrEqual(dbVersion, "10.2.0")) {
this.cteCapabilities.enabled = true;
}
}
if (this.options.type === "mysql") {
if(VersionUtils.isGreaterOrEqual(dbVersion, "8.0.0")) {
this.cteCapabilities.enabled = true;
}
}
}

Expand Down Expand Up @@ -955,9 +964,10 @@ export class MysqlDriver implements Driver {
* Loads all driver dependencies.
*/
protected loadDependencies(): void {
const defaultPackage = this.options.connectorPackage || "mysql";
try {
// try to load first supported package
const mysql = this.options.driver || PlatformTools.load("mysql");
const mysql = this.options.driver || PlatformTools.load(defaultPackage);
this.mysql = mysql;
/*
* Some frameworks (such as Jest) may mess up Node's require cache and provide garbage for the 'mysql' module
Expand Down
6 changes: 4 additions & 2 deletions src/driver/oracle/OracleDriver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Driver} from "../Driver";
import {ConnectionIsNotSetError} from "../../error/ConnectionIsNotSetError";
import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError";
import { CteCapabilities } from "../types/CteCapabilities";
import {CteCapabilities} from "../types/CteCapabilities";
import {OracleQueryRunner} from "./OracleQueryRunner";
import {ObjectLiteral} from "../../common/ObjectLiteral";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
Expand Down Expand Up @@ -223,7 +223,9 @@ export class OracleDriver implements Driver {
*/
maxAliasLength = 30;

cteCapabilities: CteCapabilities = {};
cteCapabilities: CteCapabilities = {
enabled: true,
};

// -------------------------------------------------------------------------
// Constructor
Expand Down
3 changes: 2 additions & 1 deletion src/driver/postgres/PostgresDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {DateUtils} from "../../util/DateUtils";
import {OrmUtils} from "../../util/OrmUtils";
import {Driver} from "../Driver";
import {ColumnType} from "../types/ColumnTypes";
import { CteCapabilities } from "../types/CteCapabilities";
import {CteCapabilities} from "../types/CteCapabilities";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ReplicationMode} from "../types/ReplicationMode";
Expand Down Expand Up @@ -278,6 +278,7 @@ export class PostgresDriver implements Driver {
maxAliasLength = 63;

cteCapabilities: CteCapabilities = {
enabled: true,
writable: true,
requiresRecursiveHint: true,
materializedHint: true,
Expand Down
6 changes: 4 additions & 2 deletions src/driver/sap/SapDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {ApplyValueTransformers} from "../../util/ApplyValueTransformers";
import {DateUtils} from "../../util/DateUtils";
import {OrmUtils} from "../../util/OrmUtils";
import {Driver} from "../Driver";
import { CteCapabilities } from "../types/CteCapabilities";
import {CteCapabilities} from "../types/CteCapabilities";
import {DataTypeDefaults} from "../types/DataTypeDefaults";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {SapConnectionOptions} from "./SapConnectionOptions";
Expand Down Expand Up @@ -212,7 +212,9 @@ export class SapDriver implements Driver {
*/
maxAliasLength = 128;

cteCapabilities: CteCapabilities = {};
cteCapabilities: CteCapabilities = {
enabled: true,
};

// -------------------------------------------------------------------------
// Constructor
Expand Down
3 changes: 2 additions & 1 deletion src/driver/sqlite-abstract/AbstractSqliteDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {ColumnMetadata} from "../../metadata/ColumnMetadata";
import {DateUtils} from "../../util/DateUtils";
import {Connection} from "../../connection/Connection";
import {RdbmsSchemaBuilder} from "../../schema-builder/RdbmsSchemaBuilder";
import { CteCapabilities } from "../types/CteCapabilities";
import {CteCapabilities} from "../types/CteCapabilities";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {QueryRunner} from "../../query-runner/QueryRunner";
Expand Down Expand Up @@ -223,6 +223,7 @@ export abstract class AbstractSqliteDriver implements Driver {
maxAliasLength?: number;

cteCapabilities: CteCapabilities = {
enabled: true,
requiresRecursiveHint: true,
};

Expand Down
1 change: 1 addition & 0 deletions src/driver/sqlserver/SqlServerDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export class SqlServerDriver implements Driver {
};

cteCapabilities: CteCapabilities = {
enabled: true,
// todo: enable it for SQL Server - it's partially supported, but there are issues with generation of non-standard OUTPUT clause
writable: false,
};
Expand Down
6 changes: 6 additions & 0 deletions src/driver/types/CteCapabilities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export interface CteCapabilities {
/**
* Are CTEs supported at all?
*/
enabled: boolean;


/**
* Are RETURNING clauses supported in CTEs?
*/
Expand Down
6 changes: 6 additions & 0 deletions test/functional/query-builder/cte/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Connection } from "../../../../src";
import { CteCapabilities } from "../../../../src/driver/types/CteCapabilities";

export function filterByCteCapabilities(capability: keyof CteCapabilities, equalsTo: boolean = true): (conn: Connection) => boolean {
return conn => conn.driver.cteCapabilities[capability] === equalsTo;
}
35 changes: 19 additions & 16 deletions test/functional/query-builder/cte/recursive-cte.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from "chai";
import { Connection } from "../../../../src";
import { closeTestingConnections, createTestingConnections, reloadTestingDatabases } from "../../../utils/test-utils";
import { filterByCteCapabilities } from "./helpers";

describe("query builder > cte > recursive", () => {
let connections: Connection[];
Expand All @@ -19,21 +20,23 @@ describe("query builder > cte > recursive", () => {
beforeEach(() => reloadTestingDatabases(connections));
after(() => closeTestingConnections(connections));

it("should work with simple recursive query", () => Promise.all(connections.map(async connection => {
const qb = await connection
.createQueryBuilder()
.select([])
.from("cte", "cte")
.addCommonTableExpression(`
SELECT 1
UNION ALL
SELECT cte.foo + 1
FROM cte
WHERE cte.foo < 10
`, "cte", { recursive: true, columnNames: ["foo"] })
.addSelect("cte.foo", "foo")
.getRawMany<{foo: number}>();
it("should work with simple recursive query", () => Promise.all(connections
.filter(filterByCteCapabilities("enabled"))
.map(async connection => {
const qb = await connection
.createQueryBuilder()
.select([])
.from("cte", "cte")
.addCommonTableExpression(`
SELECT 1
UNION ALL
SELECT cte.foo + 1
FROM cte
WHERE cte.foo < 10
`, "cte", { recursive: true, columnNames: ["foo"] })
.addSelect("cte.foo", "foo")
.getRawMany<{ foo: number }>();

expect(qb).to.have.length(10);
})));
expect(qb).to.have.length(10);
})));
});

0 comments on commit b5a962d

Please sign in to comment.