forge-sql-orm 2.0.30 → 2.1.1
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 +1410 -81
- package/dist/ForgeSQLORM.js +1456 -60
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +1440 -61
- 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 +248 -13
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +394 -19
- 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 +90 -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 +743 -34
- package/src/core/ForgeSQLQueryBuilder.ts +456 -20
- package/src/index.ts +1 -1
- package/src/lib/drizzle/extensions/additionalActions.ts +852 -0
- package/src/lib/drizzle/extensions/types.d.ts +99 -10
- package/src/utils/cacheContextUtils.ts +212 -0
- package/src/utils/cacheUtils.ts +403 -0
- package/src/utils/sqlUtils.ts +42 -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,852 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MySqlRawQueryResult,
|
|
3
|
+
MySqlRemoteDatabase,
|
|
4
|
+
MySqlRemotePreparedQueryHKT,
|
|
5
|
+
MySqlRemoteQueryResultHKT,
|
|
6
|
+
} from "drizzle-orm/mysql-proxy";
|
|
7
|
+
|
|
8
|
+
import {SelectedFields} from "drizzle-orm/mysql-core/query-builders/select.types";
|
|
9
|
+
import {applyFromDriverTransform, ForgeSqlOrmOptions, mapSelectFieldsWithAlias} from "../../..";
|
|
10
|
+
import {MySqlSelectBase, MySqlSelectBuilder} from "drizzle-orm/mysql-core";
|
|
11
|
+
import type {MySqlTable} from "drizzle-orm/mysql-core/table";
|
|
12
|
+
import {MySqlDeleteBase, MySqlInsertBuilder, MySqlUpdateBuilder,} from "drizzle-orm/mysql-core/query-builders";
|
|
13
|
+
import {clearCache, getFromCache, setCacheResult} from "../../../utils/cacheUtils";
|
|
14
|
+
import {
|
|
15
|
+
cacheApplicationContext,
|
|
16
|
+
evictLocalCacheQuery,
|
|
17
|
+
getQueryLocalCacheQuery,
|
|
18
|
+
saveQueryLocalCacheQuery,
|
|
19
|
+
saveTableIfInsideCacheContext,
|
|
20
|
+
} from "../../../utils/cacheContextUtils";
|
|
21
|
+
import {BuildQueryConfig, isSQLWrapper, SQLWrapper} from "drizzle-orm/sql/sql";
|
|
22
|
+
import type {MySqlQueryResultKind} from "drizzle-orm/mysql-core/session";
|
|
23
|
+
import {getTableColumns, Query} from "drizzle-orm";
|
|
24
|
+
import {MySqlDialect} from "drizzle-orm/mysql-core/dialect";
|
|
25
|
+
import type {GetSelectTableName, GetSelectTableSelection} from "drizzle-orm/query-builders/select.types";
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// TYPES AND INTERFACES
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Base interface for query builders that can be executed
|
|
33
|
+
*/
|
|
34
|
+
interface QueryBuilder {
|
|
35
|
+
execute: (...args: any[]) => Promise<any>;
|
|
36
|
+
then?: (onfulfilled?: any, onrejected?: any) => Promise<any>;
|
|
37
|
+
toSQL?: () => any;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Error codes that should not trigger cache clearing
|
|
42
|
+
*/
|
|
43
|
+
const NON_CACHE_CLEARING_ERROR_CODES = [
|
|
44
|
+
"VALIDATION_ERROR",
|
|
45
|
+
"CONSTRAINT_ERROR"
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Error codes that should trigger cache clearing
|
|
50
|
+
*/
|
|
51
|
+
const CACHE_CLEARING_ERROR_CODES = [
|
|
52
|
+
"DEADLOCK",
|
|
53
|
+
"LOCK_WAIT_TIMEOUT",
|
|
54
|
+
"CONNECTION_ERROR"
|
|
55
|
+
] as const;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Error message patterns that should not trigger cache clearing
|
|
59
|
+
*/
|
|
60
|
+
const NON_CACHE_CLEARING_PATTERNS = [
|
|
61
|
+
/validation/i,
|
|
62
|
+
/constraint/i
|
|
63
|
+
] as const;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Error message patterns that should trigger cache clearing
|
|
67
|
+
*/
|
|
68
|
+
const CACHE_CLEARING_PATTERNS = [
|
|
69
|
+
/timeout/i,
|
|
70
|
+
/connection/i
|
|
71
|
+
] as const;
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// CACHE MANAGEMENT UTILITIES
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Determines whether cache should be cleared based on the error type.
|
|
79
|
+
* Only clears cache for errors that might indicate data consistency issues.
|
|
80
|
+
*
|
|
81
|
+
* @param error - The error that occurred during query execution
|
|
82
|
+
* @returns true if cache should be cleared, false otherwise
|
|
83
|
+
*/
|
|
84
|
+
function shouldClearCacheOnError(error: any): boolean {
|
|
85
|
+
// Don't clear cache for client-side errors (validation, etc.)
|
|
86
|
+
if (error?.code && NON_CACHE_CLEARING_ERROR_CODES.includes(error.code)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (error?.message && NON_CACHE_CLEARING_PATTERNS.some(pattern => pattern.test(error.message))) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Clear cache for database-level errors that might affect data consistency
|
|
95
|
+
if (error?.code && CACHE_CLEARING_ERROR_CODES.includes(error.code)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (error?.message && CACHE_CLEARING_PATTERNS.some(pattern => pattern.test(error.message))) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// For unknown errors, be conservative and clear cache
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// EXPORTED TYPES
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Type for select queries with field aliasing
|
|
113
|
+
*/
|
|
114
|
+
export type SelectAliasedType = <TSelection extends SelectedFields>(
|
|
115
|
+
fields: TSelection,
|
|
116
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Type for select distinct queries with field aliasing
|
|
120
|
+
*/
|
|
121
|
+
export type SelectAliasedDistinctType = <TSelection extends SelectedFields>(
|
|
122
|
+
fields: TSelection,
|
|
123
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Type for select queries with field aliasing and caching
|
|
127
|
+
*/
|
|
128
|
+
export type SelectAliasedCacheableType = <TSelection extends SelectedFields>(
|
|
129
|
+
fields: TSelection,
|
|
130
|
+
cacheTtl?: number,
|
|
131
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Type for select distinct queries with field aliasing and caching
|
|
135
|
+
*/
|
|
136
|
+
export type SelectAliasedDistinctCacheableType = <TSelection extends SelectedFields>(
|
|
137
|
+
fields: TSelection,
|
|
138
|
+
cacheTtl?: number,
|
|
139
|
+
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Type for select queries from table with field aliasing
|
|
143
|
+
*/
|
|
144
|
+
export type SelectAllFromAliasedType = <T extends MySqlTable>(
|
|
145
|
+
table: T,
|
|
146
|
+
) => MySqlSelectBase<GetSelectTableName<T>, T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"], T["_"]["columns"] extends undefined ? "single" : "partial", MySqlRemotePreparedQueryHKT, GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {}, false, never, any>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Type for select distinct queries from table with field aliasing
|
|
150
|
+
*/
|
|
151
|
+
export type SelectAllDistinctFromAliasedType = <T extends MySqlTable>(
|
|
152
|
+
table: T,
|
|
153
|
+
) => MySqlSelectBase<GetSelectTableName<T>, T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"], T["_"]["columns"] extends undefined ? "single" : "partial", MySqlRemotePreparedQueryHKT, GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {}, false, never, any>;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Type for select queries from table with field aliasing and caching
|
|
157
|
+
*/
|
|
158
|
+
export type SelectAllFromCacheableAliasedType = <T extends MySqlTable>(
|
|
159
|
+
table: T,
|
|
160
|
+
cacheTtl?: number,
|
|
161
|
+
) => MySqlSelectBase<GetSelectTableName<T>, T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"], T["_"]["columns"] extends undefined ? "single" : "partial", MySqlRemotePreparedQueryHKT, GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {}, false, never, any>;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Type for select distinct queries from table with field aliasing and caching
|
|
165
|
+
*/
|
|
166
|
+
export type SelectAllDistinctFromCacheableAliasedType = <T extends MySqlTable>(
|
|
167
|
+
table: T,
|
|
168
|
+
cacheTtl?: number,
|
|
169
|
+
) => MySqlSelectBase<GetSelectTableName<T>, T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"], T["_"]["columns"] extends undefined ? "single" : "partial", MySqlRemotePreparedQueryHKT, GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {}, false, never, any>;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Type for executing raw SQL queries with local cache
|
|
173
|
+
*/
|
|
174
|
+
export type ExecuteQuery = (query: SQLWrapper | string) => Promise<MySqlQueryResultKind<MySqlRemoteQueryResultHKT, unknown>>;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Type for executing raw SQL queries with local and global cache
|
|
178
|
+
*/
|
|
179
|
+
export type ExecuteQueryCacheable = (query: SQLWrapper | string, cacheTtl?: number) => Promise<MySqlQueryResultKind<MySqlRemoteQueryResultHKT, unknown>>;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Type for insert operations with cache eviction
|
|
183
|
+
*/
|
|
184
|
+
export type InsertAndEvictCacheType = <TTable extends MySqlTable>(
|
|
185
|
+
table: TTable,
|
|
186
|
+
) => MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Type for update operations with cache eviction
|
|
190
|
+
*/
|
|
191
|
+
export type UpdateAndEvictCacheType = <TTable extends MySqlTable>(
|
|
192
|
+
table: TTable,
|
|
193
|
+
) => MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Type for delete operations with cache eviction
|
|
197
|
+
*/
|
|
198
|
+
export type DeleteAndEvictCacheType = <TTable extends MySqlTable>(
|
|
199
|
+
table: TTable,
|
|
200
|
+
) => MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Handles successful query execution with cache management.
|
|
204
|
+
*
|
|
205
|
+
* @param rows - Query result rows
|
|
206
|
+
* @param onfulfilled - Success callback
|
|
207
|
+
* @param table - The table being modified
|
|
208
|
+
* @param options - ForgeSQL ORM options
|
|
209
|
+
* @param isCached - Whether to clear cache immediately
|
|
210
|
+
* @returns Promise with result
|
|
211
|
+
*/
|
|
212
|
+
async function handleSuccessfulExecution(
|
|
213
|
+
rows: unknown[],
|
|
214
|
+
onfulfilled: any,
|
|
215
|
+
table: MySqlTable,
|
|
216
|
+
options: ForgeSqlOrmOptions,
|
|
217
|
+
isCached: boolean,
|
|
218
|
+
): Promise<any> {
|
|
219
|
+
try {
|
|
220
|
+
await evictLocalCacheQuery(table, options);
|
|
221
|
+
await saveTableIfInsideCacheContext(table);
|
|
222
|
+
if (isCached && !cacheApplicationContext.getStore()) {
|
|
223
|
+
await clearCache(table, options);
|
|
224
|
+
}
|
|
225
|
+
const result = onfulfilled?.(rows);
|
|
226
|
+
return result;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
// Only clear cache for certain types of errors
|
|
229
|
+
if (shouldClearCacheOnError(error)) {
|
|
230
|
+
await evictLocalCacheQuery(table, options);
|
|
231
|
+
if (isCached) {
|
|
232
|
+
await clearCache(table, options).catch(() => {
|
|
233
|
+
console.warn("Ignore cache clear errors");
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
await saveTableIfInsideCacheContext(table);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Handles function calls on the wrapped builder.
|
|
245
|
+
*
|
|
246
|
+
* @param value - The function to call
|
|
247
|
+
* @param target - The target object
|
|
248
|
+
* @param args - Function arguments
|
|
249
|
+
* @param table - The table being modified
|
|
250
|
+
* @param options - ForgeSQL ORM options
|
|
251
|
+
* @param isCached - Whether to clear cache immediately
|
|
252
|
+
* @returns Function result or wrapped builder
|
|
253
|
+
*/
|
|
254
|
+
function handleFunctionCall(
|
|
255
|
+
value: Function,
|
|
256
|
+
target: any,
|
|
257
|
+
args: any[],
|
|
258
|
+
table: MySqlTable,
|
|
259
|
+
options: ForgeSqlOrmOptions,
|
|
260
|
+
isCached: boolean,
|
|
261
|
+
): any {
|
|
262
|
+
const result = value.apply(target, args);
|
|
263
|
+
if (typeof result === "object" && result !== null && "execute" in result) {
|
|
264
|
+
return wrapCacheEvictBuilder(result as QueryBuilder, table, options, isCached);
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Wraps a query builder with cache eviction functionality.
|
|
271
|
+
* Automatically clears cache after successful execution of insert/update/delete operations.
|
|
272
|
+
*
|
|
273
|
+
* @param rawBuilder - The original query builder to wrap
|
|
274
|
+
* @param table - The table being modified (used for cache eviction)
|
|
275
|
+
* @param options - ForgeSQL ORM options including cache configuration
|
|
276
|
+
* @param isCached - Whether to clear cache immediately
|
|
277
|
+
* @returns Wrapped query builder with cache eviction
|
|
278
|
+
*/
|
|
279
|
+
const wrapCacheEvictBuilder = <TTable extends MySqlTable>(
|
|
280
|
+
rawBuilder: QueryBuilder,
|
|
281
|
+
table: TTable,
|
|
282
|
+
options: ForgeSqlOrmOptions,
|
|
283
|
+
isCached: boolean,
|
|
284
|
+
): QueryBuilder => {
|
|
285
|
+
return new Proxy(rawBuilder, {
|
|
286
|
+
get(target, prop, receiver) {
|
|
287
|
+
if (prop === "then") {
|
|
288
|
+
return (onfulfilled?: any, onrejected?: any) =>
|
|
289
|
+
target
|
|
290
|
+
.execute()
|
|
291
|
+
.then(
|
|
292
|
+
(rows: unknown[]) =>
|
|
293
|
+
handleSuccessfulExecution(rows, onfulfilled, table, options, isCached),
|
|
294
|
+
onrejected,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const value = Reflect.get(target, prop, receiver);
|
|
299
|
+
|
|
300
|
+
if (typeof value === "function") {
|
|
301
|
+
return (...args: any[]) =>
|
|
302
|
+
handleFunctionCall(value, target, args, table, options, isCached);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return value;
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Creates an insert query builder that automatically evicts cache after execution.
|
|
312
|
+
*
|
|
313
|
+
* @param db - The database instance
|
|
314
|
+
* @param table - The table to insert into
|
|
315
|
+
* @param options - ForgeSQL ORM options
|
|
316
|
+
* @returns Insert query builder with cache eviction
|
|
317
|
+
*/
|
|
318
|
+
function insertAndEvictCacheBuilder<TTable extends MySqlTable>(
|
|
319
|
+
db: MySqlRemoteDatabase<any>,
|
|
320
|
+
table: TTable,
|
|
321
|
+
options: ForgeSqlOrmOptions,
|
|
322
|
+
isCached: boolean,
|
|
323
|
+
): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
|
|
324
|
+
const builder = db.insert(table);
|
|
325
|
+
return wrapCacheEvictBuilder(
|
|
326
|
+
builder as unknown as QueryBuilder,
|
|
327
|
+
table,
|
|
328
|
+
options,
|
|
329
|
+
isCached,
|
|
330
|
+
) as unknown as MySqlInsertBuilder<
|
|
331
|
+
TTable,
|
|
332
|
+
MySqlRemoteQueryResultHKT,
|
|
333
|
+
MySqlRemotePreparedQueryHKT
|
|
334
|
+
>;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Creates an update query builder that automatically evicts cache after execution.
|
|
339
|
+
*
|
|
340
|
+
* @param db - The database instance
|
|
341
|
+
* @param table - The table to update
|
|
342
|
+
* @param options - ForgeSQL ORM options
|
|
343
|
+
* @returns Update query builder with cache eviction
|
|
344
|
+
*/
|
|
345
|
+
function updateAndEvictCacheBuilder<TTable extends MySqlTable>(
|
|
346
|
+
db: MySqlRemoteDatabase<any>,
|
|
347
|
+
table: TTable,
|
|
348
|
+
options: ForgeSqlOrmOptions,
|
|
349
|
+
isCached: boolean,
|
|
350
|
+
): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
|
|
351
|
+
const builder = db.update(table);
|
|
352
|
+
return wrapCacheEvictBuilder(
|
|
353
|
+
builder as unknown as QueryBuilder,
|
|
354
|
+
table,
|
|
355
|
+
options,
|
|
356
|
+
isCached,
|
|
357
|
+
) as unknown as MySqlUpdateBuilder<
|
|
358
|
+
TTable,
|
|
359
|
+
MySqlRemoteQueryResultHKT,
|
|
360
|
+
MySqlRemotePreparedQueryHKT
|
|
361
|
+
>;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Creates a delete query builder that automatically evicts cache after execution.
|
|
366
|
+
*
|
|
367
|
+
* @param db - The database instance
|
|
368
|
+
* @param table - The table to delete from
|
|
369
|
+
* @param options - ForgeSQL ORM options
|
|
370
|
+
* @returns Delete query builder with cache eviction
|
|
371
|
+
*/
|
|
372
|
+
function deleteAndEvictCacheBuilder<TTable extends MySqlTable>(
|
|
373
|
+
db: MySqlRemoteDatabase<any>,
|
|
374
|
+
table: TTable,
|
|
375
|
+
options: ForgeSqlOrmOptions,
|
|
376
|
+
isCached: boolean,
|
|
377
|
+
): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
|
|
378
|
+
const builder = db.delete(table);
|
|
379
|
+
return wrapCacheEvictBuilder(
|
|
380
|
+
builder as unknown as QueryBuilder,
|
|
381
|
+
table,
|
|
382
|
+
options,
|
|
383
|
+
isCached,
|
|
384
|
+
) as unknown as MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Handles cached query execution with proper error handling.
|
|
389
|
+
*
|
|
390
|
+
* @param target - The query target
|
|
391
|
+
* @param options - ForgeSQL ORM options
|
|
392
|
+
* @param cacheTtl - Cache TTL
|
|
393
|
+
* @param selections - Field selections
|
|
394
|
+
* @param aliasMap - Field alias mapping
|
|
395
|
+
* @param onfulfilled - Success callback
|
|
396
|
+
* @param onrejected - Error callback
|
|
397
|
+
* @returns Promise with cached result
|
|
398
|
+
*/
|
|
399
|
+
async function handleCachedQuery(
|
|
400
|
+
target: any,
|
|
401
|
+
options: ForgeSqlOrmOptions,
|
|
402
|
+
cacheTtl: number,
|
|
403
|
+
selections: any,
|
|
404
|
+
aliasMap: any,
|
|
405
|
+
onfulfilled?: any,
|
|
406
|
+
onrejected?: any,
|
|
407
|
+
): Promise<any> {
|
|
408
|
+
try {
|
|
409
|
+
const localCached = await getQueryLocalCacheQuery(target);
|
|
410
|
+
if (localCached) {
|
|
411
|
+
return onfulfilled?.(localCached);
|
|
412
|
+
}
|
|
413
|
+
const cacheResult = await getFromCache(target, options);
|
|
414
|
+
if (cacheResult) {
|
|
415
|
+
return onfulfilled?.(cacheResult);
|
|
416
|
+
}
|
|
417
|
+
const rows = await target.execute();
|
|
418
|
+
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
419
|
+
await saveQueryLocalCacheQuery(target, transformed);
|
|
420
|
+
await setCacheResult(target, options, transformed, cacheTtl).catch((cacheError) => {
|
|
421
|
+
// Log cache error but don't fail the query
|
|
422
|
+
console.warn("Cache set error:", cacheError);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
return onfulfilled?.(transformed);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
return onrejected?.(error);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Handles non-cached query execution.
|
|
433
|
+
*
|
|
434
|
+
* @param target - The query target
|
|
435
|
+
* @param selections - Field selections
|
|
436
|
+
* @param aliasMap - Field alias mapping
|
|
437
|
+
* @param onfulfilled - Success callback
|
|
438
|
+
* @param onrejected - Error callback
|
|
439
|
+
* @returns Promise with transformed result
|
|
440
|
+
*/
|
|
441
|
+
async function handleNonCachedQuery(
|
|
442
|
+
target: any,
|
|
443
|
+
selections: any,
|
|
444
|
+
aliasMap: any,
|
|
445
|
+
onfulfilled?: any,
|
|
446
|
+
onrejected?: any,
|
|
447
|
+
): Promise<any> {
|
|
448
|
+
try {
|
|
449
|
+
const localCached = await getQueryLocalCacheQuery(target);
|
|
450
|
+
if (localCached) {
|
|
451
|
+
return onfulfilled?.(localCached);
|
|
452
|
+
}
|
|
453
|
+
const rows = await target.execute();
|
|
454
|
+
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
455
|
+
await saveQueryLocalCacheQuery(target, transformed);
|
|
456
|
+
return onfulfilled?.(transformed);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
return onrejected?.(error);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Creates a select query builder with field aliasing and optional caching support.
|
|
464
|
+
*
|
|
465
|
+
* @param db - The database instance
|
|
466
|
+
* @param fields - The fields to select with aliases
|
|
467
|
+
* @param selectFn - Function to create the base select query
|
|
468
|
+
* @param useCache - Whether to enable caching for this query
|
|
469
|
+
* @param options - ForgeSQL ORM options
|
|
470
|
+
* @param cacheTtl - Optional cache TTL override
|
|
471
|
+
* @returns Select query builder with aliasing and optional caching
|
|
472
|
+
*/
|
|
473
|
+
function createAliasedSelectBuilder<TSelection extends SelectedFields>(
|
|
474
|
+
db: MySqlRemoteDatabase<any>,
|
|
475
|
+
fields: TSelection,
|
|
476
|
+
selectFn: (selections: any) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>,
|
|
477
|
+
useCache: boolean,
|
|
478
|
+
options: ForgeSqlOrmOptions,
|
|
479
|
+
cacheTtl?: number,
|
|
480
|
+
): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
|
|
481
|
+
const { selections, aliasMap } = mapSelectFieldsWithAlias(fields);
|
|
482
|
+
const builder = selectFn(selections);
|
|
483
|
+
|
|
484
|
+
const wrapBuilder = (rawBuilder: any): any => {
|
|
485
|
+
return new Proxy(rawBuilder, {
|
|
486
|
+
get(target, prop, receiver) {
|
|
487
|
+
if (prop === "execute") {
|
|
488
|
+
return async (...args: any[]) => {
|
|
489
|
+
const rows = await target.execute(...args);
|
|
490
|
+
return applyFromDriverTransform(rows, selections, aliasMap);
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (prop === "then") {
|
|
495
|
+
return (onfulfilled?: any, onrejected?: any) => {
|
|
496
|
+
if (useCache) {
|
|
497
|
+
const ttl = cacheTtl ?? options.cacheTTL ?? 120;
|
|
498
|
+
return handleCachedQuery(
|
|
499
|
+
target,
|
|
500
|
+
options,
|
|
501
|
+
ttl,
|
|
502
|
+
selections,
|
|
503
|
+
aliasMap,
|
|
504
|
+
onfulfilled,
|
|
505
|
+
onrejected,
|
|
506
|
+
);
|
|
507
|
+
} else {
|
|
508
|
+
return handleNonCachedQuery(target, selections, aliasMap, onfulfilled, onrejected);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const value = Reflect.get(target, prop, receiver);
|
|
514
|
+
|
|
515
|
+
if (typeof value === "function") {
|
|
516
|
+
return (...args: any[]) => {
|
|
517
|
+
const result = value.apply(target, args);
|
|
518
|
+
|
|
519
|
+
if (typeof result === "object" && result !== null && "execute" in result) {
|
|
520
|
+
return wrapBuilder(result);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return result;
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return value;
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
return wrapBuilder(builder);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ============================================================================
|
|
536
|
+
// CONFIGURATION AND CONSTANTS
|
|
537
|
+
// ============================================================================
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Default options for ForgeSQL ORM
|
|
541
|
+
*/
|
|
542
|
+
const DEFAULT_OPTIONS: ForgeSqlOrmOptions = {
|
|
543
|
+
logRawSqlQuery: false,
|
|
544
|
+
disableOptimisticLocking: false,
|
|
545
|
+
cacheTTL: 120,
|
|
546
|
+
cacheWrapTable: true,
|
|
547
|
+
cacheEntityQueryName: "sql",
|
|
548
|
+
cacheEntityExpirationName: "expiration",
|
|
549
|
+
cacheEntityDataName: "data",
|
|
550
|
+
} as const;
|
|
551
|
+
|
|
552
|
+
// ============================================================================
|
|
553
|
+
// QUERY BUILDER FACTORIES
|
|
554
|
+
// ============================================================================
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Creates a raw SQL query executor with caching support
|
|
559
|
+
*/
|
|
560
|
+
function createRawQueryExecutor(
|
|
561
|
+
db: MySqlRemoteDatabase<any>,
|
|
562
|
+
options: ForgeSqlOrmOptions,
|
|
563
|
+
useGlobalCache: boolean = false
|
|
564
|
+
) {
|
|
565
|
+
return async function <T extends { [column: string]: any }>(
|
|
566
|
+
query: SQLWrapper | string,
|
|
567
|
+
cacheTtl?: number
|
|
568
|
+
): Promise<MySqlRawQueryResult> {
|
|
569
|
+
let sql: Query;
|
|
570
|
+
|
|
571
|
+
if (isSQLWrapper(query)) {
|
|
572
|
+
const sqlWrapper = query as SQLWrapper;
|
|
573
|
+
sql = sqlWrapper.getSQL()
|
|
574
|
+
.toQuery(((db as unknown as { dialect: MySqlDialect }).dialect) as unknown as BuildQueryConfig);
|
|
575
|
+
} else {
|
|
576
|
+
sql = {
|
|
577
|
+
sql: query,
|
|
578
|
+
params: []
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Check local cache first
|
|
583
|
+
const localCacheResult = await getQueryLocalCacheQuery(sql);
|
|
584
|
+
if (localCacheResult) {
|
|
585
|
+
return localCacheResult as MySqlRawQueryResult;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Check global cache if enabled
|
|
589
|
+
if (useGlobalCache) {
|
|
590
|
+
const cacheResult = await getFromCache({ toSQL: () => sql }, options);
|
|
591
|
+
if (cacheResult) {
|
|
592
|
+
return cacheResult as MySqlRawQueryResult;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Execute query
|
|
597
|
+
const results = await db.execute<T>(query);
|
|
598
|
+
|
|
599
|
+
// Save to local cache
|
|
600
|
+
await saveQueryLocalCacheQuery(sql, results);
|
|
601
|
+
|
|
602
|
+
// Save to global cache if enabled
|
|
603
|
+
if (useGlobalCache) {
|
|
604
|
+
await setCacheResult(
|
|
605
|
+
{ toSQL: () => sql },
|
|
606
|
+
options,
|
|
607
|
+
results,
|
|
608
|
+
cacheTtl ?? options.cacheTTL ?? 120
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return results;
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ============================================================================
|
|
617
|
+
// MAIN PATCH FUNCTION
|
|
618
|
+
// ============================================================================
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Patches a Drizzle database instance with additional methods for aliased selects and cache management.
|
|
622
|
+
*
|
|
623
|
+
* This function extends the database instance with:
|
|
624
|
+
* - selectAliased: Select with field aliasing support
|
|
625
|
+
* - selectAliasedDistinct: Select distinct with field aliasing support
|
|
626
|
+
* - selectAliasedCacheable: Select with field aliasing and caching
|
|
627
|
+
* - selectAliasedDistinctCacheable: Select distinct with field aliasing and caching
|
|
628
|
+
* - insertAndEvictCache: Insert operations that automatically evict cache
|
|
629
|
+
* - updateAndEvictCache: Update operations that automatically evict cache
|
|
630
|
+
* - deleteAndEvictCache: Delete operations that automatically evict cache
|
|
631
|
+
*
|
|
632
|
+
* @param db - The Drizzle database instance to patch
|
|
633
|
+
* @param options - Optional ForgeSQL ORM configuration
|
|
634
|
+
* @returns The patched database instance with additional methods
|
|
635
|
+
*/
|
|
636
|
+
export function patchDbWithSelectAliased(
|
|
637
|
+
db: MySqlRemoteDatabase<any>,
|
|
638
|
+
options?: ForgeSqlOrmOptions,
|
|
639
|
+
): MySqlRemoteDatabase<any> & {
|
|
640
|
+
selectAliased: SelectAliasedType;
|
|
641
|
+
selectAliasedDistinct: SelectAliasedDistinctType;
|
|
642
|
+
selectAliasedCacheable: SelectAliasedCacheableType;
|
|
643
|
+
selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
|
|
644
|
+
insertWithCacheContext: InsertAndEvictCacheType;
|
|
645
|
+
insertAndEvictCache: InsertAndEvictCacheType;
|
|
646
|
+
updateAndEvictCache: UpdateAndEvictCacheType;
|
|
647
|
+
updateWithCacheContext: UpdateAndEvictCacheType;
|
|
648
|
+
deleteAndEvictCache: DeleteAndEvictCacheType;
|
|
649
|
+
deleteWithCacheContext: DeleteAndEvictCacheType;
|
|
650
|
+
} {
|
|
651
|
+
const newOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
652
|
+
|
|
653
|
+
// ============================================================================
|
|
654
|
+
// SELECT METHODS WITH FIELD ALIASING
|
|
655
|
+
// ============================================================================
|
|
656
|
+
|
|
657
|
+
// Select aliased without cache
|
|
658
|
+
db.selectAliased = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
659
|
+
return createAliasedSelectBuilder(
|
|
660
|
+
db,
|
|
661
|
+
fields,
|
|
662
|
+
(selections) => db.select(selections),
|
|
663
|
+
false,
|
|
664
|
+
newOptions,
|
|
665
|
+
);
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
// Select aliased with cache
|
|
669
|
+
db.selectAliasedCacheable = function <TSelection extends SelectedFields>(
|
|
670
|
+
fields: TSelection,
|
|
671
|
+
cacheTtl?: number,
|
|
672
|
+
) {
|
|
673
|
+
return createAliasedSelectBuilder(
|
|
674
|
+
db,
|
|
675
|
+
fields,
|
|
676
|
+
(selections) => db.select(selections),
|
|
677
|
+
true,
|
|
678
|
+
newOptions,
|
|
679
|
+
cacheTtl,
|
|
680
|
+
);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// Select aliased distinct without cache
|
|
684
|
+
db.selectAliasedDistinct = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
685
|
+
return createAliasedSelectBuilder(
|
|
686
|
+
db,
|
|
687
|
+
fields,
|
|
688
|
+
(selections) => db.selectDistinct(selections),
|
|
689
|
+
false,
|
|
690
|
+
newOptions,
|
|
691
|
+
);
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// Select aliased distinct with cache
|
|
695
|
+
db.selectAliasedDistinctCacheable = function <TSelection extends SelectedFields>(
|
|
696
|
+
fields: TSelection,
|
|
697
|
+
cacheTtl?: number,
|
|
698
|
+
) {
|
|
699
|
+
return createAliasedSelectBuilder(
|
|
700
|
+
db,
|
|
701
|
+
fields,
|
|
702
|
+
(selections) => db.selectDistinct(selections),
|
|
703
|
+
true,
|
|
704
|
+
newOptions,
|
|
705
|
+
cacheTtl,
|
|
706
|
+
);
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// ============================================================================
|
|
710
|
+
// TABLE-BASED SELECT METHODS
|
|
711
|
+
// ============================================================================
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Creates a select query builder for all columns from a table with field aliasing support.
|
|
715
|
+
* This is a convenience method that automatically selects all columns from the specified table.
|
|
716
|
+
*
|
|
717
|
+
* @param table - The table to select from
|
|
718
|
+
* @returns Select query builder with all table columns and field aliasing support
|
|
719
|
+
* @example
|
|
720
|
+
* ```typescript
|
|
721
|
+
* const users = await db.selectFrom(userTable).where(eq(userTable.id, 1));
|
|
722
|
+
* ```
|
|
723
|
+
*/
|
|
724
|
+
db.selectFrom = function <T extends MySqlTable>(table: T) {
|
|
725
|
+
return db.selectAliased(getTableColumns(table)).from(table);
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Creates a select query builder for all columns from a table with field aliasing and caching support.
|
|
730
|
+
* This is a convenience method that automatically selects all columns from the specified table with caching enabled.
|
|
731
|
+
*
|
|
732
|
+
* @param table - The table to select from
|
|
733
|
+
* @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
|
|
734
|
+
* @returns Select query builder with all table columns, field aliasing, and caching support
|
|
735
|
+
* @example
|
|
736
|
+
* ```typescript
|
|
737
|
+
* const users = await db.selectFromCacheable(userTable, 300).where(eq(userTable.id, 1));
|
|
738
|
+
* ```
|
|
739
|
+
*/
|
|
740
|
+
db.selectFromCacheable = function <T extends MySqlTable>(table: T, cacheTtl?: number) {
|
|
741
|
+
return db.selectAliasedCacheable(getTableColumns(table), cacheTtl).from(table);
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Creates a select distinct query builder for all columns from a table with field aliasing support.
|
|
746
|
+
* This is a convenience method that automatically selects all distinct columns from the specified table.
|
|
747
|
+
*
|
|
748
|
+
* @param table - The table to select from
|
|
749
|
+
* @returns Select distinct query builder with all table columns and field aliasing support
|
|
750
|
+
* @example
|
|
751
|
+
* ```typescript
|
|
752
|
+
* const uniqueUsers = await db.selectDistinctFrom(userTable).where(eq(userTable.status, 'active'));
|
|
753
|
+
* ```
|
|
754
|
+
*/
|
|
755
|
+
db.selectDistinctFrom = function <T extends MySqlTable>(table: T) {
|
|
756
|
+
return db.selectAliasedDistinct(getTableColumns(table)).from(table);
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Creates a select distinct query builder for all columns from a table with field aliasing and caching support.
|
|
761
|
+
* This is a convenience method that automatically selects all distinct columns from the specified table with caching enabled.
|
|
762
|
+
*
|
|
763
|
+
* @param table - The table to select from
|
|
764
|
+
* @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
|
|
765
|
+
* @returns Select distinct query builder with all table columns, field aliasing, and caching support
|
|
766
|
+
* @example
|
|
767
|
+
* ```typescript
|
|
768
|
+
* const uniqueUsers = await db.selectDistinctFromCacheable(userTable, 300).where(eq(userTable.status, 'active'));
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
db.selectDistinctFromCacheable = function <T extends MySqlTable>(table: T, cacheTtl?: number) {
|
|
772
|
+
return db.selectAliasedDistinctCacheable(getTableColumns(table), cacheTtl).from(table);
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// ============================================================================
|
|
776
|
+
// CACHE-AWARE MODIFY OPERATIONS
|
|
777
|
+
// ============================================================================
|
|
778
|
+
|
|
779
|
+
// Insert with cache context support (participates in cache clearing when used within cache context)
|
|
780
|
+
db.insertWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
781
|
+
return insertAndEvictCacheBuilder(db, table, newOptions, false);
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// Insert with cache eviction
|
|
785
|
+
db.insertAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
786
|
+
return insertAndEvictCacheBuilder(db, table, newOptions, true);
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
// Update with cache context support (participates in cache clearing when used within cache context)
|
|
790
|
+
db.updateWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
791
|
+
return updateAndEvictCacheBuilder(db, table, newOptions, false);
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
// Update with cache eviction
|
|
795
|
+
db.updateAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
796
|
+
return updateAndEvictCacheBuilder(db, table, newOptions, true);
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
// Delete with cache context support (participates in cache clearing when used within cache context)
|
|
800
|
+
db.deleteWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
801
|
+
return deleteAndEvictCacheBuilder(db, table, newOptions, false);
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
// Delete with cache eviction
|
|
805
|
+
db.deleteAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
806
|
+
return deleteAndEvictCacheBuilder(db, table, newOptions, true);
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// ============================================================================
|
|
810
|
+
// RAW SQL QUERY EXECUTORS
|
|
811
|
+
// ============================================================================
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Executes a raw SQL query with local cache support.
|
|
815
|
+
* This method provides local caching for raw SQL queries within the current invocation context.
|
|
816
|
+
* Results are cached locally and will be returned from cache on subsequent identical queries.
|
|
817
|
+
*
|
|
818
|
+
* @param query - The SQL query to execute (SQLWrapper or string)
|
|
819
|
+
* @returns Promise with query results
|
|
820
|
+
* @example
|
|
821
|
+
* ```typescript
|
|
822
|
+
* // Using SQLWrapper
|
|
823
|
+
* const result = await db.executeQuery(sql`SELECT * FROM users WHERE id = ${userId}`);
|
|
824
|
+
*
|
|
825
|
+
* // Using string
|
|
826
|
+
* const result = await db.executeQuery("SELECT * FROM users WHERE status = 'active'");
|
|
827
|
+
* ```
|
|
828
|
+
*/
|
|
829
|
+
db.executeQuery = createRawQueryExecutor(db, newOptions, false);
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Executes a raw SQL query with both local and global cache support.
|
|
833
|
+
* This method provides comprehensive caching for raw SQL queries:
|
|
834
|
+
* - Local cache: Within the current invocation context
|
|
835
|
+
* - Global cache: Cross-invocation caching using @forge/kvs
|
|
836
|
+
*
|
|
837
|
+
* @param query - The SQL query to execute (SQLWrapper or string)
|
|
838
|
+
* @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
|
|
839
|
+
* @returns Promise with query results
|
|
840
|
+
* @example
|
|
841
|
+
* ```typescript
|
|
842
|
+
* // Using SQLWrapper with custom TTL
|
|
843
|
+
* const result = await db.executeQueryCacheable(sql`SELECT * FROM users WHERE id = ${userId}`, 300);
|
|
844
|
+
*
|
|
845
|
+
* // Using string with default TTL
|
|
846
|
+
* const result = await db.executeQueryCacheable("SELECT * FROM users WHERE status = 'active'");
|
|
847
|
+
* ```
|
|
848
|
+
*/
|
|
849
|
+
db.executeQueryCacheable = createRawQueryExecutor(db, newOptions, true);
|
|
850
|
+
|
|
851
|
+
return db;
|
|
852
|
+
}
|