@carno.js/orm 1.0.7 → 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
package/dist/SqlBuilder.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export declare class SqlBuilder<T> {
|
|
|
15
15
|
private _modelTransformer?;
|
|
16
16
|
private _joinManager?;
|
|
17
17
|
private readonly boundGetAlias;
|
|
18
|
+
private quoteId;
|
|
19
|
+
private qualifyTable;
|
|
18
20
|
constructor(model: new () => T);
|
|
19
21
|
private get modelTransformer();
|
|
20
22
|
private get joinManager();
|
|
@@ -37,7 +39,6 @@ export declare class SqlBuilder<T> {
|
|
|
37
39
|
cache(cache: boolean | number | Date | undefined): SqlBuilder<T>;
|
|
38
40
|
load(load: string[]): SqlBuilder<T>;
|
|
39
41
|
count(): SqlBuilder<T>;
|
|
40
|
-
private getPrimaryKeyColumnName;
|
|
41
42
|
private shouldUseCache;
|
|
42
43
|
private getCacheTtl;
|
|
43
44
|
private getCachedResult;
|
|
@@ -63,7 +64,6 @@ export declare class SqlBuilder<T> {
|
|
|
63
64
|
private attachOneToManyRelations;
|
|
64
65
|
private removeDuplicatesByPrimaryKey;
|
|
65
66
|
private getPrimaryKeyName;
|
|
66
|
-
private getPrimaryKeyNameForEntity;
|
|
67
67
|
executeCount(): Promise<number>;
|
|
68
68
|
private logExecution;
|
|
69
69
|
inTransaction<T>(callback: (builder: SqlBuilder<T>) => Promise<T>): Promise<T>;
|
package/dist/SqlBuilder.js
CHANGED
|
@@ -10,6 +10,16 @@ const model_transformer_1 = require("./query/model-transformer");
|
|
|
10
10
|
const sql_column_manager_1 = require("./query/sql-column-manager");
|
|
11
11
|
const sql_join_manager_1 = require("./query/sql-join-manager");
|
|
12
12
|
class SqlBuilder {
|
|
13
|
+
quoteId(identifier) {
|
|
14
|
+
const q = this.driver.getIdentifierQuote();
|
|
15
|
+
return `${q}${identifier}${q}`;
|
|
16
|
+
}
|
|
17
|
+
qualifyTable(schema, tableName) {
|
|
18
|
+
if (this.driver.dbType === 'mysql') {
|
|
19
|
+
return this.quoteId(tableName);
|
|
20
|
+
}
|
|
21
|
+
return `${this.quoteId(schema)}.${this.quoteId(tableName)}`;
|
|
22
|
+
}
|
|
13
23
|
constructor(model) {
|
|
14
24
|
this.statements = {};
|
|
15
25
|
this.aliases = new Set();
|
|
@@ -24,12 +34,12 @@ class SqlBuilder {
|
|
|
24
34
|
this.statements.hooks = this.entity.hooks;
|
|
25
35
|
// Pre-bind once
|
|
26
36
|
this.boundGetAlias = this.getAlias.bind(this);
|
|
27
|
-
this.columnManager = new sql_column_manager_1.SqlColumnManager(this.entityStorage, this.statements, this.entity);
|
|
37
|
+
this.columnManager = new sql_column_manager_1.SqlColumnManager(this.entityStorage, this.statements, this.entity, this.driver);
|
|
28
38
|
const applyJoinWrapper = (relationship, value, alias) => {
|
|
29
39
|
return this.joinManager.applyJoin(relationship, value, alias);
|
|
30
40
|
};
|
|
31
|
-
this.conditionBuilder = new sql_condition_builder_1.SqlConditionBuilder(this.entityStorage, applyJoinWrapper, this.statements);
|
|
32
|
-
const subqueryBuilder = new sql_subquery_builder_1.SqlSubqueryBuilder(this.entityStorage, () => this.conditionBuilder);
|
|
41
|
+
this.conditionBuilder = new sql_condition_builder_1.SqlConditionBuilder(this.entityStorage, applyJoinWrapper, this.statements, this.driver);
|
|
42
|
+
const subqueryBuilder = new sql_subquery_builder_1.SqlSubqueryBuilder(this.entityStorage, () => this.conditionBuilder, this.driver);
|
|
33
43
|
this.conditionBuilder.setSubqueryBuilder(subqueryBuilder);
|
|
34
44
|
}
|
|
35
45
|
// Lazy getter for modelTransformer - only created when transform is needed
|
|
@@ -53,7 +63,7 @@ class SqlBuilder {
|
|
|
53
63
|
this.statements.columns = columns;
|
|
54
64
|
this.originalColumns = columns || [];
|
|
55
65
|
this.statements.alias = this.getAlias(tableName);
|
|
56
|
-
this.statements.table =
|
|
66
|
+
this.statements.table = this.qualifyTable(schema, tableName);
|
|
57
67
|
return this;
|
|
58
68
|
}
|
|
59
69
|
setStrategy(strategy = 'joined') {
|
|
@@ -70,7 +80,7 @@ class SqlBuilder {
|
|
|
70
80
|
this.statements.statement = 'insert';
|
|
71
81
|
this.statements.instance = value_processor_1.ValueProcessor.createInstance(processedValues, this.model, 'insert');
|
|
72
82
|
this.statements.alias = this.getAlias(tableName);
|
|
73
|
-
this.statements.table =
|
|
83
|
+
this.statements.table = this.qualifyTable(schema, tableName);
|
|
74
84
|
this.statements.values = this.withUpdatedValues(this.withDefaultValues(processedValues, this.entity), this.entity);
|
|
75
85
|
this.reflectToValues();
|
|
76
86
|
return this;
|
|
@@ -80,7 +90,7 @@ class SqlBuilder {
|
|
|
80
90
|
const processedValues = value_processor_1.ValueProcessor.processForUpdate(values, this.entity);
|
|
81
91
|
this.statements.statement = 'update';
|
|
82
92
|
this.statements.alias = this.getAlias(tableName);
|
|
83
|
-
this.statements.table =
|
|
93
|
+
this.statements.table = this.qualifyTable(schema, tableName);
|
|
84
94
|
this.statements.values = this.withUpdatedValues(processedValues, this.entity);
|
|
85
95
|
this.statements.instance = value_processor_1.ValueProcessor.createInstance(processedValues, this.model, 'update');
|
|
86
96
|
return this;
|
|
@@ -89,7 +99,7 @@ class SqlBuilder {
|
|
|
89
99
|
const { tableName, schema } = this.getTableName();
|
|
90
100
|
this.statements.statement = 'delete';
|
|
91
101
|
this.statements.alias = this.getAlias(tableName);
|
|
92
|
-
this.statements.table =
|
|
102
|
+
this.statements.table = this.qualifyTable(schema, tableName);
|
|
93
103
|
return this;
|
|
94
104
|
}
|
|
95
105
|
where(where) {
|
|
@@ -143,20 +153,16 @@ class SqlBuilder {
|
|
|
143
153
|
const { tableName, schema } = this.getTableName();
|
|
144
154
|
this.statements.statement = 'count';
|
|
145
155
|
this.statements.alias = this.getAlias(tableName);
|
|
146
|
-
this.statements.table =
|
|
156
|
+
this.statements.table = this.qualifyTable(schema, tableName);
|
|
147
157
|
return this;
|
|
148
158
|
}
|
|
149
|
-
getPrimaryKeyColumnName(entity) {
|
|
150
|
-
// Logic to resolve the entity primary key column name
|
|
151
|
-
// Replace this with your own logic based on your project structure
|
|
152
|
-
// For example, if the primary key is always 'id', you can return 'id'.
|
|
153
|
-
// If the logic becomes more complex, add a method on Options to resolve the primary key.
|
|
154
|
-
return 'id';
|
|
155
|
-
}
|
|
156
159
|
shouldUseCache() {
|
|
157
160
|
if (this.statements.statement !== 'select') {
|
|
158
161
|
return false;
|
|
159
162
|
}
|
|
163
|
+
if (this.statements.cache === false) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
160
166
|
if (this.statements.cache instanceof Date) {
|
|
161
167
|
return this.statements.cache.getTime() > Date.now();
|
|
162
168
|
}
|
|
@@ -203,6 +209,9 @@ class SqlBuilder {
|
|
|
203
209
|
if (this.shouldUseCache()) {
|
|
204
210
|
await this.setCachedResult(result);
|
|
205
211
|
}
|
|
212
|
+
if (this.isWriteOperation()) {
|
|
213
|
+
await this.invalidateCache();
|
|
214
|
+
}
|
|
206
215
|
return result;
|
|
207
216
|
}
|
|
208
217
|
isWriteOperation() {
|
|
@@ -213,6 +222,11 @@ class SqlBuilder {
|
|
|
213
222
|
if (!this.cacheManager) {
|
|
214
223
|
return;
|
|
215
224
|
}
|
|
225
|
+
const cacheConfig = orm_1.Orm.getInstance().connection.cache;
|
|
226
|
+
const shouldInvalidate = cacheConfig?.invalidateCacheOnWrite ?? true;
|
|
227
|
+
if (!shouldInvalidate) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
216
230
|
await this.cacheManager.invalidate(this.statements);
|
|
217
231
|
}
|
|
218
232
|
prepareColumns() {
|
|
@@ -299,9 +313,9 @@ class SqlBuilder {
|
|
|
299
313
|
for (const row of rows) {
|
|
300
314
|
const models = this.modelTransformer.transform(this.model, this.statements, row);
|
|
301
315
|
this.afterHooks(models);
|
|
302
|
-
await this.joinManager.handleSelectJoin(row, models);
|
|
303
316
|
results.push(models);
|
|
304
317
|
}
|
|
318
|
+
await this.joinManager.handleSelectJoinBatch(rows, results);
|
|
305
319
|
return results;
|
|
306
320
|
}
|
|
307
321
|
hasOneToManyJoinedJoin() {
|
|
@@ -404,7 +418,7 @@ class SqlBuilder {
|
|
|
404
418
|
if (!entity) {
|
|
405
419
|
return models;
|
|
406
420
|
}
|
|
407
|
-
const primaryKey =
|
|
421
|
+
const primaryKey = entity._primaryKeyPropertyName || 'id';
|
|
408
422
|
const seen = new Set();
|
|
409
423
|
const unique = [];
|
|
410
424
|
for (const model of models) {
|
|
@@ -417,15 +431,7 @@ class SqlBuilder {
|
|
|
417
431
|
return unique;
|
|
418
432
|
}
|
|
419
433
|
getPrimaryKeyName() {
|
|
420
|
-
return this.
|
|
421
|
-
}
|
|
422
|
-
getPrimaryKeyNameForEntity(entity) {
|
|
423
|
-
for (const prop in entity.properties) {
|
|
424
|
-
if (entity.properties[prop].options.isPrimary) {
|
|
425
|
-
return prop;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return 'id';
|
|
434
|
+
return this.entity._primaryKeyPropertyName || 'id';
|
|
429
435
|
}
|
|
430
436
|
async executeCount() {
|
|
431
437
|
const result = await this.execute();
|
|
@@ -570,7 +576,9 @@ class SqlBuilder {
|
|
|
570
576
|
applyOnInsert(values, key, property) {
|
|
571
577
|
const columnName = property.options.columnName;
|
|
572
578
|
values[columnName] = property.options.onInsert();
|
|
573
|
-
|
|
579
|
+
const col = this.quoteId(columnName);
|
|
580
|
+
const aliasedCol = this.quoteId(`${this.statements.alias}_${columnName}`);
|
|
581
|
+
this.updatedColumns.push(`${this.statements.alias}.${col} as ${aliasedCol}`);
|
|
574
582
|
}
|
|
575
583
|
withUpdatedValues(values, entityOptions) {
|
|
576
584
|
const properties = Object.entries(entityOptions.properties).filter(([_, value]) => value.options.onUpdate);
|
|
@@ -580,7 +588,9 @@ class SqlBuilder {
|
|
|
580
588
|
applyOnUpdate(values, property) {
|
|
581
589
|
const columnName = property.options.columnName;
|
|
582
590
|
values[columnName] = property.options.onUpdate();
|
|
583
|
-
|
|
591
|
+
const col = this.quoteId(columnName);
|
|
592
|
+
const aliasedCol = this.quoteId(`${this.statements.alias}_${columnName}`);
|
|
593
|
+
this.updatedColumns.push(`${this.statements.alias}.${col} as ${aliasedCol}`);
|
|
584
594
|
}
|
|
585
595
|
callHook(type, model) {
|
|
586
596
|
const hooks = this.statements.hooks?.filter(hook => hook.type === type) || [];
|
|
@@ -49,6 +49,11 @@ export declare abstract class BaseEntity {
|
|
|
49
49
|
* @return {boolean} Returns true if the object has been persisted, otherwise false.
|
|
50
50
|
*/
|
|
51
51
|
isPersisted(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Removes this entity from the database.
|
|
54
|
+
* Uses cached primary key property name instead of hardcoded 'id'.
|
|
55
|
+
*/
|
|
56
|
+
remove(): Promise<void>;
|
|
52
57
|
toJSON(): Record<string, any>;
|
|
53
58
|
private serializeWithEntity;
|
|
54
59
|
private serializeWithMetadata;
|
|
@@ -111,8 +111,12 @@ class BaseEntity {
|
|
|
111
111
|
if (this.$_isPersisted) {
|
|
112
112
|
qb.update(this._changedValues);
|
|
113
113
|
qb.setInstance(this);
|
|
114
|
+
// Use cached primary key property name instead of hardcoded 'id'
|
|
115
|
+
const storage = entities_1.EntityStorage.getInstance();
|
|
116
|
+
const options = storage.get(this.constructor);
|
|
117
|
+
const pkName = options?._primaryKeyPropertyName || 'id';
|
|
114
118
|
// @ts-ignore
|
|
115
|
-
qb.where({
|
|
119
|
+
qb.where({ [pkName]: this._oldValues[pkName] });
|
|
116
120
|
}
|
|
117
121
|
else {
|
|
118
122
|
qb.insert(this._oldValues);
|
|
@@ -134,6 +138,19 @@ class BaseEntity {
|
|
|
134
138
|
isPersisted() {
|
|
135
139
|
return this.$_isPersisted;
|
|
136
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Removes this entity from the database.
|
|
143
|
+
* Uses cached primary key property name instead of hardcoded 'id'.
|
|
144
|
+
*/
|
|
145
|
+
async remove() {
|
|
146
|
+
const qb = this.createQueryBuilder();
|
|
147
|
+
const storage = entities_1.EntityStorage.getInstance();
|
|
148
|
+
const options = storage.get(this.constructor);
|
|
149
|
+
const pkName = options?._primaryKeyPropertyName || 'id';
|
|
150
|
+
// @ts-ignore
|
|
151
|
+
qb.delete().where({ [pkName]: this._oldValues[pkName] });
|
|
152
|
+
await qb.execute();
|
|
153
|
+
}
|
|
137
154
|
toJSON() {
|
|
138
155
|
const storage = entities_1.EntityStorage.getInstance();
|
|
139
156
|
const entity = storage.get(this.constructor);
|
|
@@ -18,6 +18,8 @@ export type Options = {
|
|
|
18
18
|
propertyName: string;
|
|
19
19
|
}[];
|
|
20
20
|
schema?: string;
|
|
21
|
+
_primaryKeyPropertyName?: string;
|
|
22
|
+
_primaryKeyColumnName?: string;
|
|
21
23
|
};
|
|
22
24
|
export declare class EntityStorage {
|
|
23
25
|
static instance: EntityStorage;
|
|
@@ -34,6 +36,12 @@ export declare class EntityStorage {
|
|
|
34
36
|
}[]): void;
|
|
35
37
|
get(entity: Function): Options;
|
|
36
38
|
entries(): MapIterator<[Function, Options]>;
|
|
39
|
+
/**
|
|
40
|
+
* Computa metadados da primary key a partir das propriedades da entidade.
|
|
41
|
+
* Chamado uma vez durante o registro da entidade para lookups O(1).
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
private computePrimaryKeyInfo;
|
|
37
45
|
static getInstance(): EntityStorage;
|
|
38
46
|
snapshot(values: Options): Promise<SnapshotTable>;
|
|
39
47
|
private snapshotColumns;
|
package/dist/domain/entities.js
CHANGED
|
@@ -112,6 +112,8 @@ let EntityStorage = EntityStorage_1 = class EntityStorage {
|
|
|
112
112
|
const indexes = core_1.Metadata.get("indexes", entity.target) || [];
|
|
113
113
|
const uniques = core_1.Metadata.get("uniques", entity.target) || [];
|
|
114
114
|
const columnMap = buildIndexColumnMap(properties, relations);
|
|
115
|
+
// Compute primary key cache once during registration
|
|
116
|
+
const pkInfo = this.computePrimaryKeyInfo(properties);
|
|
115
117
|
this.entities.set(entity.target, {
|
|
116
118
|
properties: properties,
|
|
117
119
|
hideProperties: Object.entries(properties)
|
|
@@ -123,6 +125,8 @@ let EntityStorage = EntityStorage_1 = class EntityStorage {
|
|
|
123
125
|
hooks,
|
|
124
126
|
tableName: entityName,
|
|
125
127
|
...entity.options,
|
|
128
|
+
_primaryKeyPropertyName: pkInfo.propertyName,
|
|
129
|
+
_primaryKeyColumnName: pkInfo.columnName,
|
|
126
130
|
});
|
|
127
131
|
}
|
|
128
132
|
get(entity) {
|
|
@@ -131,6 +135,26 @@ let EntityStorage = EntityStorage_1 = class EntityStorage {
|
|
|
131
135
|
entries() {
|
|
132
136
|
return this.entities.entries();
|
|
133
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Computa metadados da primary key a partir das propriedades da entidade.
|
|
140
|
+
* Chamado uma vez durante o registro da entidade para lookups O(1).
|
|
141
|
+
* @private
|
|
142
|
+
*/
|
|
143
|
+
computePrimaryKeyInfo(properties) {
|
|
144
|
+
for (const prop in properties) {
|
|
145
|
+
if (properties[prop].options.isPrimary) {
|
|
146
|
+
return {
|
|
147
|
+
propertyName: prop,
|
|
148
|
+
columnName: properties[prop].options.columnName,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Fallback para 'id' (backward compatibility)
|
|
153
|
+
return {
|
|
154
|
+
propertyName: 'id',
|
|
155
|
+
columnName: 'id',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
134
158
|
static getInstance() {
|
|
135
159
|
const scoped = orm_session_context_1.ormSessionContext.getStorage();
|
|
136
160
|
if (scoped) {
|
|
@@ -13,7 +13,7 @@ export declare abstract class BunDriverBase implements Partial<DriverInterface>
|
|
|
13
13
|
constructor(options: ConnectionSettings);
|
|
14
14
|
protected buildConnectionString(options: ConnectionSettings): string;
|
|
15
15
|
protected abstract getProtocol(): string;
|
|
16
|
-
|
|
16
|
+
abstract getIdentifierQuote(): string;
|
|
17
17
|
connect(): Promise<void>;
|
|
18
18
|
protected buildPoolOptions(options: ConnectionSettings): {
|
|
19
19
|
max: number;
|
|
@@ -29,6 +29,7 @@ export declare abstract class BunDriverBase implements Partial<DriverInterface>
|
|
|
29
29
|
transaction<T>(callback: (tx: SQL) => Promise<T>): Promise<T>;
|
|
30
30
|
protected toDatabaseValue(value: unknown): string | number | boolean;
|
|
31
31
|
protected escapeIdentifier(identifier: string): string;
|
|
32
|
+
protected formatDateForMysql(value: Date): string;
|
|
32
33
|
protected buildWhereClause(where: string | undefined): string;
|
|
33
34
|
protected buildOrderByClause(orderBy: string[] | undefined): string;
|
|
34
35
|
protected buildLimitClause(limit: number | undefined): string;
|
|
@@ -41,7 +42,7 @@ export declare abstract class BunDriverBase implements Partial<DriverInterface>
|
|
|
41
42
|
beforeSql: string;
|
|
42
43
|
columnType: string;
|
|
43
44
|
};
|
|
44
|
-
protected abstract handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number): Promise<{
|
|
45
|
+
protected abstract handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number, context: any): Promise<{
|
|
45
46
|
query: any;
|
|
46
47
|
startTime: number;
|
|
47
48
|
sql: string;
|
|
@@ -76,7 +76,10 @@ class BunDriverBase {
|
|
|
76
76
|
return "NULL";
|
|
77
77
|
}
|
|
78
78
|
if (value instanceof Date) {
|
|
79
|
-
|
|
79
|
+
const formatted = this.dbType === "mysql"
|
|
80
|
+
? this.formatDateForMysql(value)
|
|
81
|
+
: value.toISOString();
|
|
82
|
+
return `'${formatted}'`;
|
|
80
83
|
}
|
|
81
84
|
switch (typeof value) {
|
|
82
85
|
case "string":
|
|
@@ -94,6 +97,13 @@ class BunDriverBase {
|
|
|
94
97
|
escapeIdentifier(identifier) {
|
|
95
98
|
return `"${identifier}"`;
|
|
96
99
|
}
|
|
100
|
+
formatDateForMysql(value) {
|
|
101
|
+
return value
|
|
102
|
+
.toISOString()
|
|
103
|
+
.replace('T', ' ')
|
|
104
|
+
.replace('Z', '')
|
|
105
|
+
.replace(/\.\d{3}/, '');
|
|
106
|
+
}
|
|
97
107
|
buildWhereClause(where) {
|
|
98
108
|
if (!where) {
|
|
99
109
|
return "";
|
|
@@ -149,7 +159,7 @@ class BunDriverBase {
|
|
|
149
159
|
if (statement.statement === "insert") {
|
|
150
160
|
const sql = this.buildInsertSqlWithReturn(statement);
|
|
151
161
|
const result = await context.unsafe(sql);
|
|
152
|
-
return this.handleInsertReturn(statement, result, sql, startTime);
|
|
162
|
+
return this.handleInsertReturn(statement, result, sql, startTime, context);
|
|
153
163
|
}
|
|
154
164
|
const sql = this.buildStatementSql(statement);
|
|
155
165
|
const result = await context.unsafe(sql);
|
|
@@ -210,7 +220,10 @@ class BunDriverBase {
|
|
|
210
220
|
return "";
|
|
211
221
|
return statement.join
|
|
212
222
|
.map((join) => {
|
|
213
|
-
const
|
|
223
|
+
const q = this.getIdentifierQuote();
|
|
224
|
+
const table = this.dbType === 'mysql'
|
|
225
|
+
? `${q}${join.joinTable}${q}`
|
|
226
|
+
: `${join.joinSchema}.${join.joinTable}`;
|
|
214
227
|
return ` ${join.type} JOIN ${table} ${join.joinAlias} ON ${join.on}`;
|
|
215
228
|
})
|
|
216
229
|
.join("");
|
|
@@ -4,14 +4,14 @@ export declare class BunMysqlDriver extends BunDriverBase implements DriverInter
|
|
|
4
4
|
readonly dbType: "mysql";
|
|
5
5
|
constructor(options: ConnectionSettings);
|
|
6
6
|
protected getProtocol(): string;
|
|
7
|
-
|
|
7
|
+
getIdentifierQuote(): string;
|
|
8
8
|
protected buildAutoIncrementType(colDiff: ColDiff): string;
|
|
9
9
|
protected buildEnumType(schema: string | undefined, tableName: string, colDiff: ColDiff): {
|
|
10
10
|
beforeSql: string;
|
|
11
11
|
columnType: string;
|
|
12
12
|
};
|
|
13
13
|
protected appendReturningClause(sql: string, statement: Statement<any>): string;
|
|
14
|
-
protected handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number): Promise<{
|
|
14
|
+
protected handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number, context: any): Promise<{
|
|
15
15
|
query: any;
|
|
16
16
|
startTime: number;
|
|
17
17
|
sql: string;
|
|
@@ -28,7 +28,7 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
28
28
|
appendReturningClause(sql, statement) {
|
|
29
29
|
return sql;
|
|
30
30
|
}
|
|
31
|
-
async handleInsertReturn(statement, result, sql, startTime) {
|
|
31
|
+
async handleInsertReturn(statement, result, sql, startTime, context) {
|
|
32
32
|
if (!statement.columns) {
|
|
33
33
|
return {
|
|
34
34
|
query: { rows: Array.isArray(result) ? result : [] },
|
|
@@ -36,10 +36,28 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
36
36
|
sql,
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
let insertId;
|
|
40
|
+
// Check if ID was manually provided in the values
|
|
41
|
+
if (statement.values && statement.values.id) {
|
|
42
|
+
insertId = statement.values.id;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// For AUTO_INCREMENT, use LAST_INSERT_ID()
|
|
46
|
+
const lastIdResult = await context.unsafe('SELECT LAST_INSERT_ID() as id');
|
|
47
|
+
insertId = lastIdResult[0]?.id;
|
|
48
|
+
}
|
|
49
|
+
if (!insertId) {
|
|
50
|
+
// If no ID available, return empty result
|
|
51
|
+
return {
|
|
52
|
+
query: { rows: [] },
|
|
53
|
+
startTime,
|
|
54
|
+
sql,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
40
57
|
const cols = statement.columns.join(', ').replaceAll(`${statement.alias}.`, '');
|
|
41
|
-
const
|
|
42
|
-
const
|
|
58
|
+
const idValue = this.toDatabaseValue(insertId);
|
|
59
|
+
const selectSql = `SELECT ${cols} FROM ${statement.table} WHERE id = ${idValue}`;
|
|
60
|
+
const selectResult = await context.unsafe(selectSql);
|
|
43
61
|
return {
|
|
44
62
|
query: { rows: Array.isArray(selectResult) ? selectResult : [] },
|
|
45
63
|
startTime,
|
|
@@ -58,7 +76,7 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
58
76
|
}
|
|
59
77
|
getCreateTableInstruction(schema, tableName, creates) {
|
|
60
78
|
let beforeSql = ``;
|
|
61
|
-
const st = `CREATE TABLE \`${
|
|
79
|
+
const st = `CREATE TABLE \`${tableName}\` (${creates
|
|
62
80
|
.map((colDiff) => {
|
|
63
81
|
const isAutoIncrement = colDiff.colChanges?.autoIncrement;
|
|
64
82
|
let sql = ``;
|
|
@@ -92,7 +110,7 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
92
110
|
return beforeSql + st;
|
|
93
111
|
}
|
|
94
112
|
getAlterTableFkInstruction(schema, tableName, colDiff, fk) {
|
|
95
|
-
return `ALTER TABLE \`${
|
|
113
|
+
return `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${tableName}_${colDiff.colName}_fk\` FOREIGN KEY (\`${colDiff.colName}\`) REFERENCES \`${fk.referencedTableName}\` (\`${fk.referencedColumnName}\`);`;
|
|
96
114
|
}
|
|
97
115
|
getCreateIndex(index, schema, tableName) {
|
|
98
116
|
const properties = index.properties || [];
|
|
@@ -103,7 +121,7 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
103
121
|
throw new Error("Partial indexes are only supported on postgres.");
|
|
104
122
|
}
|
|
105
123
|
const columns = properties.map((prop) => `\`${prop}\``).join(', ');
|
|
106
|
-
return `CREATE INDEX \`${index.name}\` ON \`${
|
|
124
|
+
return `CREATE INDEX \`${index.name}\` ON \`${tableName}\` (${columns});`;
|
|
107
125
|
}
|
|
108
126
|
getAddColumn(schema, tableName, colName, colDiff, colDiffInstructions) {
|
|
109
127
|
let sql = ``;
|
|
@@ -111,10 +129,10 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
111
129
|
const enumValues = colDiff.colChanges.enumItems
|
|
112
130
|
.map((item) => `'${item}'`)
|
|
113
131
|
.join(', ');
|
|
114
|
-
sql += `ALTER TABLE \`${
|
|
132
|
+
sql += `ALTER TABLE \`${tableName}\` ADD COLUMN \`${colDiff.colName}\` ENUM(${enumValues})`;
|
|
115
133
|
}
|
|
116
134
|
else {
|
|
117
|
-
sql += `ALTER TABLE \`${
|
|
135
|
+
sql += `ALTER TABLE \`${tableName}\` ADD COLUMN \`${colName}\` ${colDiff.colType}${colDiff.colLength ? `(${colDiff.colLength})` : ''}`;
|
|
118
136
|
}
|
|
119
137
|
if (!colDiff.colChanges?.nullable) {
|
|
120
138
|
sql += ' NOT NULL';
|
|
@@ -131,15 +149,15 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
131
149
|
colDiffInstructions.push(sql.concat(';'));
|
|
132
150
|
if (colDiff.colChanges?.foreignKeys) {
|
|
133
151
|
colDiff.colChanges.foreignKeys.forEach((fk) => {
|
|
134
|
-
colDiffInstructions.push(`ALTER TABLE \`${
|
|
152
|
+
colDiffInstructions.push(`ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${tableName}_${colName}_fk\` FOREIGN KEY (\`${colName}\`) REFERENCES \`${fk.referencedTableName}\` (\`${fk.referencedColumnName}\`);`);
|
|
135
153
|
});
|
|
136
154
|
}
|
|
137
155
|
}
|
|
138
156
|
getDropColumn(colDiffInstructions, schema, tableName, colName) {
|
|
139
|
-
colDiffInstructions.push(`ALTER TABLE \`${
|
|
157
|
+
colDiffInstructions.push(`ALTER TABLE \`${tableName}\` DROP COLUMN IF EXISTS \`${colName}\`;`);
|
|
140
158
|
}
|
|
141
159
|
getDropIndex(index, schema, tableName) {
|
|
142
|
-
return `ALTER TABLE \`${
|
|
160
|
+
return `ALTER TABLE \`${tableName}\` DROP INDEX \`${index.name}\`;`;
|
|
143
161
|
}
|
|
144
162
|
getCreateUniqueConstraint(unique, schema, tableName) {
|
|
145
163
|
const properties = unique.properties || [];
|
|
@@ -147,37 +165,37 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
147
165
|
throw new Error("Unique properties are required.");
|
|
148
166
|
}
|
|
149
167
|
const columns = properties.map((prop) => `\`${prop}\``).join(', ');
|
|
150
|
-
return `ALTER TABLE \`${
|
|
168
|
+
return `ALTER TABLE \`${tableName}\` ADD CONSTRAINT \`${unique.name}\` UNIQUE (${columns});`;
|
|
151
169
|
}
|
|
152
170
|
getDropUniqueConstraint(unique, schema, tableName) {
|
|
153
|
-
return `ALTER TABLE \`${
|
|
171
|
+
return `ALTER TABLE \`${tableName}\` DROP INDEX \`${unique.name}\`;`;
|
|
154
172
|
}
|
|
155
173
|
getAlterTableType(schema, tableName, colName, colDiff) {
|
|
156
|
-
return `ALTER TABLE \`${
|
|
174
|
+
return `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${colName}\` ${colDiff.colType}${colDiff.colLength ? `(${colDiff.colLength})` : ''};`;
|
|
157
175
|
}
|
|
158
176
|
getAlterTableDefaultInstruction(schema, tableName, colName, colDiff) {
|
|
159
|
-
return `ALTER TABLE \`${
|
|
177
|
+
return `ALTER TABLE \`${tableName}\` ALTER COLUMN \`${colName}\` SET DEFAULT ${colDiff.colChanges.default};`;
|
|
160
178
|
}
|
|
161
179
|
getAlterTablePrimaryKeyInstruction(schema, tableName, colName, colDiff) {
|
|
162
|
-
return `ALTER TABLE \`${
|
|
180
|
+
return `ALTER TABLE \`${tableName}\` ADD PRIMARY KEY (\`${colName}\`);`;
|
|
163
181
|
}
|
|
164
182
|
getDropConstraint(param, schema, tableName) {
|
|
165
|
-
return `ALTER TABLE \`${
|
|
183
|
+
return `ALTER TABLE \`${tableName}\` DROP FOREIGN KEY \`${param.name}\`;`;
|
|
166
184
|
}
|
|
167
185
|
getAddUniqueConstraint(schema, tableName, colName) {
|
|
168
|
-
return `ALTER TABLE \`${
|
|
186
|
+
return `ALTER TABLE \`${tableName}\` ADD UNIQUE (\`${colName}\`);`;
|
|
169
187
|
}
|
|
170
188
|
getAlterTableDropNullInstruction(schema, tableName, colName, colDiff) {
|
|
171
|
-
return `ALTER TABLE \`${
|
|
189
|
+
return `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${colName}\` ${colDiff.colType} NULL;`;
|
|
172
190
|
}
|
|
173
191
|
getAlterTableDropNotNullInstruction(schema, tableName, colName, colDiff) {
|
|
174
|
-
return `ALTER TABLE \`${
|
|
192
|
+
return `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${colName}\` ${colDiff.colType} NOT NULL;`;
|
|
175
193
|
}
|
|
176
194
|
getAlterTableEnumInstruction(schema, tableName, colName, colDiff) {
|
|
177
195
|
const enumValues = colDiff.colChanges.enumItems
|
|
178
196
|
.map((item) => `'${item}'`)
|
|
179
197
|
.join(', ');
|
|
180
|
-
return `ALTER TABLE \`${
|
|
198
|
+
return `ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${colName}\` ENUM(${enumValues});`;
|
|
181
199
|
}
|
|
182
200
|
getDropTypeEnumInstruction(param, schema, tableName) {
|
|
183
201
|
return '';
|
|
@@ -224,7 +242,7 @@ class BunMysqlDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
224
242
|
return match[1].split(',').map((v) => v.trim().replace(/'/g, ''));
|
|
225
243
|
}
|
|
226
244
|
async index(tableName, options) {
|
|
227
|
-
const result = await this.sql.unsafe(`SHOW INDEX FROM \`${tableName}
|
|
245
|
+
const result = await this.sql.unsafe(`SHOW INDEX FROM \`${tableName}\``);
|
|
228
246
|
return result
|
|
229
247
|
.filter((row) => row.Key_name !== 'PRIMARY')
|
|
230
248
|
.map((row) => {
|
|
@@ -4,14 +4,14 @@ export declare class BunPgDriver extends BunDriverBase implements DriverInterfac
|
|
|
4
4
|
readonly dbType: "postgres";
|
|
5
5
|
constructor(options: ConnectionSettings);
|
|
6
6
|
protected getProtocol(): string;
|
|
7
|
-
|
|
7
|
+
getIdentifierQuote(): string;
|
|
8
8
|
protected buildAutoIncrementType(colDiff: ColDiff): string;
|
|
9
9
|
protected buildEnumType(schema: string | undefined, tableName: string, colDiff: ColDiff): {
|
|
10
10
|
beforeSql: string;
|
|
11
11
|
columnType: string;
|
|
12
12
|
};
|
|
13
13
|
protected appendReturningClause(sql: string, statement: Statement<any>): string;
|
|
14
|
-
protected handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number): Promise<{
|
|
14
|
+
protected handleInsertReturn(statement: Statement<any>, result: any, sql: string, startTime: number, context: any): Promise<{
|
|
15
15
|
query: any;
|
|
16
16
|
startTime: number;
|
|
17
17
|
sql: string;
|
|
@@ -29,7 +29,7 @@ class BunPgDriver extends bun_driver_base_1.BunDriverBase {
|
|
|
29
29
|
const cols = statement.columns.join(', ').replaceAll(`${statement.alias}.`, '');
|
|
30
30
|
return `${sql} RETURNING ${cols}`;
|
|
31
31
|
}
|
|
32
|
-
async handleInsertReturn(statement, result, sql, startTime) {
|
|
32
|
+
async handleInsertReturn(statement, result, sql, startTime, context) {
|
|
33
33
|
return {
|
|
34
34
|
query: { rows: Array.isArray(result) ? result : [] },
|
|
35
35
|
startTime,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ConnectionSettings, DriverInterface } from './driver.interface';
|
|
2
|
+
export type DriverType = 'postgres' | 'mysql';
|
|
3
|
+
export type DriverClass = new (options: ConnectionSettings) => DriverInterface;
|
|
4
|
+
export declare function getDriverType(): DriverType;
|
|
5
|
+
export declare function getDriverClass(type: DriverType): DriverClass;
|
|
6
|
+
export declare function getDefaultConnectionSettings(type: DriverType): {
|
|
7
|
+
host: string;
|
|
8
|
+
port: number;
|
|
9
|
+
database: string;
|
|
10
|
+
username: string;
|
|
11
|
+
password: string;
|
|
12
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDriverType = getDriverType;
|
|
4
|
+
exports.getDriverClass = getDriverClass;
|
|
5
|
+
exports.getDefaultConnectionSettings = getDefaultConnectionSettings;
|
|
6
|
+
const bun_pg_driver_1 = require("./bun-pg.driver");
|
|
7
|
+
const bun_mysql_driver_1 = require("./bun-mysql.driver");
|
|
8
|
+
function getDriverType() {
|
|
9
|
+
const envDriver = process.env.DB_DRIVER?.toLowerCase();
|
|
10
|
+
return envDriver === 'mysql' ? 'mysql' : 'postgres';
|
|
11
|
+
}
|
|
12
|
+
function getDriverClass(type) {
|
|
13
|
+
return type === 'mysql' ? bun_mysql_driver_1.BunMysqlDriver : bun_pg_driver_1.BunPgDriver;
|
|
14
|
+
}
|
|
15
|
+
function getDefaultConnectionSettings(type) {
|
|
16
|
+
if (type === 'mysql') {
|
|
17
|
+
return {
|
|
18
|
+
host: 'localhost',
|
|
19
|
+
port: 3306,
|
|
20
|
+
database: 'mysql_test',
|
|
21
|
+
username: 'root',
|
|
22
|
+
password: 'root',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
host: 'localhost',
|
|
27
|
+
port: 5433,
|
|
28
|
+
database: 'postgres',
|
|
29
|
+
username: 'postgres',
|
|
30
|
+
password: 'postgres',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -5,6 +5,7 @@ import { ValueObject } from "../common/value-object";
|
|
|
5
5
|
export interface DriverInterface {
|
|
6
6
|
connectionString: string;
|
|
7
7
|
readonly dbType: "postgres" | "mysql";
|
|
8
|
+
getIdentifierQuote(): string;
|
|
8
9
|
executeStatement(statement: Statement<any>): Promise<{
|
|
9
10
|
query: any;
|
|
10
11
|
startTime: number;
|
|
@@ -46,6 +47,7 @@ export type SnapshotConstraintInfo = {
|
|
|
46
47
|
};
|
|
47
48
|
export interface CacheSettings {
|
|
48
49
|
maxKeysPerTable?: number;
|
|
50
|
+
invalidateCacheOnWrite?: boolean;
|
|
49
51
|
}
|
|
50
52
|
export interface ConnectionSettings<T extends DriverInterface = DriverInterface> {
|
|
51
53
|
host?: string;
|