Skip to content

Commit

Permalink
feat: support for Common Table Expressions
Browse files Browse the repository at this point in the history
Resolves #1116 #5899
  • Loading branch information
Ginden committed Feb 17, 2022
1 parent 7dbe956 commit 9045252
Show file tree
Hide file tree
Showing 23 changed files with 393 additions and 47 deletions.
33 changes: 33 additions & 0 deletions docs/select-query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -1148,3 +1148,36 @@ const users = await connection.getRepository(User)

You will get all the rows, including the ones which are deleted.

## Common table expressions

`QueryBuilder` instances
support [common table expressions](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression)
, if minimal supported version of your database supports them.

```typescript
const users = await connection.getRepository(User)
.createQueryBuilder('user')
.select("user.id", 'id')
.addCommonTableExpression(`
SELECT "userId" FROM "post"
`, 'post_users_ids')
.where(`user.id IN (SELECT "userId" FROM 'post_users_ids')`)
.getMany();
```

Result values of `InsertQueryBuilder` or `UpdateQueryBuilder` can be used in Postgres:

```typescript
const insertQueryBuilder = await connection.getRepository(User)
.createQueryBuilder()
.insert({
name: 'John Smith'
})
.returning(['id']);

const users = await connection.getRepository(User)
.createQueryBuilder('user')
.addCommonTableExpression(insertQueryBuilder, 'insert_results')
.where(`user.id IN (SELECT "id" FROM 'insert_results')`)
.getMany();
```
3 changes: 3 additions & 0 deletions src/driver/Driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {QueryRunner} from "../query-runner/QueryRunner";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {ObjectLiteral} from "../common/ObjectLiteral";
import {ColumnType} from "./types/ColumnTypes";
import {CteCapabilities} from "./types/CteCapabilities";
import {MappedColumnTypes} from "./types/MappedColumnTypes";
import {SchemaBuilder} from "../schema-builder/SchemaBuilder";
import {DataTypeDefaults} from "./types/DataTypeDefaults";
Expand Down Expand Up @@ -93,6 +94,8 @@ export interface Driver {
*/
maxAliasLength?: number;

cteCapabilities: CteCapabilities;

/**
* Performs connection to the database.
* Depend on driver type it may create a connection pool.
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
@@ -1,5 +1,6 @@
import {Driver} from "../Driver";
import {DriverUtils} from "../DriverUtils";
import { CteCapabilities } from "../types/CteCapabilities";
import {AuroraDataApiQueryRunner} from "./AuroraDataApiQueryRunner";
import {ObjectLiteral} from "../../common/ObjectLiteral";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
Expand Down Expand Up @@ -298,13 +299,14 @@ export class AuroraDataApiDriver implements Driver {
"bigint": { width: 20 }
};


/**
* Max length allowed by MySQL for aliases.
* @see https://dev.mysql.com/doc/refman/5.5/en/identifiers.html
*/
maxAliasLength = 63;

cteCapabilities: CteCapabilities = {};

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/driver/cockroachdb/CockroachDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {ObjectLiteral} from "../../common/ObjectLiteral";
import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError";
import {DriverUtils} from "../DriverUtils";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
import { CteCapabilities } from "../types/CteCapabilities";
import {CockroachConnectionCredentialsOptions} from "./CockroachConnectionCredentialsOptions";
import {CockroachConnectionOptions} from "./CockroachConnectionOptions";
import {DateUtils} from "../../util/DateUtils";
Expand Down Expand Up @@ -232,6 +233,8 @@ export class CockroachDriver implements Driver {
*/
maxAliasLength?: number;

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

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/driver/mongodb/MongoDriver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Driver} from "../Driver";
import {ConnectionIsNotSetError} from "../../error/ConnectionIsNotSetError";
import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError";
import { CteCapabilities } from "../types/CteCapabilities";
import {MongoQueryRunner} from "./MongoQueryRunner";
import {ObjectLiteral} from "../../common/ObjectLiteral";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
Expand Down Expand Up @@ -207,6 +208,8 @@ export class MongoDriver implements Driver {
"retryWrites"
];

cteCapabilities: CteCapabilities = {};

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions src/driver/mysql/MysqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Driver} from "../Driver";
import {ConnectionIsNotSetError} from "../../error/ConnectionIsNotSetError";
import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError";
import {DriverUtils} from "../DriverUtils";
import { CteCapabilities } from "../types/CteCapabilities";
import {MysqlQueryRunner} from "./MysqlQueryRunner";
import {ObjectLiteral} from "../../common/ObjectLiteral";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
Expand Down Expand Up @@ -304,6 +305,10 @@ export class MysqlDriver implements Driver {
*/
maxAliasLength = 63;

cteCapabilities: CteCapabilities = {
requiresRecursiveHint: true,
};

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/driver/oracle/OracleDriver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Driver} from "../Driver";
import {ConnectionIsNotSetError} from "../../error/ConnectionIsNotSetError";
import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError";
import { CteCapabilities } from "../types/CteCapabilities";
import {OracleQueryRunner} from "./OracleQueryRunner";
import {ObjectLiteral} from "../../common/ObjectLiteral";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
Expand Down Expand Up @@ -217,6 +218,8 @@ export class OracleDriver implements Driver {
*/
maxAliasLength = 30;

cteCapabilities: CteCapabilities = {};

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions src/driver/postgres/PostgresDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +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 {DataTypeDefaults} from "../types/DataTypeDefaults";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ReplicationMode} from "../types/ReplicationMode";
Expand Down Expand Up @@ -271,6 +272,12 @@ export class PostgresDriver implements Driver {
*/
maxAliasLength = 63;

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

isGeneratedColumnsSupported: boolean = false;

// -------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions src/driver/sap/SapDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +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 {DataTypeDefaults} from "../types/DataTypeDefaults";
import {MappedColumnTypes} from "../types/MappedColumnTypes";
import {SapConnectionOptions} from "./SapConnectionOptions";
Expand Down Expand Up @@ -206,6 +207,8 @@ export class SapDriver implements Driver {
*/
maxAliasLength = 128;

cteCapabilities: CteCapabilities = {};

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions src/driver/sqlite-abstract/AbstractSqliteDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +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 {MappedColumnTypes} from "../types/MappedColumnTypes";
import {ColumnType} from "../types/ColumnTypes";
import {QueryRunner} from "../../query-runner/QueryRunner";
Expand Down Expand Up @@ -216,6 +217,10 @@ export abstract class AbstractSqliteDriver implements Driver {
*/
maxAliasLength?: number;

cteCapabilities: CteCapabilities = {
requiresRecursiveHint: true,
};

// -------------------------------------------------------------------------
// Protected Properties
// -------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions src/driver/sqlserver/SqlServerDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Driver} from "../Driver";
import {ConnectionIsNotSetError} from "../../error/ConnectionIsNotSetError";
import {DriverPackageNotInstalledError} from "../../error/DriverPackageNotInstalledError";
import {DriverUtils} from "../DriverUtils";
import { CteCapabilities } from "../types/CteCapabilities";
import {SqlServerQueryRunner} from "./SqlServerQueryRunner";
import {ObjectLiteral} from "../../common/ObjectLiteral";
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
Expand Down Expand Up @@ -223,6 +224,11 @@ export class SqlServerDriver implements Driver {
"datetimeoffset": { precision: 7 }
};

cteCapabilities: CteCapabilities = {
// todo: enable it for SQL Server - it's partially supported, but there are issues with generation of non-standard OUTPUT clause
writable: false,
};

/**
* Max length allowed by MSSQL Server for aliases (identifiers).
* @see https://docs.microsoft.com/en-us/sql/sql-server/maximum-capacity-specifications-for-sql-server
Expand Down
15 changes: 15 additions & 0 deletions src/driver/types/CteCapabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface CteCapabilities {
/**
* Are RETURNING clauses supported in CTEs?
*/
writable?: boolean;
/**
* Is RECURSIVE clause required for recursive CTEs?
*/
requiresRecursiveHint?: boolean;

/**
* Is MATERIALIZED clause supported?
*/
materializedHint?: boolean;
}
1 change: 1 addition & 0 deletions src/query-builder/DeleteQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class DeleteQueryBuilder<Entity> extends QueryBuilder<Entity> implements
*/
getQuery(): string {
let sql = this.createComment();
sql += this.createCteExpression();
sql += this.createDeleteExpression();
return sql.trim();
}
Expand Down
1 change: 1 addition & 0 deletions src/query-builder/InsertQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class InsertQueryBuilder<Entity> extends QueryBuilder<Entity> {
*/
getQuery(): string {
let sql = this.createComment();
sql += this.createCteExpression();
sql += this.createInsertExpression();
return sql.trim();
}
Expand Down

0 comments on commit 9045252

Please sign in to comment.