@mikro-orm/sql 7.0.0-rc.3 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AbstractSqlConnection.d.ts +5 -4
- package/AbstractSqlConnection.js +18 -5
- package/AbstractSqlDriver.d.ts +1 -1
- package/AbstractSqlDriver.js +39 -10
- package/AbstractSqlPlatform.d.ts +34 -0
- package/AbstractSqlPlatform.js +47 -3
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/README.md +5 -4
- package/SqlEntityManager.d.ts +1 -1
- package/dialects/index.d.ts +1 -0
- package/dialects/index.js +1 -0
- package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
- package/dialects/mysql/BaseMySqlPlatform.js +17 -0
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +6 -6
- package/dialects/oracledb/OracleDialect.d.ts +78 -0
- package/dialects/oracledb/OracleDialect.js +166 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
- package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
- package/dialects/oracledb/index.d.ts +2 -0
- package/dialects/oracledb/index.js +2 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.js +12 -8
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +13 -13
- package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
- package/dialects/sqlite/SqlitePlatform.js +3 -0
- package/dialects/sqlite/SqliteSchemaHelper.js +12 -8
- package/index.d.ts +1 -1
- package/index.js +0 -1
- package/package.json +3 -3
- package/plugin/index.d.ts +1 -14
- package/plugin/index.js +13 -13
- package/plugin/transformer.d.ts +6 -22
- package/plugin/transformer.js +81 -73
- package/query/ArrayCriteriaNode.d.ts +1 -1
- package/query/CriteriaNodeFactory.js +15 -3
- package/query/NativeQueryBuilder.d.ts +3 -3
- package/query/NativeQueryBuilder.js +4 -2
- package/query/ObjectCriteriaNode.js +4 -4
- package/query/QueryBuilder.d.ts +58 -62
- package/query/QueryBuilder.js +377 -370
- package/query/QueryBuilderHelper.d.ts +14 -11
- package/query/QueryBuilderHelper.js +324 -137
- package/query/ScalarCriteriaNode.js +3 -1
- package/query/enums.d.ts +2 -0
- package/query/enums.js +2 -0
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +50 -33
- package/schema/DatabaseTable.d.ts +8 -6
- package/schema/DatabaseTable.js +84 -60
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +22 -20
- package/schema/SchemaHelper.d.ts +2 -13
- package/schema/SchemaHelper.js +2 -1
- package/schema/SqlSchemaGenerator.d.ts +4 -14
- package/schema/SqlSchemaGenerator.js +15 -7
- package/typings.d.ts +4 -1
- package/tsconfig.build.tsbuildinfo +0 -1
|
@@ -6,6 +6,12 @@ import { MySqlNativeQueryBuilder } from './MySqlNativeQueryBuilder.js';
|
|
|
6
6
|
export class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
7
7
|
schemaHelper = new MySqlSchemaHelper(this);
|
|
8
8
|
exceptionConverter = new MySqlExceptionConverter();
|
|
9
|
+
#jsonTypeCasts = {
|
|
10
|
+
string: 'text',
|
|
11
|
+
number: 'double',
|
|
12
|
+
bigint: 'bigint',
|
|
13
|
+
boolean: 'unsigned',
|
|
14
|
+
};
|
|
9
15
|
ORDER_BY_NULLS_TRANSLATE = {
|
|
10
16
|
[QueryOrder.asc_nulls_first]: 'is not null',
|
|
11
17
|
[QueryOrder.asc_nulls_last]: 'is null',
|
|
@@ -114,6 +120,17 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
|
|
|
114
120
|
ret.push(`${col} ${dir.replace(/(\s|nulls|first|last)*/gi, '')}`);
|
|
115
121
|
return ret;
|
|
116
122
|
}
|
|
123
|
+
getJsonArrayFromSQL(column, alias, properties) {
|
|
124
|
+
const columns = properties
|
|
125
|
+
.map(p => `${this.quoteIdentifier(p.name)} ${this.#jsonTypeCasts[p.type] ?? 'text'} path '$.${this.quoteJsonKey(p.name)}'`)
|
|
126
|
+
.join(', ');
|
|
127
|
+
return `json_table(${column}, '$[*]' columns (${columns})) as ${this.quoteIdentifier(alias)}`;
|
|
128
|
+
}
|
|
129
|
+
// MySQL does not support correlated json_table inside EXISTS subqueries,
|
|
130
|
+
// so we use a semi-join via the comma-join pattern instead.
|
|
131
|
+
getJsonArrayExistsSQL(from, where) {
|
|
132
|
+
return `(select 1 from ${from} where ${where} limit 1) is not null`;
|
|
133
|
+
}
|
|
117
134
|
getDefaultClientUrl() {
|
|
118
135
|
return 'mysql://root@127.0.0.1:3306';
|
|
119
136
|
}
|
|
@@ -5,7 +5,7 @@ import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
|
|
5
5
|
import type { DatabaseSchema } from '../../schema/DatabaseSchema.js';
|
|
6
6
|
import type { DatabaseTable } from '../../schema/DatabaseTable.js';
|
|
7
7
|
export declare class MySqlSchemaHelper extends SchemaHelper {
|
|
8
|
-
private
|
|
8
|
+
#private;
|
|
9
9
|
static readonly DEFAULT_VALUES: {
|
|
10
10
|
'now()': string[];
|
|
11
11
|
'current_timestamp(?)': string[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EnumType, StringType, TextType } from '@mikro-orm/core';
|
|
2
2
|
import { SchemaHelper } from '../../schema/SchemaHelper.js';
|
|
3
3
|
export class MySqlSchemaHelper extends SchemaHelper {
|
|
4
|
-
|
|
4
|
+
#cache = {};
|
|
5
5
|
static DEFAULT_VALUES = {
|
|
6
6
|
'now()': ['now()', 'current_timestamp'],
|
|
7
7
|
'current_timestamp(?)': ['current_timestamp(?)'],
|
|
@@ -46,7 +46,7 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
46
46
|
const createView = await connection.execute(`show create view \`${view.view_name}\``);
|
|
47
47
|
if (createView[0]?.['Create View']) {
|
|
48
48
|
// Extract SELECT statement from CREATE VIEW ... AS SELECT ...
|
|
49
|
-
const match = createView[0]['Create View']
|
|
49
|
+
const match = /\bAS\s+(.+)$/is.exec(createView[0]['Create View']);
|
|
50
50
|
definition = match?.[1]?.trim();
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -339,17 +339,17 @@ export class MySqlSchemaHelper extends SchemaHelper {
|
|
|
339
339
|
o[item.table_name][item.column_name] = item.column_type
|
|
340
340
|
.match(/enum\((.*)\)/)[1]
|
|
341
341
|
.split(',')
|
|
342
|
-
.map((item) =>
|
|
342
|
+
.map((item) => /'(.*)'/.exec(item)[1]);
|
|
343
343
|
return o;
|
|
344
344
|
}, {});
|
|
345
345
|
}
|
|
346
346
|
async supportsCheckConstraints(connection) {
|
|
347
|
-
if (this.
|
|
348
|
-
return this.
|
|
347
|
+
if (this.#cache.supportsCheckConstraints != null) {
|
|
348
|
+
return this.#cache.supportsCheckConstraints;
|
|
349
349
|
}
|
|
350
350
|
const sql = `select 1 from information_schema.tables where table_name = 'CHECK_CONSTRAINTS' and table_schema = 'information_schema'`;
|
|
351
351
|
const res = await connection.execute(sql);
|
|
352
|
-
return (this.
|
|
352
|
+
return (this.#cache.supportsCheckConstraints = res.length > 0);
|
|
353
353
|
}
|
|
354
354
|
getChecksSQL(tables) {
|
|
355
355
|
return `select cc.constraint_schema as table_schema, tc.table_name as table_name, cc.constraint_name as name, cc.check_clause as expression
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type AliasNode, CompiledQuery, type DatabaseConnection, type DatabaseIntrospector, DefaultQueryCompiler, type Dialect, DialectAdapterBase, type Driver, type Kysely, type QueryCompiler, type QueryResult, type TransactionSettings } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Subset of oracledb's Pool interface used by the dialect.
|
|
4
|
+
* We define our own interface to avoid importing the `oracledb` package directly.
|
|
5
|
+
*/
|
|
6
|
+
export interface OraclePool {
|
|
7
|
+
getConnection(): Promise<OraclePoolConnection>;
|
|
8
|
+
close(drainTime?: number): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Subset of oracledb's Connection interface used by the dialect.
|
|
12
|
+
*/
|
|
13
|
+
export interface OraclePoolConnection {
|
|
14
|
+
execute<R>(sql: string, params: unknown[], options?: Record<string, unknown>): Promise<{
|
|
15
|
+
rows?: R[];
|
|
16
|
+
rowsAffected?: number;
|
|
17
|
+
resultSet?: OracleResultSet<R>;
|
|
18
|
+
outBinds?: unknown;
|
|
19
|
+
}>;
|
|
20
|
+
commit(): Promise<void>;
|
|
21
|
+
rollback(): Promise<void>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
interface OracleResultSet<R> {
|
|
25
|
+
getRow(): Promise<R>;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
declare class OracleQueryCompiler extends DefaultQueryCompiler {
|
|
29
|
+
protected getLeftIdentifierWrapper(): string;
|
|
30
|
+
protected getRightIdentifierWrapper(): string;
|
|
31
|
+
protected visitAlias(node: AliasNode): void;
|
|
32
|
+
}
|
|
33
|
+
declare class OracleAdapter extends DialectAdapterBase {
|
|
34
|
+
#private;
|
|
35
|
+
get supportsReturning(): boolean;
|
|
36
|
+
get supportsTransactionalDdl(): boolean;
|
|
37
|
+
acquireMigrationLock(_: Kysely<any>): Promise<void>;
|
|
38
|
+
releaseMigrationLock(_: Kysely<any>): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
declare class OracleConnection implements DatabaseConnection {
|
|
41
|
+
#private;
|
|
42
|
+
readonly id: number;
|
|
43
|
+
constructor(connection: OraclePoolConnection, executeOptions?: Record<string, unknown>);
|
|
44
|
+
executeQuery<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>>;
|
|
45
|
+
formatQuery(query: CompiledQuery): {
|
|
46
|
+
sql: string;
|
|
47
|
+
bindParams: unknown[];
|
|
48
|
+
};
|
|
49
|
+
streamQuery<R>(compiledQuery: CompiledQuery, _chunkSize?: number): AsyncIterableIterator<QueryResult<R>>;
|
|
50
|
+
get connection(): OraclePoolConnection;
|
|
51
|
+
}
|
|
52
|
+
declare class OracleDriver implements Driver {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(config: OracleDialectConfig);
|
|
55
|
+
init(): Promise<void>;
|
|
56
|
+
acquireConnection(): Promise<OracleConnection>;
|
|
57
|
+
savepoint(connection: OracleConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
58
|
+
rollbackToSavepoint(connection: OracleConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
59
|
+
releaseSavepoint(connection: OracleConnection, savepointName: string, compileQuery: QueryCompiler['compileQuery']): Promise<void>;
|
|
60
|
+
beginTransaction(connection: OracleConnection, settings: TransactionSettings): Promise<void>;
|
|
61
|
+
commitTransaction(connection: OracleConnection): Promise<void>;
|
|
62
|
+
rollbackTransaction(connection: OracleConnection): Promise<void>;
|
|
63
|
+
releaseConnection(connection: OracleConnection): Promise<void>;
|
|
64
|
+
destroy(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface OracleDialectConfig {
|
|
67
|
+
pool: OraclePool;
|
|
68
|
+
executeOptions?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
export declare class OracleDialect implements Dialect {
|
|
71
|
+
#private;
|
|
72
|
+
constructor(config: OracleDialectConfig);
|
|
73
|
+
createDriver(): OracleDriver;
|
|
74
|
+
createAdapter(): OracleAdapter;
|
|
75
|
+
createIntrospector(db: Kysely<any>): DatabaseIntrospector;
|
|
76
|
+
createQueryCompiler(): OracleQueryCompiler;
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// inlined https://github.com/griffiths-waite/kysely-oracledb with minor adjustments
|
|
2
|
+
/* v8 ignore start: internal Kysely driver integration, tested through the main Oracle driver */
|
|
3
|
+
import { CompiledQuery, createQueryId, DefaultQueryCompiler, DialectAdapterBase, IdentifierNode, RawNode, } from 'kysely';
|
|
4
|
+
function parseSavepointCommand(command, savepointName) {
|
|
5
|
+
return RawNode.createWithChildren([
|
|
6
|
+
RawNode.createWithSql(`${command} `),
|
|
7
|
+
IdentifierNode.create(savepointName), // ensures savepointName gets sanitized
|
|
8
|
+
]);
|
|
9
|
+
}
|
|
10
|
+
class OracleQueryCompiler extends DefaultQueryCompiler {
|
|
11
|
+
getLeftIdentifierWrapper() {
|
|
12
|
+
return '';
|
|
13
|
+
}
|
|
14
|
+
getRightIdentifierWrapper() {
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
visitAlias(node) {
|
|
18
|
+
this.visitNode(node.node);
|
|
19
|
+
this.append(' ');
|
|
20
|
+
this.visitNode(node.alias);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class OracleAdapter extends DialectAdapterBase {
|
|
24
|
+
#supportsReturning = false;
|
|
25
|
+
#supportsTransactionalDdl = false;
|
|
26
|
+
get supportsReturning() {
|
|
27
|
+
return this.#supportsReturning;
|
|
28
|
+
}
|
|
29
|
+
get supportsTransactionalDdl() {
|
|
30
|
+
return this.#supportsTransactionalDdl;
|
|
31
|
+
}
|
|
32
|
+
async acquireMigrationLock(_) {
|
|
33
|
+
throw new Error('Not implemented');
|
|
34
|
+
}
|
|
35
|
+
async releaseMigrationLock(_) {
|
|
36
|
+
throw new Error('Not implemented');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const OUT_FORMAT_OBJECT = 4002;
|
|
40
|
+
let i = 0;
|
|
41
|
+
class OracleConnection {
|
|
42
|
+
id = i++;
|
|
43
|
+
#executeOptions;
|
|
44
|
+
#connection;
|
|
45
|
+
constructor(connection, executeOptions) {
|
|
46
|
+
this.#executeOptions = executeOptions ?? {};
|
|
47
|
+
this.#connection = connection;
|
|
48
|
+
}
|
|
49
|
+
async executeQuery(compiledQuery) {
|
|
50
|
+
const { sql, bindParams } = this.formatQuery(compiledQuery);
|
|
51
|
+
const result = await this.#connection.execute(sql, bindParams, {
|
|
52
|
+
autoCommit: compiledQuery.autoCommit,
|
|
53
|
+
outFormat: OUT_FORMAT_OBJECT,
|
|
54
|
+
...this.#executeOptions,
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
rows: result?.rows || [],
|
|
58
|
+
numAffectedRows: result.rowsAffected ? BigInt(result.rowsAffected) : undefined,
|
|
59
|
+
// @ts-ignore internal extension for Oracle returning clause
|
|
60
|
+
outBinds: result.outBinds,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
formatQuery(query) {
|
|
64
|
+
return {
|
|
65
|
+
sql: query.sql.replace(/\$(\d+)/g, (_match, p1) => `:${parseInt(p1, 10) - 1}`), // Format bind params in Oracle syntax :0, :1, etc.
|
|
66
|
+
bindParams: query.parameters,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async *streamQuery(compiledQuery, _chunkSize) {
|
|
70
|
+
const { sql, bindParams } = this.formatQuery(compiledQuery);
|
|
71
|
+
const result = await this.#connection.execute(sql, bindParams, {
|
|
72
|
+
resultSet: true,
|
|
73
|
+
autoCommit: compiledQuery.autoCommit,
|
|
74
|
+
outFormat: OUT_FORMAT_OBJECT,
|
|
75
|
+
...this.#executeOptions,
|
|
76
|
+
});
|
|
77
|
+
const rs = result.resultSet;
|
|
78
|
+
try {
|
|
79
|
+
let row;
|
|
80
|
+
while ((row = await rs.getRow())) {
|
|
81
|
+
yield { rows: [row] };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
await rs.close();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
get connection() {
|
|
89
|
+
return this.#connection;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
class OracleDriver {
|
|
93
|
+
#config;
|
|
94
|
+
#connections = new Set();
|
|
95
|
+
constructor(config) {
|
|
96
|
+
this.#config = config;
|
|
97
|
+
}
|
|
98
|
+
async init() {
|
|
99
|
+
//
|
|
100
|
+
}
|
|
101
|
+
async acquireConnection() {
|
|
102
|
+
const connection = new OracleConnection(await this.#config.pool.getConnection(), this.#config.executeOptions);
|
|
103
|
+
this.#connections.add(connection);
|
|
104
|
+
return connection;
|
|
105
|
+
}
|
|
106
|
+
async savepoint(connection, savepointName, compileQuery) {
|
|
107
|
+
await connection.executeQuery(compileQuery(parseSavepointCommand('savepoint', savepointName), createQueryId()));
|
|
108
|
+
}
|
|
109
|
+
async rollbackToSavepoint(connection, savepointName, compileQuery) {
|
|
110
|
+
await connection.executeQuery(compileQuery(parseSavepointCommand('rollback to savepoint', savepointName), createQueryId()));
|
|
111
|
+
}
|
|
112
|
+
async releaseSavepoint(connection, savepointName, compileQuery) {
|
|
113
|
+
//
|
|
114
|
+
}
|
|
115
|
+
async beginTransaction(connection, settings) {
|
|
116
|
+
if (settings.accessMode) {
|
|
117
|
+
await connection.executeQuery(CompiledQuery.raw(`set transaction ${settings.accessMode}`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (settings.isolationLevel) {
|
|
121
|
+
await connection.executeQuery(CompiledQuery.raw(`set transaction isolation level ${settings.isolationLevel}`));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async commitTransaction(connection) {
|
|
125
|
+
await connection.connection.commit();
|
|
126
|
+
}
|
|
127
|
+
async rollbackTransaction(connection) {
|
|
128
|
+
await connection.connection.rollback();
|
|
129
|
+
}
|
|
130
|
+
async releaseConnection(connection) {
|
|
131
|
+
try {
|
|
132
|
+
await connection.connection.close();
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
//
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.#connections.delete(connection);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async destroy() {
|
|
142
|
+
for (const connection of this.#connections) {
|
|
143
|
+
await this.releaseConnection(connection);
|
|
144
|
+
}
|
|
145
|
+
await this.#config.pool?.close(0);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
export class OracleDialect {
|
|
149
|
+
#config;
|
|
150
|
+
constructor(config) {
|
|
151
|
+
this.#config = config;
|
|
152
|
+
}
|
|
153
|
+
createDriver() {
|
|
154
|
+
return new OracleDriver(this.#config);
|
|
155
|
+
}
|
|
156
|
+
createAdapter() {
|
|
157
|
+
return new OracleAdapter();
|
|
158
|
+
}
|
|
159
|
+
createIntrospector(db) {
|
|
160
|
+
throw new Error('Not implemented');
|
|
161
|
+
}
|
|
162
|
+
createQueryCompiler() {
|
|
163
|
+
return new OracleQueryCompiler();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/* v8 ignore stop */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Dictionary } from '@mikro-orm/core';
|
|
2
|
+
import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
|
|
3
|
+
/** @internal */
|
|
4
|
+
export declare function markOutBindings(obj: Dictionary): void;
|
|
5
|
+
/** @internal */
|
|
6
|
+
export declare class OracleNativeQueryBuilder extends NativeQueryBuilder {
|
|
7
|
+
as(alias: string): this;
|
|
8
|
+
compile(): {
|
|
9
|
+
sql: string;
|
|
10
|
+
params: unknown[];
|
|
11
|
+
};
|
|
12
|
+
protected compileTruncate(): void;
|
|
13
|
+
protected combineParts(): {
|
|
14
|
+
sql: string;
|
|
15
|
+
params: unknown[];
|
|
16
|
+
};
|
|
17
|
+
private compileUpsert;
|
|
18
|
+
protected compileSelect(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { raw, RawQueryFragment, Utils } from '@mikro-orm/core';
|
|
2
|
+
import { QueryType } from '../../query/enums.js';
|
|
3
|
+
import { NativeQueryBuilder } from '../../query/NativeQueryBuilder.js';
|
|
4
|
+
/** @internal */
|
|
5
|
+
export function markOutBindings(obj) {
|
|
6
|
+
Object.defineProperty(obj, '__outBindings', {
|
|
7
|
+
value: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
enumerable: false,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** @internal */
|
|
14
|
+
export class OracleNativeQueryBuilder extends NativeQueryBuilder {
|
|
15
|
+
as(alias) {
|
|
16
|
+
this.wrap('(', `) ${this.platform.quoteIdentifier(alias)}`);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
compile() {
|
|
20
|
+
if (!this.type) {
|
|
21
|
+
throw new Error('No query type provided');
|
|
22
|
+
}
|
|
23
|
+
this.parts.length = 0;
|
|
24
|
+
this.params.length = 0;
|
|
25
|
+
/* v8 ignore next 3: query comment branch */
|
|
26
|
+
if (this.options.comment) {
|
|
27
|
+
this.parts.push(...this.options.comment.map(comment => `/* ${comment} */`));
|
|
28
|
+
}
|
|
29
|
+
let copy;
|
|
30
|
+
if (this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0])) {
|
|
31
|
+
this.compileUpsert();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (this.options.returning && Array.isArray(this.options.data) && this.options.data.length > 1) {
|
|
35
|
+
copy = [...this.options.data];
|
|
36
|
+
this.options.data.length = 1;
|
|
37
|
+
}
|
|
38
|
+
switch (this.type) {
|
|
39
|
+
case QueryType.SELECT:
|
|
40
|
+
case QueryType.COUNT:
|
|
41
|
+
this.compileSelect();
|
|
42
|
+
break;
|
|
43
|
+
case QueryType.INSERT:
|
|
44
|
+
this.compileInsert();
|
|
45
|
+
break;
|
|
46
|
+
case QueryType.UPDATE:
|
|
47
|
+
this.compileUpdate();
|
|
48
|
+
break;
|
|
49
|
+
case QueryType.DELETE:
|
|
50
|
+
this.compileDelete();
|
|
51
|
+
break;
|
|
52
|
+
case QueryType.TRUNCATE:
|
|
53
|
+
this.compileTruncate();
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
this.addOnConflictClause();
|
|
57
|
+
}
|
|
58
|
+
if (this.options.returning) {
|
|
59
|
+
const isUpsert = this.options.onConflict && !Utils.isEmpty(Utils.asArray(this.options.data)[0]);
|
|
60
|
+
const prefix = isUpsert ? `${this.getTableName()}.` : '';
|
|
61
|
+
const fields = this.options.returning.map(field => prefix + this.quote(Array.isArray(field) ? field[0] : field));
|
|
62
|
+
const into = this.options.returning.map(field => ':out_' + (Array.isArray(field) ? field[0] : field));
|
|
63
|
+
const outBindings = this.options.returning.map(field => {
|
|
64
|
+
const name = 'out_' + (Array.isArray(field) ? field[0] : field);
|
|
65
|
+
const type = Array.isArray(field) ? field[1] : 'string';
|
|
66
|
+
return [name, type];
|
|
67
|
+
});
|
|
68
|
+
markOutBindings(outBindings);
|
|
69
|
+
this.parts.push(`returning ${fields.join(', ')}`);
|
|
70
|
+
this.parts.push(`into ${into.join(', ')}`);
|
|
71
|
+
this.params.push(outBindings);
|
|
72
|
+
}
|
|
73
|
+
this.addLockClause();
|
|
74
|
+
if (!copy) {
|
|
75
|
+
return this.combineParts();
|
|
76
|
+
}
|
|
77
|
+
// multi insert with returning
|
|
78
|
+
const sql = this.parts.join(' ');
|
|
79
|
+
const blockLines = [];
|
|
80
|
+
const block2Lines = [];
|
|
81
|
+
const keys = Object.keys(copy[0]);
|
|
82
|
+
const last = this.params[this.params.length - 1];
|
|
83
|
+
/* v8 ignore next 3: defensive check — output bindings are always set by compile() */
|
|
84
|
+
if (!Array.isArray(last) || !('__outBindings' in last) || !last.__outBindings) {
|
|
85
|
+
throw new Error('Output bindings are required for multi insert with returning');
|
|
86
|
+
}
|
|
87
|
+
const outBindings = {};
|
|
88
|
+
markOutBindings(outBindings);
|
|
89
|
+
for (let i = 0; i < copy.length; i++) {
|
|
90
|
+
const params = [];
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
/* v8 ignore next 3: undefined value branch in multi-insert */
|
|
93
|
+
if (typeof copy[i][key] === 'undefined') {
|
|
94
|
+
params.push(this.platform.usesDefaultKeyword() ? raw('default') : null);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
params.push(copy[i][key]);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// we need to interpolate to allow proper escaping
|
|
101
|
+
const formatted = this.platform.formatQuery(sql, params).replaceAll(`'`, `''`);
|
|
102
|
+
/* v8 ignore next 3: returning field type branches */
|
|
103
|
+
const using = this.options.returning.map(field => {
|
|
104
|
+
const name = Array.isArray(field) ? field[0] : field;
|
|
105
|
+
const type = Array.isArray(field) ? field[1] : 'string';
|
|
106
|
+
outBindings[`out_${name}__${i}`] = {
|
|
107
|
+
dir: this.platform.mapToBindType('out'),
|
|
108
|
+
type: this.platform.mapToBindType(type),
|
|
109
|
+
};
|
|
110
|
+
return `out :out_${name}__${i}`;
|
|
111
|
+
});
|
|
112
|
+
blockLines.push(` execute immediate '${formatted}' using ${using.join(', ')};`);
|
|
113
|
+
block2Lines.push(` execute immediate '${sql}' using ${using.join(', ')};`);
|
|
114
|
+
}
|
|
115
|
+
const block = `begin\n${blockLines.join('\n')}\n end;`;
|
|
116
|
+
const block2 = `begin\n${block2Lines.join('\n')}\n end;`;
|
|
117
|
+
// save raw query without interpolation for logging,
|
|
118
|
+
Object.defineProperty(outBindings, '__rawQuery', {
|
|
119
|
+
value: block2,
|
|
120
|
+
writable: true,
|
|
121
|
+
configurable: true,
|
|
122
|
+
enumerable: false,
|
|
123
|
+
});
|
|
124
|
+
this.options.data = copy;
|
|
125
|
+
return { sql: block, params: [outBindings] };
|
|
126
|
+
}
|
|
127
|
+
compileTruncate() {
|
|
128
|
+
super.compileTruncate();
|
|
129
|
+
this.parts.push('drop all storage cascade');
|
|
130
|
+
}
|
|
131
|
+
combineParts() {
|
|
132
|
+
let sql = this.parts.join(' ');
|
|
133
|
+
const last = this.params[this.params.length - 1];
|
|
134
|
+
if (this.options.wrap) {
|
|
135
|
+
const [a, b] = this.options.wrap;
|
|
136
|
+
sql = `${a}${sql}${b}`;
|
|
137
|
+
}
|
|
138
|
+
if (!(Array.isArray(last) && '__outBindings' in last && last.__outBindings)) {
|
|
139
|
+
return { sql, params: this.params };
|
|
140
|
+
}
|
|
141
|
+
const out = this.params.pop();
|
|
142
|
+
const outBindings = {};
|
|
143
|
+
markOutBindings(outBindings);
|
|
144
|
+
this.params.push(outBindings);
|
|
145
|
+
for (const item of out) {
|
|
146
|
+
outBindings[item[0]] = {
|
|
147
|
+
dir: this.platform.mapToBindType('out'),
|
|
148
|
+
type: this.platform.mapToBindType(item[1]),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return { sql, params: this.params };
|
|
152
|
+
}
|
|
153
|
+
compileUpsert() {
|
|
154
|
+
const clause = this.options.onConflict;
|
|
155
|
+
const dataAsArray = Utils.asArray(this.options.data);
|
|
156
|
+
const keys = Object.keys(dataAsArray[0]);
|
|
157
|
+
const parts = [];
|
|
158
|
+
for (const data of dataAsArray) {
|
|
159
|
+
for (const key of keys) {
|
|
160
|
+
this.params.push(data[key]);
|
|
161
|
+
}
|
|
162
|
+
parts.push(`select ${keys.map(k => `? as ${this.quote(k)}`).join(', ')} from dual`);
|
|
163
|
+
}
|
|
164
|
+
this.parts.push(`merge into ${this.getTableName()}`);
|
|
165
|
+
this.parts.push(`using (${parts.join(' union all ')}) tsource`);
|
|
166
|
+
/* v8 ignore next 4: RawQueryFragment conflict fields branch */
|
|
167
|
+
if (clause.fields instanceof RawQueryFragment) {
|
|
168
|
+
this.parts.push(clause.fields.sql);
|
|
169
|
+
this.params.push(...clause.fields.params);
|
|
170
|
+
}
|
|
171
|
+
else if (clause.fields.length > 0) {
|
|
172
|
+
const fields = clause.fields.map(field => {
|
|
173
|
+
const col = this.quote(field);
|
|
174
|
+
return `${this.getTableName()}.${col} = tsource.${col}`;
|
|
175
|
+
});
|
|
176
|
+
this.parts.push(`on (${fields.join(' and ')})`);
|
|
177
|
+
}
|
|
178
|
+
const sourceColumns = keys.map(field => `tsource.${this.quote(field)}`).join(', ');
|
|
179
|
+
const destinationColumns = keys.map(field => this.quote(field)).join(', ');
|
|
180
|
+
this.parts.push(`when not matched then insert (${destinationColumns}) values (${sourceColumns})`);
|
|
181
|
+
if (!clause.ignore) {
|
|
182
|
+
/* v8 ignore next: merge type branch */
|
|
183
|
+
if (!clause.merge || Array.isArray(clause.merge)) {
|
|
184
|
+
const mergeParts = (clause.merge || keys)
|
|
185
|
+
.filter(field => !Array.isArray(clause.fields) || !clause.fields.includes(field))
|
|
186
|
+
.filter((field) => keys.includes(field)) // only reference columns present in the source data
|
|
187
|
+
.map((column) => `${this.quote(column)} = tsource.${this.quote(column)}`);
|
|
188
|
+
/* v8 ignore next 10: empty mergeParts branch */
|
|
189
|
+
if (mergeParts.length > 0) {
|
|
190
|
+
this.parts.push('when matched');
|
|
191
|
+
if (clause.where) {
|
|
192
|
+
this.parts.push(`and ${clause.where.sql}`);
|
|
193
|
+
this.params.push(...clause.where.params);
|
|
194
|
+
}
|
|
195
|
+
this.parts.push('then update set');
|
|
196
|
+
this.parts.push(mergeParts.join(', '));
|
|
197
|
+
}
|
|
198
|
+
} /* v8 ignore start: object-form merge branch */
|
|
199
|
+
else if (typeof clause.merge === 'object') {
|
|
200
|
+
this.parts.push('when matched');
|
|
201
|
+
if (clause.where) {
|
|
202
|
+
this.parts.push(`and ${clause.where.sql}`);
|
|
203
|
+
this.params.push(...clause.where.params);
|
|
204
|
+
}
|
|
205
|
+
this.parts.push('then update set');
|
|
206
|
+
const parts = Object.entries(clause.merge).map(([key, value]) => {
|
|
207
|
+
this.params.push(value);
|
|
208
|
+
return `${this.getTableName()}.${this.quote(key)} = ?`;
|
|
209
|
+
});
|
|
210
|
+
this.parts.push(parts.join(', '));
|
|
211
|
+
}
|
|
212
|
+
/* v8 ignore stop */
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
compileSelect() {
|
|
216
|
+
this.parts.push('select');
|
|
217
|
+
this.addHintComment();
|
|
218
|
+
this.parts.push(`${this.getFields()} from ${this.getTableName()}`);
|
|
219
|
+
if (this.options.joins) {
|
|
220
|
+
for (const join of this.options.joins) {
|
|
221
|
+
this.parts.push(join.sql);
|
|
222
|
+
this.params.push(...join.params);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (this.options.where?.sql.trim()) {
|
|
226
|
+
this.parts.push(`where ${this.options.where.sql}`);
|
|
227
|
+
this.params.push(...this.options.where.params);
|
|
228
|
+
}
|
|
229
|
+
if (this.options.groupBy) {
|
|
230
|
+
const fields = this.options.groupBy.map(field => this.quote(field));
|
|
231
|
+
this.parts.push(`group by ${fields.join(', ')}`);
|
|
232
|
+
}
|
|
233
|
+
if (this.options.having) {
|
|
234
|
+
this.parts.push(`having ${this.options.having.sql}`);
|
|
235
|
+
this.params.push(...this.options.having.params);
|
|
236
|
+
}
|
|
237
|
+
if (this.options.orderBy) {
|
|
238
|
+
this.parts.push(`order by ${this.options.orderBy}`);
|
|
239
|
+
}
|
|
240
|
+
if (this.options.offset != null) {
|
|
241
|
+
this.parts.push(`offset ? rows`);
|
|
242
|
+
this.params.push(this.options.offset);
|
|
243
|
+
}
|
|
244
|
+
if (this.options.limit != null) {
|
|
245
|
+
this.parts.push(`fetch next ? rows only`);
|
|
246
|
+
this.params.push(this.options.limit);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -5,6 +5,7 @@ import { PostgreSqlNativeQueryBuilder } from './PostgreSqlNativeQueryBuilder.js'
|
|
|
5
5
|
import { PostgreSqlSchemaHelper } from './PostgreSqlSchemaHelper.js';
|
|
6
6
|
import { PostgreSqlExceptionConverter } from './PostgreSqlExceptionConverter.js';
|
|
7
7
|
export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
8
|
+
#private;
|
|
8
9
|
protected readonly schemaHelper: PostgreSqlSchemaHelper;
|
|
9
10
|
protected readonly exceptionConverter: PostgreSqlExceptionConverter;
|
|
10
11
|
createNativeQueryBuilder(): PostgreSqlNativeQueryBuilder;
|
|
@@ -102,5 +103,10 @@ export declare class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
102
103
|
castColumn(prop?: {
|
|
103
104
|
columnTypes?: string[];
|
|
104
105
|
}): string;
|
|
106
|
+
getJsonArrayFromSQL(column: string, alias: string, _properties: {
|
|
107
|
+
name: string;
|
|
108
|
+
type: string;
|
|
109
|
+
}[]): string;
|
|
110
|
+
getJsonArrayElementPropertySQL(alias: string, property: string, type: string): string;
|
|
105
111
|
getDefaultClientUrl(): string;
|
|
106
112
|
}
|
|
@@ -7,6 +7,8 @@ import { FullTextType } from './FullTextType.js';
|
|
|
7
7
|
export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
8
8
|
schemaHelper = new PostgreSqlSchemaHelper(this);
|
|
9
9
|
exceptionConverter = new PostgreSqlExceptionConverter();
|
|
10
|
+
/** Maps JS runtime type names to PostgreSQL cast types for JSON property access. @internal */
|
|
11
|
+
#jsonTypeCasts = { number: 'float8', bigint: 'int8', boolean: 'bool' };
|
|
10
12
|
createNativeQueryBuilder() {
|
|
11
13
|
return new PostgreSqlNativeQueryBuilder(this);
|
|
12
14
|
}
|
|
@@ -168,7 +170,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
168
170
|
return ['begin'];
|
|
169
171
|
}
|
|
170
172
|
marshallArray(values) {
|
|
171
|
-
const quote = (v) => (v === '' ||
|
|
173
|
+
const quote = (v) => (v === '' || /["{},\\]/.exec(v) ? JSON.stringify(v) : v);
|
|
172
174
|
return `{${values.map(v => quote('' + v)).join(',')}}`;
|
|
173
175
|
}
|
|
174
176
|
/* v8 ignore next */
|
|
@@ -183,7 +185,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
183
185
|
if (v === `""`) {
|
|
184
186
|
return '';
|
|
185
187
|
}
|
|
186
|
-
if (
|
|
188
|
+
if (/"(.*)"/.exec(v)) {
|
|
187
189
|
return v.substring(1, v.length - 1).replaceAll('\\"', '"');
|
|
188
190
|
}
|
|
189
191
|
return v;
|
|
@@ -215,12 +217,7 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
215
217
|
const last = path.pop();
|
|
216
218
|
const root = this.quoteIdentifier(aliased ? `${ALIAS_REPLACEMENT}.${first}` : first);
|
|
217
219
|
type = typeof type === 'string' ? this.getMappedType(type).runtimeType : String(type);
|
|
218
|
-
const
|
|
219
|
-
number: 'float8',
|
|
220
|
-
bigint: 'int8',
|
|
221
|
-
boolean: 'bool',
|
|
222
|
-
};
|
|
223
|
-
const cast = (key) => raw(type in types ? `(${key})::${types[type]}` : key);
|
|
220
|
+
const cast = (key) => raw(type in this.#jsonTypeCasts ? `(${key})::${this.#jsonTypeCasts[type]}` : key);
|
|
224
221
|
let lastOperator = '->>';
|
|
225
222
|
// force `->` for operator payloads with array values
|
|
226
223
|
if (Utils.isPlainObject(value) &&
|
|
@@ -352,6 +349,13 @@ export class BasePostgreSqlPlatform extends AbstractSqlPlatform {
|
|
|
352
349
|
return '';
|
|
353
350
|
}
|
|
354
351
|
}
|
|
352
|
+
getJsonArrayFromSQL(column, alias, _properties) {
|
|
353
|
+
return `jsonb_array_elements(${column}) as ${this.quoteIdentifier(alias)}`;
|
|
354
|
+
}
|
|
355
|
+
getJsonArrayElementPropertySQL(alias, property, type) {
|
|
356
|
+
const expr = `${this.quoteIdentifier(alias)}->>${this.quoteValue(property)}`;
|
|
357
|
+
return type in this.#jsonTypeCasts ? `(${expr})::${this.#jsonTypeCasts[type]}` : expr;
|
|
358
|
+
}
|
|
355
359
|
getDefaultClientUrl() {
|
|
356
360
|
return 'postgresql://postgres@127.0.0.1:5432';
|
|
357
361
|
}
|