forge-sql-orm 2.0.30 → 2.1.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/README.md +1090 -81
- package/dist/ForgeSQLORM.js +1080 -60
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +1063 -60
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLAnalyseOperations.d.ts +1 -1
- package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLCacheOperations.d.ts +119 -0
- package/dist/core/ForgeSQLCacheOperations.d.ts.map +1 -0
- package/dist/core/ForgeSQLCrudOperations.d.ts +38 -22
- package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts +104 -13
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +243 -15
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/additionalActions.d.ts +42 -0
- package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -0
- package/dist/utils/cacheContextUtils.d.ts +123 -0
- package/dist/utils/cacheContextUtils.d.ts.map +1 -0
- package/dist/utils/cacheUtils.d.ts +56 -0
- package/dist/utils/cacheUtils.d.ts.map +1 -0
- package/dist/utils/sqlUtils.d.ts +8 -0
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts +46 -0
- package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts.map +1 -0
- package/dist/webtriggers/index.d.ts +1 -0
- package/dist/webtriggers/index.d.ts.map +1 -1
- package/package.json +15 -12
- package/src/core/ForgeSQLAnalyseOperations.ts +1 -1
- package/src/core/ForgeSQLCacheOperations.ts +195 -0
- package/src/core/ForgeSQLCrudOperations.ts +49 -40
- package/src/core/ForgeSQLORM.ts +443 -34
- package/src/core/ForgeSQLQueryBuilder.ts +291 -20
- package/src/index.ts +1 -1
- package/src/lib/drizzle/extensions/additionalActions.ts +548 -0
- package/src/lib/drizzle/extensions/types.d.ts +68 -10
- package/src/utils/cacheContextUtils.ts +210 -0
- package/src/utils/cacheUtils.ts +403 -0
- package/src/utils/sqlUtils.ts +16 -0
- package/src/webtriggers/clearCacheSchedulerTrigger.ts +79 -0
- package/src/webtriggers/index.ts +1 -0
- package/dist/lib/drizzle/extensions/selectAliased.d.ts +0 -9
- package/dist/lib/drizzle/extensions/selectAliased.d.ts.map +0 -1
- package/src/lib/drizzle/extensions/selectAliased.ts +0 -72
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MySqlRemoteDatabase,
|
|
3
|
+
MySqlRemotePreparedQueryHKT,
|
|
4
|
+
MySqlRemoteQueryResultHKT,
|
|
5
|
+
} from "drizzle-orm/mysql-proxy";
|
|
6
|
+
import type { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
7
|
+
import { applyFromDriverTransform, ForgeSqlOrmOptions, mapSelectFieldsWithAlias } from "../../..";
|
|
8
|
+
import { MySqlSelectBuilder } from "drizzle-orm/mysql-core";
|
|
9
|
+
import type { MySqlTable } from "drizzle-orm/mysql-core/table";
|
|
10
|
+
import {
|
|
11
|
+
MySqlDeleteBase,
|
|
12
|
+
MySqlInsertBuilder,
|
|
13
|
+
MySqlUpdateBuilder,
|
|
14
|
+
} from "drizzle-orm/mysql-core/query-builders";
|
|
15
|
+
import { clearCache, getFromCache, setCacheResult } from "../../../utils/cacheUtils";
|
|
16
|
+
import {
|
|
17
|
+
cacheApplicationContext,
|
|
18
|
+
evictLocalCacheQuery,
|
|
19
|
+
getQueryLocalCacheQuery,
|
|
20
|
+
saveTableIfInsideCacheContext,
|
|
21
|
+
saveQueryLocalCacheQuery,
|
|
22
|
+
} from "../../../utils/cacheContextUtils";
|
|
23
|
+
|
|
24
|
+
// Types for better type safety
|
|
25
|
+
type QueryBuilder = {
|
|
26
|
+
execute: (...args: any[]) => Promise<any>;
|
|
27
|
+
then?: (onfulfilled?: any, onrejected?: any) => Promise<any>;
|
|
28
|
+
toSQL?: () => any;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Determines whether cache should be cleared based on the error type.
|
|
33
|
+
* Only clears cache for errors that might indicate data consistency issues.
|
|
34
|
+
*
|
|
35
|
+
* @param error - The error that occurred during query execution
|
|
36
|
+
* @returns true if cache should be cleared, false otherwise
|
|
37
|
+
*/
|
|
38
|
+
function shouldClearCacheOnError(error: any): boolean {
|
|
39
|
+
// Don't clear cache for client-side errors (validation, etc.)
|
|
40
|
+
if (
|
|
41
|
+
error?.code === "VALIDATION_ERROR" ||
|
|
42
|
+
error?.code === "CONSTRAINT_ERROR" ||
|
|
43
|
+
(error?.message && /validation/i.exec(error.message))
|
|
44
|
+
) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Clear cache for database-level errors that might affect data consistency
|
|
49
|
+
if (
|
|
50
|
+
error?.code === "DEADLOCK" ||
|
|
51
|
+
error?.code === "LOCK_WAIT_TIMEOUT" ||
|
|
52
|
+
error?.code === "CONNECTION_ERROR" ||
|
|
53
|
+
(error?.message && /timeout/i.exec(error.message)) ||
|
|
54
|
+
(error?.message && /connection/i.exec(error.message))
|
|
55
|
+
) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// For unknown errors, be conservative and clear cache
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type SelectAliasedType = <TSelection extends SelectedFields>(
|
|
64
|
+
fields: TSelection,
|
|
65
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
66
|
+
|
|
67
|
+
export type SelectAliasedDistinctType = <TSelection extends SelectedFields>(
|
|
68
|
+
fields: TSelection,
|
|
69
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
70
|
+
|
|
71
|
+
export type SelectAliasedCacheableType = <TSelection extends SelectedFields>(
|
|
72
|
+
fields: TSelection,
|
|
73
|
+
cacheTtl?: number,
|
|
74
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
75
|
+
|
|
76
|
+
export type SelectAliasedDistinctCacheableType = <TSelection extends SelectedFields>(
|
|
77
|
+
fields: TSelection,
|
|
78
|
+
cacheTtl?: number,
|
|
79
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
80
|
+
|
|
81
|
+
export type InsertAndEvictCacheType = <TTable extends MySqlTable>(
|
|
82
|
+
table: TTable,
|
|
83
|
+
) => MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
84
|
+
export type UpdateAndEvictCacheType = <TTable extends MySqlTable>(
|
|
85
|
+
table: TTable,
|
|
86
|
+
) => MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
87
|
+
export type DeleteAndEvictCacheType = <TTable extends MySqlTable>(
|
|
88
|
+
table: TTable,
|
|
89
|
+
) => MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Handles successful query execution with cache management.
|
|
93
|
+
*
|
|
94
|
+
* @param rows - Query result rows
|
|
95
|
+
* @param onfulfilled - Success callback
|
|
96
|
+
* @param table - The table being modified
|
|
97
|
+
* @param options - ForgeSQL ORM options
|
|
98
|
+
* @param isCached - Whether to clear cache immediately
|
|
99
|
+
* @returns Promise with result
|
|
100
|
+
*/
|
|
101
|
+
async function handleSuccessfulExecution(
|
|
102
|
+
rows: unknown[],
|
|
103
|
+
onfulfilled: any,
|
|
104
|
+
table: MySqlTable,
|
|
105
|
+
options: ForgeSqlOrmOptions,
|
|
106
|
+
isCached: boolean,
|
|
107
|
+
): Promise<any> {
|
|
108
|
+
try {
|
|
109
|
+
await evictLocalCacheQuery(table, options);
|
|
110
|
+
await saveTableIfInsideCacheContext(table);
|
|
111
|
+
if (isCached && !cacheApplicationContext.getStore()) {
|
|
112
|
+
await clearCache(table, options);
|
|
113
|
+
}
|
|
114
|
+
const result = onfulfilled?.(rows);
|
|
115
|
+
return result;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// Only clear cache for certain types of errors
|
|
118
|
+
if (shouldClearCacheOnError(error)) {
|
|
119
|
+
await evictLocalCacheQuery(table, options);
|
|
120
|
+
if (isCached) {
|
|
121
|
+
await clearCache(table, options).catch(() => {
|
|
122
|
+
console.warn("Ignore cache clear errors");
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
await saveTableIfInsideCacheContext(table);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handles function calls on the wrapped builder.
|
|
134
|
+
*
|
|
135
|
+
* @param value - The function to call
|
|
136
|
+
* @param target - The target object
|
|
137
|
+
* @param args - Function arguments
|
|
138
|
+
* @param table - The table being modified
|
|
139
|
+
* @param options - ForgeSQL ORM options
|
|
140
|
+
* @param isCached - Whether to clear cache immediately
|
|
141
|
+
* @returns Function result or wrapped builder
|
|
142
|
+
*/
|
|
143
|
+
function handleFunctionCall(
|
|
144
|
+
value: Function,
|
|
145
|
+
target: any,
|
|
146
|
+
args: any[],
|
|
147
|
+
table: MySqlTable,
|
|
148
|
+
options: ForgeSqlOrmOptions,
|
|
149
|
+
isCached: boolean,
|
|
150
|
+
): any {
|
|
151
|
+
const result = value.apply(target, args);
|
|
152
|
+
if (typeof result === "object" && result !== null && "execute" in result) {
|
|
153
|
+
return wrapCacheEvictBuilder(result as QueryBuilder, table, options, isCached);
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Wraps a query builder with cache eviction functionality.
|
|
160
|
+
* Automatically clears cache after successful execution of insert/update/delete operations.
|
|
161
|
+
*
|
|
162
|
+
* @param rawBuilder - The original query builder to wrap
|
|
163
|
+
* @param table - The table being modified (used for cache eviction)
|
|
164
|
+
* @param options - ForgeSQL ORM options including cache configuration
|
|
165
|
+
* @param isCached - Whether to clear cache immediately
|
|
166
|
+
* @returns Wrapped query builder with cache eviction
|
|
167
|
+
*/
|
|
168
|
+
const wrapCacheEvictBuilder = <TTable extends MySqlTable>(
|
|
169
|
+
rawBuilder: QueryBuilder,
|
|
170
|
+
table: TTable,
|
|
171
|
+
options: ForgeSqlOrmOptions,
|
|
172
|
+
isCached: boolean,
|
|
173
|
+
): QueryBuilder => {
|
|
174
|
+
return new Proxy(rawBuilder, {
|
|
175
|
+
get(target, prop, receiver) {
|
|
176
|
+
if (prop === "then") {
|
|
177
|
+
return (onfulfilled?: any, onrejected?: any) =>
|
|
178
|
+
target
|
|
179
|
+
.execute()
|
|
180
|
+
.then(
|
|
181
|
+
(rows: unknown[]) =>
|
|
182
|
+
handleSuccessfulExecution(rows, onfulfilled, table, options, isCached),
|
|
183
|
+
onrejected,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const value = Reflect.get(target, prop, receiver);
|
|
188
|
+
|
|
189
|
+
if (typeof value === "function") {
|
|
190
|
+
return (...args: any[]) =>
|
|
191
|
+
handleFunctionCall(value, target, args, table, options, isCached);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return value;
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Creates an insert query builder that automatically evicts cache after execution.
|
|
201
|
+
*
|
|
202
|
+
* @param db - The database instance
|
|
203
|
+
* @param table - The table to insert into
|
|
204
|
+
* @param options - ForgeSQL ORM options
|
|
205
|
+
* @returns Insert query builder with cache eviction
|
|
206
|
+
*/
|
|
207
|
+
function insertAndEvictCacheBuilder<TTable extends MySqlTable>(
|
|
208
|
+
db: MySqlRemoteDatabase<any>,
|
|
209
|
+
table: TTable,
|
|
210
|
+
options: ForgeSqlOrmOptions,
|
|
211
|
+
isCached: boolean,
|
|
212
|
+
): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
|
|
213
|
+
const builder = db.insert(table);
|
|
214
|
+
return wrapCacheEvictBuilder(
|
|
215
|
+
builder as unknown as QueryBuilder,
|
|
216
|
+
table,
|
|
217
|
+
options,
|
|
218
|
+
isCached,
|
|
219
|
+
) as unknown as MySqlInsertBuilder<
|
|
220
|
+
TTable,
|
|
221
|
+
MySqlRemoteQueryResultHKT,
|
|
222
|
+
MySqlRemotePreparedQueryHKT
|
|
223
|
+
>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Creates an update query builder that automatically evicts cache after execution.
|
|
228
|
+
*
|
|
229
|
+
* @param db - The database instance
|
|
230
|
+
* @param table - The table to update
|
|
231
|
+
* @param options - ForgeSQL ORM options
|
|
232
|
+
* @returns Update query builder with cache eviction
|
|
233
|
+
*/
|
|
234
|
+
function updateAndEvictCacheBuilder<TTable extends MySqlTable>(
|
|
235
|
+
db: MySqlRemoteDatabase<any>,
|
|
236
|
+
table: TTable,
|
|
237
|
+
options: ForgeSqlOrmOptions,
|
|
238
|
+
isCached: boolean,
|
|
239
|
+
): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
|
|
240
|
+
const builder = db.update(table);
|
|
241
|
+
return wrapCacheEvictBuilder(
|
|
242
|
+
builder as unknown as QueryBuilder,
|
|
243
|
+
table,
|
|
244
|
+
options,
|
|
245
|
+
isCached,
|
|
246
|
+
) as unknown as MySqlUpdateBuilder<
|
|
247
|
+
TTable,
|
|
248
|
+
MySqlRemoteQueryResultHKT,
|
|
249
|
+
MySqlRemotePreparedQueryHKT
|
|
250
|
+
>;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Creates a delete query builder that automatically evicts cache after execution.
|
|
255
|
+
*
|
|
256
|
+
* @param db - The database instance
|
|
257
|
+
* @param table - The table to delete from
|
|
258
|
+
* @param options - ForgeSQL ORM options
|
|
259
|
+
* @returns Delete query builder with cache eviction
|
|
260
|
+
*/
|
|
261
|
+
function deleteAndEvictCacheBuilder<TTable extends MySqlTable>(
|
|
262
|
+
db: MySqlRemoteDatabase<any>,
|
|
263
|
+
table: TTable,
|
|
264
|
+
options: ForgeSqlOrmOptions,
|
|
265
|
+
isCached: boolean,
|
|
266
|
+
): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
|
|
267
|
+
const builder = db.delete(table);
|
|
268
|
+
return wrapCacheEvictBuilder(
|
|
269
|
+
builder as unknown as QueryBuilder,
|
|
270
|
+
table,
|
|
271
|
+
options,
|
|
272
|
+
isCached,
|
|
273
|
+
) as unknown as MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Handles cached query execution with proper error handling.
|
|
278
|
+
*
|
|
279
|
+
* @param target - The query target
|
|
280
|
+
* @param options - ForgeSQL ORM options
|
|
281
|
+
* @param cacheTtl - Cache TTL
|
|
282
|
+
* @param selections - Field selections
|
|
283
|
+
* @param aliasMap - Field alias mapping
|
|
284
|
+
* @param onfulfilled - Success callback
|
|
285
|
+
* @param onrejected - Error callback
|
|
286
|
+
* @returns Promise with cached result
|
|
287
|
+
*/
|
|
288
|
+
async function handleCachedQuery(
|
|
289
|
+
target: any,
|
|
290
|
+
options: ForgeSqlOrmOptions,
|
|
291
|
+
cacheTtl: number,
|
|
292
|
+
selections: any,
|
|
293
|
+
aliasMap: any,
|
|
294
|
+
onfulfilled?: any,
|
|
295
|
+
onrejected?: any,
|
|
296
|
+
): Promise<any> {
|
|
297
|
+
try {
|
|
298
|
+
const localCached = await getQueryLocalCacheQuery(target);
|
|
299
|
+
if (localCached) {
|
|
300
|
+
return onfulfilled?.(localCached);
|
|
301
|
+
}
|
|
302
|
+
const cacheResult = await getFromCache(target, options);
|
|
303
|
+
if (cacheResult) {
|
|
304
|
+
return onfulfilled?.(cacheResult);
|
|
305
|
+
}
|
|
306
|
+
const rows = await target.execute();
|
|
307
|
+
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
308
|
+
await saveQueryLocalCacheQuery(target, transformed);
|
|
309
|
+
await setCacheResult(target, options, transformed, cacheTtl).catch((cacheError) => {
|
|
310
|
+
// Log cache error but don't fail the query
|
|
311
|
+
console.warn("Cache set error:", cacheError);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return onfulfilled?.(transformed);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
return onrejected?.(error);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Handles non-cached query execution.
|
|
322
|
+
*
|
|
323
|
+
* @param target - The query target
|
|
324
|
+
* @param selections - Field selections
|
|
325
|
+
* @param aliasMap - Field alias mapping
|
|
326
|
+
* @param onfulfilled - Success callback
|
|
327
|
+
* @param onrejected - Error callback
|
|
328
|
+
* @returns Promise with transformed result
|
|
329
|
+
*/
|
|
330
|
+
async function handleNonCachedQuery(
|
|
331
|
+
target: any,
|
|
332
|
+
selections: any,
|
|
333
|
+
aliasMap: any,
|
|
334
|
+
onfulfilled?: any,
|
|
335
|
+
onrejected?: any,
|
|
336
|
+
): Promise<any> {
|
|
337
|
+
try {
|
|
338
|
+
const localCached = await getQueryLocalCacheQuery(target);
|
|
339
|
+
if (localCached) {
|
|
340
|
+
return onfulfilled?.(localCached);
|
|
341
|
+
}
|
|
342
|
+
const rows = await target.execute();
|
|
343
|
+
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
344
|
+
await saveQueryLocalCacheQuery(target, transformed);
|
|
345
|
+
return onfulfilled?.(transformed);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
return onrejected?.(error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Creates a select query builder with field aliasing and optional caching support.
|
|
353
|
+
*
|
|
354
|
+
* @param db - The database instance
|
|
355
|
+
* @param fields - The fields to select with aliases
|
|
356
|
+
* @param selectFn - Function to create the base select query
|
|
357
|
+
* @param useCache - Whether to enable caching for this query
|
|
358
|
+
* @param options - ForgeSQL ORM options
|
|
359
|
+
* @param cacheTtl - Optional cache TTL override
|
|
360
|
+
* @returns Select query builder with aliasing and optional caching
|
|
361
|
+
*/
|
|
362
|
+
function createAliasedSelectBuilder<TSelection extends SelectedFields>(
|
|
363
|
+
db: MySqlRemoteDatabase<any>,
|
|
364
|
+
fields: TSelection,
|
|
365
|
+
selectFn: (selections: any) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>,
|
|
366
|
+
useCache: boolean,
|
|
367
|
+
options: ForgeSqlOrmOptions,
|
|
368
|
+
cacheTtl?: number,
|
|
369
|
+
): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
|
|
370
|
+
const { selections, aliasMap } = mapSelectFieldsWithAlias(fields);
|
|
371
|
+
const builder = selectFn(selections);
|
|
372
|
+
|
|
373
|
+
const wrapBuilder = (rawBuilder: any): any => {
|
|
374
|
+
return new Proxy(rawBuilder, {
|
|
375
|
+
get(target, prop, receiver) {
|
|
376
|
+
if (prop === "execute") {
|
|
377
|
+
return async (...args: any[]) => {
|
|
378
|
+
const rows = await target.execute(...args);
|
|
379
|
+
return applyFromDriverTransform(rows, selections, aliasMap);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (prop === "then") {
|
|
384
|
+
return (onfulfilled?: any, onrejected?: any) => {
|
|
385
|
+
if (useCache) {
|
|
386
|
+
const ttl = cacheTtl ?? options.cacheTTL ?? 120;
|
|
387
|
+
return handleCachedQuery(
|
|
388
|
+
target,
|
|
389
|
+
options,
|
|
390
|
+
ttl,
|
|
391
|
+
selections,
|
|
392
|
+
aliasMap,
|
|
393
|
+
onfulfilled,
|
|
394
|
+
onrejected,
|
|
395
|
+
);
|
|
396
|
+
} else {
|
|
397
|
+
return handleNonCachedQuery(target, selections, aliasMap, onfulfilled, onrejected);
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const value = Reflect.get(target, prop, receiver);
|
|
403
|
+
|
|
404
|
+
if (typeof value === "function") {
|
|
405
|
+
return (...args: any[]) => {
|
|
406
|
+
const result = value.apply(target, args);
|
|
407
|
+
|
|
408
|
+
if (typeof result === "object" && result !== null && "execute" in result) {
|
|
409
|
+
return wrapBuilder(result);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return result;
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return value;
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
return wrapBuilder(builder);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Default options for better type safety
|
|
425
|
+
const DEFAULT_OPTIONS: ForgeSqlOrmOptions = {
|
|
426
|
+
logRawSqlQuery: false,
|
|
427
|
+
disableOptimisticLocking: false,
|
|
428
|
+
cacheTTL: 120,
|
|
429
|
+
cacheWrapTable: true,
|
|
430
|
+
cacheEntityQueryName: "sql",
|
|
431
|
+
cacheEntityExpirationName: "expiration",
|
|
432
|
+
cacheEntityDataName: "data",
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Patches a Drizzle database instance with additional methods for aliased selects and cache management.
|
|
437
|
+
*
|
|
438
|
+
* This function extends the database instance with:
|
|
439
|
+
* - selectAliased: Select with field aliasing support
|
|
440
|
+
* - selectAliasedDistinct: Select distinct with field aliasing support
|
|
441
|
+
* - selectAliasedCacheable: Select with field aliasing and caching
|
|
442
|
+
* - selectAliasedDistinctCacheable: Select distinct with field aliasing and caching
|
|
443
|
+
* - insertAndEvictCache: Insert operations that automatically evict cache
|
|
444
|
+
* - updateAndEvictCache: Update operations that automatically evict cache
|
|
445
|
+
* - deleteAndEvictCache: Delete operations that automatically evict cache
|
|
446
|
+
*
|
|
447
|
+
* @param db - The Drizzle database instance to patch
|
|
448
|
+
* @param options - Optional ForgeSQL ORM configuration
|
|
449
|
+
* @returns The patched database instance with additional methods
|
|
450
|
+
*/
|
|
451
|
+
export function patchDbWithSelectAliased(
|
|
452
|
+
db: MySqlRemoteDatabase<any>,
|
|
453
|
+
options?: ForgeSqlOrmOptions,
|
|
454
|
+
): MySqlRemoteDatabase<any> & {
|
|
455
|
+
selectAliased: SelectAliasedType;
|
|
456
|
+
selectAliasedDistinct: SelectAliasedDistinctType;
|
|
457
|
+
selectAliasedCacheable: SelectAliasedCacheableType;
|
|
458
|
+
selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
|
|
459
|
+
insertWithCacheContext: InsertAndEvictCacheType;
|
|
460
|
+
insertAndEvictCache: InsertAndEvictCacheType;
|
|
461
|
+
updateAndEvictCache: UpdateAndEvictCacheType;
|
|
462
|
+
updateWithCacheContext: UpdateAndEvictCacheType;
|
|
463
|
+
deleteAndEvictCache: DeleteAndEvictCacheType;
|
|
464
|
+
deleteWithCacheContext: DeleteAndEvictCacheType;
|
|
465
|
+
} {
|
|
466
|
+
const newOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
467
|
+
|
|
468
|
+
// Select aliased without cache
|
|
469
|
+
db.selectAliased = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
470
|
+
return createAliasedSelectBuilder(
|
|
471
|
+
db,
|
|
472
|
+
fields,
|
|
473
|
+
(selections) => db.select(selections),
|
|
474
|
+
false,
|
|
475
|
+
newOptions,
|
|
476
|
+
);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Select aliased distinct without cache
|
|
480
|
+
db.selectAliasedDistinct = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
481
|
+
return createAliasedSelectBuilder(
|
|
482
|
+
db,
|
|
483
|
+
fields,
|
|
484
|
+
(selections) => db.selectDistinct(selections),
|
|
485
|
+
false,
|
|
486
|
+
newOptions,
|
|
487
|
+
);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// Select aliased with cache
|
|
491
|
+
db.selectAliasedCacheable = function <TSelection extends SelectedFields>(
|
|
492
|
+
fields: TSelection,
|
|
493
|
+
cacheTtl?: number,
|
|
494
|
+
) {
|
|
495
|
+
return createAliasedSelectBuilder(
|
|
496
|
+
db,
|
|
497
|
+
fields,
|
|
498
|
+
(selections) => db.select(selections),
|
|
499
|
+
true,
|
|
500
|
+
newOptions,
|
|
501
|
+
cacheTtl,
|
|
502
|
+
);
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// Select aliased distinct with cache
|
|
506
|
+
db.selectAliasedDistinctCacheable = function <TSelection extends SelectedFields>(
|
|
507
|
+
fields: TSelection,
|
|
508
|
+
cacheTtl?: number,
|
|
509
|
+
) {
|
|
510
|
+
return createAliasedSelectBuilder(
|
|
511
|
+
db,
|
|
512
|
+
fields,
|
|
513
|
+
(selections) => db.selectDistinct(selections),
|
|
514
|
+
true,
|
|
515
|
+
newOptions,
|
|
516
|
+
cacheTtl,
|
|
517
|
+
);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// Insert with cache context support (participates in cache clearing when used within cache context)
|
|
521
|
+
db.insertWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
522
|
+
return insertAndEvictCacheBuilder(db, table, newOptions, false);
|
|
523
|
+
};
|
|
524
|
+
// Insert with cache eviction
|
|
525
|
+
db.insertAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
526
|
+
return insertAndEvictCacheBuilder(db, table, newOptions, true);
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// Update with cache context support (participates in cache clearing when used within cache context)
|
|
530
|
+
db.updateWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
531
|
+
return updateAndEvictCacheBuilder(db, table, newOptions, false);
|
|
532
|
+
};
|
|
533
|
+
// Update with cache eviction
|
|
534
|
+
db.updateAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
535
|
+
return updateAndEvictCacheBuilder(db, table, newOptions, true);
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// Delete with cache context support (participates in cache clearing when used within cache context)
|
|
539
|
+
db.deleteWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
540
|
+
return deleteAndEvictCacheBuilder(db, table, newOptions, false);
|
|
541
|
+
};
|
|
542
|
+
// Delete with cache eviction
|
|
543
|
+
db.deleteAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
544
|
+
return deleteAndEvictCacheBuilder(db, table, newOptions, true);
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
return db;
|
|
548
|
+
}
|
|
@@ -1,14 +1,72 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
DeleteAndEvictCacheType,
|
|
3
|
+
InsertAndEvictCacheType,
|
|
4
|
+
SelectAliasedCacheableType,
|
|
5
|
+
SelectAliasedDistinctCacheableType,
|
|
6
|
+
SelectAliasedDistinctType,
|
|
7
|
+
SelectAliasedType,
|
|
8
|
+
UpdateAndEvictCacheType,
|
|
9
|
+
} from "./additionalActions";
|
|
4
10
|
|
|
5
11
|
declare module "drizzle-orm/mysql-proxy" {
|
|
6
|
-
interface MySqlRemoteDatabase
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
interface MySqlRemoteDatabase {
|
|
13
|
+
/**
|
|
14
|
+
* Select with field aliasing support
|
|
15
|
+
*/
|
|
16
|
+
selectAliased: SelectAliasedType;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Select distinct with field aliasing support
|
|
20
|
+
*/
|
|
21
|
+
selectAliasedDistinct: SelectAliasedDistinctType;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Select with field aliasing and caching support
|
|
25
|
+
*/
|
|
26
|
+
selectAliasedCacheable: SelectAliasedCacheableType;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Select distinct with field aliasing and caching support
|
|
30
|
+
*/
|
|
31
|
+
selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Insert operation with cache context support.
|
|
35
|
+
* Participates in cache clearing when used within executeWithCacheContext().
|
|
36
|
+
* Does not immediately clear cache, but marks table for batch cache clearing.
|
|
37
|
+
*/
|
|
38
|
+
insertWithCacheContext: InsertAndEvictCacheType;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Insert operation that automatically evicts cache immediately after execution.
|
|
42
|
+
* Always clears cache for the affected table, regardless of cache context.
|
|
43
|
+
*/
|
|
44
|
+
insertAndEvictCache: InsertAndEvictCacheType;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update operation with cache context support.
|
|
48
|
+
* Participates in cache clearing when used within executeWithCacheContext().
|
|
49
|
+
* Does not immediately clear cache, but marks table for batch cache clearing.
|
|
50
|
+
*/
|
|
51
|
+
updateWithCacheContext: UpdateAndEvictCacheType;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Update operation that automatically evicts cache immediately after execution.
|
|
55
|
+
* Always clears cache for the affected table, regardless of cache context.
|
|
56
|
+
*/
|
|
57
|
+
updateAndEvictCache: UpdateAndEvictCacheType;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Delete operation with cache context support.
|
|
61
|
+
* Participates in cache clearing when used within executeWithCacheContext().
|
|
62
|
+
* Does not immediately clear cache, but marks table for batch cache clearing.
|
|
63
|
+
*/
|
|
64
|
+
deleteWithCacheContext: DeleteAndEvictCacheType;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Delete operation that automatically evicts cache immediately after execution.
|
|
68
|
+
* Always clears cache for the affected table, regardless of cache context.
|
|
69
|
+
*/
|
|
70
|
+
deleteAndEvictCache: DeleteAndEvictCacheType;
|
|
13
71
|
}
|
|
14
72
|
}
|