@carno.js/orm 1.0.6 → 1.0.8
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/dist/SqlBuilder.d.ts +2 -2
- package/dist/SqlBuilder.js +38 -28
- package/dist/domain/base-entity.d.ts +5 -0
- package/dist/domain/base-entity.js +18 -1
- package/dist/domain/entities.d.ts +8 -0
- package/dist/domain/entities.js +24 -0
- package/dist/driver/bun-driver.base.d.ts +3 -2
- package/dist/driver/bun-driver.base.js +16 -3
- package/dist/driver/bun-mysql.driver.d.ts +2 -2
- package/dist/driver/bun-mysql.driver.js +41 -23
- package/dist/driver/bun-pg.driver.d.ts +2 -2
- package/dist/driver/bun-pg.driver.js +1 -1
- package/dist/driver/driver-factory.d.ts +12 -0
- package/dist/driver/driver-factory.js +32 -0
- package/dist/driver/driver.interface.d.ts +2 -0
- package/dist/identity-map/entity-key-generator.js +1 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/query/model-transformer.d.ts +1 -0
- package/dist/query/model-transformer.js +21 -7
- package/dist/query/sql-column-manager.d.ts +4 -2
- package/dist/query/sql-column-manager.js +20 -7
- package/dist/query/sql-condition-builder.d.ts +4 -2
- package/dist/query/sql-condition-builder.js +13 -2
- package/dist/query/sql-join-manager.d.ts +4 -0
- package/dist/query/sql-join-manager.js +67 -12
- package/dist/query/sql-subquery-builder.d.ts +5 -2
- package/dist/query/sql-subquery-builder.js +20 -6
- package/dist/testing/with-database.d.ts +6 -4
- package/dist/testing/with-database.js +122 -20
- package/dist/utils/value-processor.d.ts +5 -0
- package/dist/utils/value-processor.js +12 -4
- package/package.json +2 -2
|
@@ -21,16 +21,7 @@ class EntityKeyGenerator {
|
|
|
21
21
|
}
|
|
22
22
|
getPrimaryKeyName(entityClass) {
|
|
23
23
|
const options = this.entityStorage.get(entityClass);
|
|
24
|
-
|
|
25
|
-
return 'id';
|
|
26
|
-
}
|
|
27
|
-
for (const prop in options.properties) {
|
|
28
|
-
const property = options.properties[prop];
|
|
29
|
-
if (property.options.isPrimary) {
|
|
30
|
-
return prop;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return 'id';
|
|
24
|
+
return options?._primaryKeyPropertyName || 'id';
|
|
34
25
|
}
|
|
35
26
|
serializePrimaryKey(pk) {
|
|
36
27
|
if (Array.isArray(pk)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { EntityStorage } from './domain/entities';
|
|
|
16
16
|
export * from './driver/bun-pg.driver';
|
|
17
17
|
export * from './driver/bun-mysql.driver';
|
|
18
18
|
export * from './driver/bun-driver.base';
|
|
19
|
+
export * from './driver/driver-factory';
|
|
19
20
|
export * from './utils';
|
|
20
21
|
export * from './driver/driver.interface';
|
|
21
22
|
export * from './entry';
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ Object.defineProperty(exports, "EntityStorage", { enumerable: true, get: functio
|
|
|
33
33
|
__exportStar(require("./driver/bun-pg.driver"), exports);
|
|
34
34
|
__exportStar(require("./driver/bun-mysql.driver"), exports);
|
|
35
35
|
__exportStar(require("./driver/bun-driver.base"), exports);
|
|
36
|
+
__exportStar(require("./driver/driver-factory"), exports);
|
|
36
37
|
__exportStar(require("./utils"), exports);
|
|
37
38
|
__exportStar(require("./driver/driver.interface"), exports);
|
|
38
39
|
__exportStar(require("./entry"), exports);
|
|
@@ -100,12 +100,8 @@ class ModelTransformer {
|
|
|
100
100
|
return data[pkKey];
|
|
101
101
|
}
|
|
102
102
|
findPrimaryKeyProperty(options) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return options.properties[prop];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return null;
|
|
103
|
+
const pkPropertyName = options._primaryKeyPropertyName || 'id';
|
|
104
|
+
return options.properties[pkPropertyName] || null;
|
|
109
105
|
}
|
|
110
106
|
buildOptionsMap(instanceMap) {
|
|
111
107
|
const optionsMap = new Map();
|
|
@@ -148,7 +144,25 @@ class ModelTransformer {
|
|
|
148
144
|
entity[key] = new property.type(value);
|
|
149
145
|
return;
|
|
150
146
|
}
|
|
151
|
-
entity[key] = value;
|
|
147
|
+
entity[key] = this.normalizePropertyValue(property, value);
|
|
148
|
+
}
|
|
149
|
+
// @todo refactor for performance
|
|
150
|
+
normalizePropertyValue(property, value) {
|
|
151
|
+
if (value === null || value === undefined) {
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
if (property?.type === Boolean) {
|
|
155
|
+
if (value === 1 || value === '1' || value === true || value === 'true') {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
if (value === 0 || value === '0' || value === false || value === 'false') {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (property?.type === Date && !(value instanceof Date)) {
|
|
163
|
+
return new Date(value);
|
|
164
|
+
}
|
|
165
|
+
return value;
|
|
152
166
|
}
|
|
153
167
|
findPropertyByColumnName(columnName, options) {
|
|
154
168
|
// First, try to find in regular properties
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Statement } from '../driver/driver.interface';
|
|
1
|
+
import { DriverInterface, Statement } from '../driver/driver.interface';
|
|
2
2
|
import { EntityStorage, Options } from '../domain/entities';
|
|
3
3
|
export declare class SqlColumnManager {
|
|
4
4
|
private entityStorage;
|
|
5
5
|
private statements;
|
|
6
6
|
private entity;
|
|
7
|
-
|
|
7
|
+
private driver;
|
|
8
|
+
constructor(entityStorage: EntityStorage, statements: Statement<any>, entity: Options, driver: DriverInterface);
|
|
9
|
+
private quoteId;
|
|
8
10
|
generateColumns(model: Function, updatedColumns: string[]): string[];
|
|
9
11
|
processUserColumns(columns: string[]): string[];
|
|
10
12
|
getColumnsForEntity(entity: Function, alias: string): string[];
|
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SqlColumnManager = void 0;
|
|
4
4
|
class SqlColumnManager {
|
|
5
|
-
constructor(entityStorage, statements, entity) {
|
|
5
|
+
constructor(entityStorage, statements, entity, driver) {
|
|
6
6
|
this.entityStorage = entityStorage;
|
|
7
7
|
this.statements = statements;
|
|
8
8
|
this.entity = entity;
|
|
9
|
+
this.driver = driver;
|
|
10
|
+
}
|
|
11
|
+
quoteId(identifier) {
|
|
12
|
+
const q = this.driver.getIdentifierQuote();
|
|
13
|
+
return `${q}${identifier}${q}`;
|
|
9
14
|
}
|
|
10
15
|
generateColumns(model, updatedColumns) {
|
|
11
16
|
const baseColumns = this.getColumnsForEntity(model, this.statements.alias);
|
|
@@ -41,7 +46,9 @@ class SqlColumnManager {
|
|
|
41
46
|
getPropertyColumns(entityOptions, alias) {
|
|
42
47
|
return Object.keys(entityOptions.properties).map(key => {
|
|
43
48
|
const columnName = entityOptions.properties[key].options.columnName;
|
|
44
|
-
|
|
49
|
+
const col = this.quoteId(columnName);
|
|
50
|
+
const aliasedCol = this.quoteId(`${alias}_${columnName}`);
|
|
51
|
+
return `${alias}.${col} as ${aliasedCol}`;
|
|
45
52
|
});
|
|
46
53
|
}
|
|
47
54
|
getRelationColumns(entityOptions, alias) {
|
|
@@ -50,7 +57,11 @@ class SqlColumnManager {
|
|
|
50
57
|
}
|
|
51
58
|
return entityOptions.relations
|
|
52
59
|
.filter(relation => relation.relation === 'many-to-one')
|
|
53
|
-
.map(relation =>
|
|
60
|
+
.map(relation => {
|
|
61
|
+
const col = this.quoteId(relation.columnName);
|
|
62
|
+
const aliasedCol = this.quoteId(`${alias}_${relation.columnName}`);
|
|
63
|
+
return `${alias}.${col} as ${aliasedCol}`;
|
|
64
|
+
});
|
|
54
65
|
}
|
|
55
66
|
extractAliases(columns) {
|
|
56
67
|
return columns
|
|
@@ -66,10 +77,11 @@ class SqlColumnManager {
|
|
|
66
77
|
}
|
|
67
78
|
buildSimpleColumnAlias(column, alias, onlyAlias) {
|
|
68
79
|
const columnName = this.getColumnNameFromProperty(column);
|
|
80
|
+
const col = this.quoteId(columnName);
|
|
69
81
|
if (onlyAlias) {
|
|
70
|
-
return `${alias}
|
|
82
|
+
return `${alias}.${col}`;
|
|
71
83
|
}
|
|
72
|
-
return `${alias}
|
|
84
|
+
return `${alias}.${col} as ${alias}_${columnName}`;
|
|
73
85
|
}
|
|
74
86
|
buildNestedColumnAlias(column, onlyAlias) {
|
|
75
87
|
this.validateJoinsExist();
|
|
@@ -122,10 +134,11 @@ class SqlColumnManager {
|
|
|
122
134
|
formatColumnWithAlias(alias, propertyName, onlyAlias) {
|
|
123
135
|
const entity = this.getEntityFromAlias(alias);
|
|
124
136
|
const columnName = this.getColumnNameFromPropertyForEntity(propertyName, entity);
|
|
137
|
+
const col = this.quoteId(columnName);
|
|
125
138
|
if (onlyAlias) {
|
|
126
|
-
return `${alias}
|
|
139
|
+
return `${alias}.${col}`;
|
|
127
140
|
}
|
|
128
|
-
return `${alias}
|
|
141
|
+
return `${alias}.${col} as ${alias}_${columnName}`;
|
|
129
142
|
}
|
|
130
143
|
getColumnNameFromProperty(propertyName) {
|
|
131
144
|
return this.getColumnNameFromPropertyForEntity(propertyName, this.entity);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilterQuery, Relationship, Statement } from '../driver/driver.interface';
|
|
1
|
+
import { DriverInterface, FilterQuery, Relationship, Statement } from '../driver/driver.interface';
|
|
2
2
|
import { EntityStorage } from '../domain/entities';
|
|
3
3
|
import { SqlSubqueryBuilder } from './sql-subquery-builder';
|
|
4
4
|
type ApplyJoinCallback = (relationship: Relationship<any>, value: FilterQuery<any>, alias: string) => string;
|
|
@@ -6,9 +6,10 @@ export declare class SqlConditionBuilder<T> {
|
|
|
6
6
|
private entityStorage;
|
|
7
7
|
private applyJoinCallback;
|
|
8
8
|
private statements;
|
|
9
|
+
private driver;
|
|
9
10
|
private lastKeyNotOperator;
|
|
10
11
|
private subqueryBuilder?;
|
|
11
|
-
constructor(entityStorage: EntityStorage, applyJoinCallback: ApplyJoinCallback, statements: Statement<T
|
|
12
|
+
constructor(entityStorage: EntityStorage, applyJoinCallback: ApplyJoinCallback, statements: Statement<T>, driver: DriverInterface);
|
|
12
13
|
setSubqueryBuilder(subqueryBuilder: SqlSubqueryBuilder): void;
|
|
13
14
|
build(condition: FilterQuery<T>, alias: string, model: Function): string;
|
|
14
15
|
private processConditions;
|
|
@@ -30,6 +31,7 @@ export declare class SqlConditionBuilder<T> {
|
|
|
30
31
|
private extractValueFromValueObject;
|
|
31
32
|
private formatValue;
|
|
32
33
|
private formatDate;
|
|
34
|
+
private formatDateForMysql;
|
|
33
35
|
private isNullish;
|
|
34
36
|
private buildNullCondition;
|
|
35
37
|
private isPrimitive;
|
|
@@ -12,10 +12,11 @@ const OPERATORS_SET = new Set([
|
|
|
12
12
|
const LOGICAL_OPERATORS_SET = new Set(['$or', '$and']);
|
|
13
13
|
const PRIMITIVES_SET = new Set(['string', 'number', 'boolean', 'bigint']);
|
|
14
14
|
class SqlConditionBuilder {
|
|
15
|
-
constructor(entityStorage, applyJoinCallback, statements) {
|
|
15
|
+
constructor(entityStorage, applyJoinCallback, statements, driver) {
|
|
16
16
|
this.entityStorage = entityStorage;
|
|
17
17
|
this.applyJoinCallback = applyJoinCallback;
|
|
18
18
|
this.statements = statements;
|
|
19
|
+
this.driver = driver;
|
|
19
20
|
this.lastKeyNotOperator = '';
|
|
20
21
|
}
|
|
21
22
|
setSubqueryBuilder(subqueryBuilder) {
|
|
@@ -178,7 +179,17 @@ class SqlConditionBuilder {
|
|
|
178
179
|
return this.formatJson(value);
|
|
179
180
|
}
|
|
180
181
|
formatDate(value) {
|
|
181
|
-
|
|
182
|
+
const formatted = this.driver.dbType === 'mysql'
|
|
183
|
+
? this.formatDateForMysql(value)
|
|
184
|
+
: value.toISOString();
|
|
185
|
+
return `'${formatted}'`;
|
|
186
|
+
}
|
|
187
|
+
formatDateForMysql(value) {
|
|
188
|
+
return value
|
|
189
|
+
.toISOString()
|
|
190
|
+
.replace('T', ' ')
|
|
191
|
+
.replace('Z', '')
|
|
192
|
+
.replace(/\.\d{3}/, '');
|
|
182
193
|
}
|
|
183
194
|
isNullish(value) {
|
|
184
195
|
return value === null || value === undefined;
|
|
@@ -17,14 +17,18 @@ export declare class SqlJoinManager<T> {
|
|
|
17
17
|
private getOriginalColumnsCallback;
|
|
18
18
|
private getAliasCallback;
|
|
19
19
|
constructor(entityStorage: EntityStorage, statements: Statement<T>, entity: Options, model: new () => T, driver: DriverInterface, logger: Logger, conditionBuilder: SqlConditionBuilder<T>, columnManager: SqlColumnManager, modelTransformer: ModelTransformer, getOriginalColumnsCallback: () => string[], getAliasCallback: (tableName: string) => string);
|
|
20
|
+
private quoteId;
|
|
21
|
+
private qualifyTable;
|
|
20
22
|
addJoinForRelationshipPath(relationshipPath: string): void;
|
|
21
23
|
applyJoin(relationShip: Relationship<any>, value: FilterQuery<any>, alias: string): string;
|
|
22
24
|
handleSelectJoin(entities: any, models: any): Promise<void>;
|
|
25
|
+
handleSelectJoinBatch(entities: any[], models: any[]): Promise<void>;
|
|
23
26
|
getPathForSelectJoin(selectJoin: Statement<any>): string[] | null;
|
|
24
27
|
private buildJoinOn;
|
|
25
28
|
private addJoinedJoin;
|
|
26
29
|
private addSelectJoin;
|
|
27
30
|
private processSelectJoin;
|
|
31
|
+
private processSelectJoinBatch;
|
|
28
32
|
private getIds;
|
|
29
33
|
private updateJoinWhere;
|
|
30
34
|
private updateJoinColumns;
|
|
@@ -15,6 +15,16 @@ class SqlJoinManager {
|
|
|
15
15
|
this.getOriginalColumnsCallback = getOriginalColumnsCallback;
|
|
16
16
|
this.getAliasCallback = getAliasCallback;
|
|
17
17
|
}
|
|
18
|
+
quoteId(identifier) {
|
|
19
|
+
const q = this.driver.getIdentifierQuote();
|
|
20
|
+
return `${q}${identifier}${q}`;
|
|
21
|
+
}
|
|
22
|
+
qualifyTable(schema, tableName) {
|
|
23
|
+
if (this.driver.dbType === 'mysql') {
|
|
24
|
+
return this.quoteId(tableName);
|
|
25
|
+
}
|
|
26
|
+
return `${this.quoteId(schema)}.${this.quoteId(tableName)}`;
|
|
27
|
+
}
|
|
18
28
|
addJoinForRelationshipPath(relationshipPath) {
|
|
19
29
|
const relationshipNames = relationshipPath.split('.');
|
|
20
30
|
let currentEntity = this.entity;
|
|
@@ -69,18 +79,29 @@ class SqlJoinManager {
|
|
|
69
79
|
}
|
|
70
80
|
return models;
|
|
71
81
|
}
|
|
82
|
+
async handleSelectJoinBatch(entities, models) {
|
|
83
|
+
if (!this.statements.selectJoin || this.statements.selectJoin.length === 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
for (const join of this.statements.selectJoin.reverse()) {
|
|
87
|
+
await this.processSelectJoinBatch(join, entities, models);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
72
90
|
getPathForSelectJoin(selectJoin) {
|
|
73
91
|
const path = this.getPathForSelectJoinRecursive(selectJoin);
|
|
74
92
|
return path.reverse();
|
|
75
93
|
}
|
|
76
94
|
buildJoinOn(relationShip, alias, joinAlias, originPrimaryKey) {
|
|
77
95
|
let on = '';
|
|
96
|
+
const fkKey = this.quoteId(this.getFkKey(relationShip));
|
|
97
|
+
const pk = this.quoteId(originPrimaryKey);
|
|
78
98
|
switch (relationShip.relation) {
|
|
79
99
|
case "one-to-many":
|
|
80
|
-
on = `${joinAlias}
|
|
100
|
+
on = `${joinAlias}.${fkKey} = ${alias}.${pk}`;
|
|
81
101
|
break;
|
|
82
102
|
case "many-to-one":
|
|
83
|
-
|
|
103
|
+
const col = this.quoteId(relationShip.columnName);
|
|
104
|
+
on = `${alias}.${col} = ${joinAlias}.${fkKey}`;
|
|
84
105
|
break;
|
|
85
106
|
}
|
|
86
107
|
return on;
|
|
@@ -109,7 +130,7 @@ class SqlJoinManager {
|
|
|
109
130
|
this.statements.selectJoin.push({
|
|
110
131
|
statement: 'select',
|
|
111
132
|
columns: this.getOriginalColumnsCallback().filter(column => column.startsWith(`${relationShip.propertyKey}`)).map(column => column.split('.')[1]) || [],
|
|
112
|
-
table:
|
|
133
|
+
table: this.qualifyTable(joinSchema || 'public', joinTableName),
|
|
113
134
|
alias: joinAlias,
|
|
114
135
|
where: joinWhere,
|
|
115
136
|
joinProperty: relationShip.propertyKey,
|
|
@@ -133,6 +154,35 @@ class SqlJoinManager {
|
|
|
133
154
|
this.logger.debug(`SQL: ${child.sql} [${Date.now() - child.startTime}ms]`);
|
|
134
155
|
this.attachJoinResults(join, child, models);
|
|
135
156
|
}
|
|
157
|
+
async processSelectJoinBatch(join, entities, models) {
|
|
158
|
+
const allIds = new Set();
|
|
159
|
+
for (let i = 0; i < entities.length; i++) {
|
|
160
|
+
const ids = this.getIds(join, entities[i], models[i]);
|
|
161
|
+
if (Array.isArray(ids)) {
|
|
162
|
+
ids.forEach(id => {
|
|
163
|
+
if (id !== undefined && id !== null) {
|
|
164
|
+
allIds.add(id);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else if (ids !== undefined && ids !== null) {
|
|
169
|
+
allIds.add(ids);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (allIds.size === 0) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const idsString = Array.from(allIds)
|
|
176
|
+
.map((id) => this.formatValue(id))
|
|
177
|
+
.join(', ');
|
|
178
|
+
this.updateJoinWhere(join, idsString);
|
|
179
|
+
this.updateJoinColumns(join);
|
|
180
|
+
const result = await this.driver.executeStatement(join);
|
|
181
|
+
this.logger.debug(`SQL (BATCHED): ${result.sql} [${Date.now() - result.startTime}ms]`);
|
|
182
|
+
for (let i = 0; i < entities.length; i++) {
|
|
183
|
+
this.attachJoinResults(join, result, models[i]);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
136
186
|
getIds(join, entities, models) {
|
|
137
187
|
let ids = entities[`${join.originAlias}_${join.primaryKey}`];
|
|
138
188
|
if (typeof ids === 'undefined') {
|
|
@@ -145,16 +195,21 @@ class SqlJoinManager {
|
|
|
145
195
|
return ids;
|
|
146
196
|
}
|
|
147
197
|
updateJoinWhere(join, ids) {
|
|
198
|
+
const fkCol = this.quoteId(join.fkKey);
|
|
148
199
|
if (join.where) {
|
|
149
|
-
join.where = `${join.where} AND ${join.alias}
|
|
200
|
+
join.where = `${join.where} AND ${join.alias}.${fkCol} IN (${ids})`;
|
|
150
201
|
}
|
|
151
202
|
else {
|
|
152
|
-
join.where = `${join.alias}
|
|
203
|
+
join.where = `${join.alias}.${fkCol} IN (${ids})`;
|
|
153
204
|
}
|
|
154
205
|
}
|
|
155
206
|
updateJoinColumns(join) {
|
|
156
207
|
if (join.columns && join.columns.length > 0) {
|
|
157
|
-
join.columns = join.columns.map((column) =>
|
|
208
|
+
join.columns = join.columns.map((column) => {
|
|
209
|
+
const col = this.quoteId(column);
|
|
210
|
+
const aliasedCol = this.quoteId(`${join.alias}_${column}`);
|
|
211
|
+
return `${join.alias}.${col} as ${aliasedCol}`;
|
|
212
|
+
});
|
|
158
213
|
}
|
|
159
214
|
else {
|
|
160
215
|
join.columns = this.columnManager.getColumnsForEntity(join.joinEntity, join.alias);
|
|
@@ -198,6 +253,11 @@ class SqlJoinManager {
|
|
|
198
253
|
}
|
|
199
254
|
getFkKey(relationShip) {
|
|
200
255
|
if (typeof relationShip.fkKey === 'undefined') {
|
|
256
|
+
// Use cached primary key column name from the related entity instead of hardcoded 'id'
|
|
257
|
+
const relatedEntity = this.entityStorage.get(relationShip.entity());
|
|
258
|
+
if (relatedEntity) {
|
|
259
|
+
return relatedEntity._primaryKeyColumnName || 'id';
|
|
260
|
+
}
|
|
201
261
|
return 'id';
|
|
202
262
|
}
|
|
203
263
|
if (typeof relationShip.fkKey === 'string') {
|
|
@@ -228,12 +288,7 @@ class SqlJoinManager {
|
|
|
228
288
|
return { tableName, schema };
|
|
229
289
|
}
|
|
230
290
|
getPrimaryKey() {
|
|
231
|
-
|
|
232
|
-
if (this.entity.properties[prop].options.isPrimary) {
|
|
233
|
-
return prop;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return 'id';
|
|
291
|
+
return this.entity._primaryKeyPropertyName || 'id';
|
|
237
292
|
}
|
|
238
293
|
formatValue(value) {
|
|
239
294
|
return (typeof value === 'string') ? `'${value}'` : value;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { FilterQuery, Relationship } from '../driver/driver.interface';
|
|
1
|
+
import { DriverInterface, FilterQuery, Relationship } from '../driver/driver.interface';
|
|
2
2
|
import { EntityStorage } from '../domain/entities';
|
|
3
3
|
import { SqlConditionBuilder } from './sql-condition-builder';
|
|
4
4
|
export declare class SqlSubqueryBuilder {
|
|
5
5
|
private entityStorage;
|
|
6
6
|
private getConditionBuilder;
|
|
7
|
+
private driver;
|
|
7
8
|
private aliasCounter;
|
|
8
|
-
constructor(entityStorage: EntityStorage, getConditionBuilder: () => SqlConditionBuilder<any
|
|
9
|
+
constructor(entityStorage: EntityStorage, getConditionBuilder: () => SqlConditionBuilder<any>, driver: DriverInterface);
|
|
10
|
+
private quoteId;
|
|
11
|
+
private qualifyTable;
|
|
9
12
|
buildExistsSubquery(relationship: Relationship<any>, filters: FilterQuery<any>, outerAlias: string, negate: boolean, outerModel?: Function): string;
|
|
10
13
|
private buildSubquery;
|
|
11
14
|
private buildWhereClause;
|
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SqlSubqueryBuilder = void 0;
|
|
4
4
|
class SqlSubqueryBuilder {
|
|
5
|
-
constructor(entityStorage, getConditionBuilder) {
|
|
5
|
+
constructor(entityStorage, getConditionBuilder, driver) {
|
|
6
6
|
this.entityStorage = entityStorage;
|
|
7
7
|
this.getConditionBuilder = getConditionBuilder;
|
|
8
|
+
this.driver = driver;
|
|
8
9
|
this.aliasCounter = 1;
|
|
9
10
|
}
|
|
11
|
+
quoteId(identifier) {
|
|
12
|
+
const q = this.driver.getIdentifierQuote();
|
|
13
|
+
return `${q}${identifier}${q}`;
|
|
14
|
+
}
|
|
15
|
+
qualifyTable(schema, tableName) {
|
|
16
|
+
if (this.driver.dbType === 'mysql') {
|
|
17
|
+
return this.quoteId(tableName);
|
|
18
|
+
}
|
|
19
|
+
return `${this.quoteId(schema)}.${this.quoteId(tableName)}`;
|
|
20
|
+
}
|
|
10
21
|
buildExistsSubquery(relationship, filters, outerAlias, negate, outerModel) {
|
|
11
22
|
const prefix = negate ? 'NOT EXISTS' : 'EXISTS';
|
|
12
23
|
const subquery = this.buildSubquery(relationship, filters, outerAlias, outerModel);
|
|
@@ -28,10 +39,13 @@ class SqlSubqueryBuilder {
|
|
|
28
39
|
const outerPkKey = this.getOuterPrimaryKey(relationship, outerModel);
|
|
29
40
|
const relatedPkKey = this.getRelatedPrimaryKey(relationship);
|
|
30
41
|
if (relationship.relation === 'one-to-many') {
|
|
31
|
-
|
|
42
|
+
const fk = this.quoteId(fkKey);
|
|
43
|
+
const pk = this.quoteId(outerPkKey);
|
|
44
|
+
return `${subqueryAlias}.${fk} = ${outerAlias}.${pk}`;
|
|
32
45
|
}
|
|
33
|
-
const outerFk = relationship.columnName;
|
|
34
|
-
|
|
46
|
+
const outerFk = this.quoteId(relationship.columnName);
|
|
47
|
+
const relatedPk = this.quoteId(relatedPkKey);
|
|
48
|
+
return `${outerAlias}.${outerFk} = ${subqueryAlias}.${relatedPk}`;
|
|
35
49
|
}
|
|
36
50
|
getOuterPrimaryKey(relationship, outerModel) {
|
|
37
51
|
if (!outerModel) {
|
|
@@ -66,11 +80,11 @@ class SqlSubqueryBuilder {
|
|
|
66
80
|
const entity = this.entityStorage.get(relationship.entity());
|
|
67
81
|
if (!entity) {
|
|
68
82
|
const name = relationship.entity().name.toLowerCase();
|
|
69
|
-
return
|
|
83
|
+
return this.qualifyTable('public', name);
|
|
70
84
|
}
|
|
71
85
|
const schema = entity.schema || 'public';
|
|
72
86
|
const tableName = entity.tableName || relationship.entity().name.toLowerCase();
|
|
73
|
-
return
|
|
87
|
+
return this.qualifyTable(schema, tableName);
|
|
74
88
|
}
|
|
75
89
|
generateAlias() {
|
|
76
90
|
const alias = `sq${this.aliasCounter}`;
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { Orm } from '../orm';
|
|
2
|
-
import {
|
|
3
|
-
import { ConnectionSettings } from '../driver/driver.interface';
|
|
2
|
+
import { ConnectionSettings, DriverInterface } from '../driver/driver.interface';
|
|
4
3
|
import type { Logger } from '../logger';
|
|
4
|
+
import { DriverType } from '../driver/driver-factory';
|
|
5
5
|
export type DatabaseTestContext = {
|
|
6
|
-
orm: Orm<
|
|
6
|
+
orm: Orm<DriverInterface>;
|
|
7
7
|
executeSql: (sql: string) => Promise<{
|
|
8
8
|
rows: unknown[];
|
|
9
9
|
}>;
|
|
10
|
+
driverType: DriverType;
|
|
11
|
+
getDbType: () => 'postgres' | 'mysql';
|
|
10
12
|
};
|
|
11
13
|
export type DatabaseTestOptions = {
|
|
12
14
|
schema?: string;
|
|
13
15
|
entityFile?: string;
|
|
14
16
|
logger?: Logger;
|
|
15
|
-
connection?: Partial<ConnectionSettings
|
|
17
|
+
connection?: Partial<ConnectionSettings>;
|
|
16
18
|
};
|
|
17
19
|
type DatabaseTestRoutine = (context: DatabaseTestContext) => Promise<void>;
|
|
18
20
|
export declare function withDatabase(tables: string[], routine: DatabaseTestRoutine, options?: DatabaseTestOptions): Promise<void>;
|