forge-sql-orm 2.1.1 → 2.1.3
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/ForgeSQLORM.js +142 -103
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +142 -103
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +24 -25
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
- package/dist/utils/cacheContextUtils.d.ts +4 -2
- package/dist/utils/cacheContextUtils.d.ts.map +1 -1
- package/dist/utils/cacheUtils.d.ts.map +1 -1
- package/dist/utils/forgeDriver.d.ts.map +1 -1
- package/dist/utils/forgeDriverProxy.d.ts.map +1 -1
- package/dist/utils/sqlUtils.d.ts +1 -1
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/dropTablesMigrationWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/core/ForgeSQLORM.ts +29 -23
- package/src/core/ForgeSQLQueryBuilder.ts +185 -128
- package/src/lib/drizzle/extensions/additionalActions.ts +125 -76
- package/src/lib/drizzle/extensions/types.d.ts +16 -11
- package/src/utils/cacheContextUtils.ts +16 -4
- package/src/utils/cacheUtils.ts +3 -1
- package/src/utils/forgeDriver.ts +16 -21
- package/src/utils/forgeDriverProxy.ts +10 -3
- package/src/utils/sqlUtils.ts +59 -35
- package/src/webtriggers/applyMigrationsWebTrigger.ts +18 -14
- package/src/webtriggers/dropMigrationWebTrigger.ts +8 -4
- package/src/webtriggers/dropTablesMigrationWebTrigger.ts +8 -4
- package/src/webtriggers/fetchSchemaWebTrigger.ts +7 -3
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
MySqlRawQueryResult,
|
|
3
|
+
MySqlRemoteDatabase,
|
|
4
|
+
MySqlRemotePreparedQueryHKT,
|
|
5
|
+
MySqlRemoteQueryResultHKT,
|
|
6
6
|
} from "drizzle-orm/mysql-proxy";
|
|
7
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 {
|
|
13
|
-
|
|
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 {
|
|
13
|
+
MySqlDeleteBase,
|
|
14
|
+
MySqlInsertBuilder,
|
|
15
|
+
MySqlUpdateBuilder,
|
|
16
|
+
} from "drizzle-orm/mysql-core/query-builders";
|
|
17
|
+
import { clearCache, getFromCache, setCacheResult } from "../../../utils/cacheUtils";
|
|
14
18
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
cacheApplicationContext,
|
|
20
|
+
evictLocalCacheQuery,
|
|
21
|
+
getQueryLocalCacheQuery,
|
|
22
|
+
saveQueryLocalCacheQuery,
|
|
23
|
+
saveTableIfInsideCacheContext,
|
|
20
24
|
} 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 {
|
|
25
|
+
import { BuildQueryConfig, isSQLWrapper, SQLWrapper } from "drizzle-orm/sql/sql";
|
|
26
|
+
import type { MySqlQueryResultKind } from "drizzle-orm/mysql-core/session";
|
|
27
|
+
import { getTableColumns, Query } from "drizzle-orm";
|
|
28
|
+
import { MySqlDialect } from "drizzle-orm/mysql-core/dialect";
|
|
29
|
+
import type {
|
|
30
|
+
GetSelectTableName,
|
|
31
|
+
GetSelectTableSelection,
|
|
32
|
+
} from "drizzle-orm/query-builders/select.types";
|
|
26
33
|
|
|
27
34
|
// ============================================================================
|
|
28
35
|
// TYPES AND INTERFACES
|
|
@@ -40,35 +47,22 @@ interface QueryBuilder {
|
|
|
40
47
|
/**
|
|
41
48
|
* Error codes that should not trigger cache clearing
|
|
42
49
|
*/
|
|
43
|
-
const NON_CACHE_CLEARING_ERROR_CODES = [
|
|
44
|
-
"VALIDATION_ERROR",
|
|
45
|
-
"CONSTRAINT_ERROR"
|
|
46
|
-
] as const;
|
|
50
|
+
const NON_CACHE_CLEARING_ERROR_CODES = ["VALIDATION_ERROR", "CONSTRAINT_ERROR"] as const;
|
|
47
51
|
|
|
48
52
|
/**
|
|
49
53
|
* Error codes that should trigger cache clearing
|
|
50
54
|
*/
|
|
51
|
-
const CACHE_CLEARING_ERROR_CODES = [
|
|
52
|
-
"DEADLOCK",
|
|
53
|
-
"LOCK_WAIT_TIMEOUT",
|
|
54
|
-
"CONNECTION_ERROR"
|
|
55
|
-
] as const;
|
|
55
|
+
const CACHE_CLEARING_ERROR_CODES = ["DEADLOCK", "LOCK_WAIT_TIMEOUT", "CONNECTION_ERROR"] as const;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Error message patterns that should not trigger cache clearing
|
|
59
59
|
*/
|
|
60
|
-
const NON_CACHE_CLEARING_PATTERNS = [
|
|
61
|
-
/validation/i,
|
|
62
|
-
/constraint/i
|
|
63
|
-
] as const;
|
|
60
|
+
const NON_CACHE_CLEARING_PATTERNS = [/validation/i, /constraint/i] as const;
|
|
64
61
|
|
|
65
62
|
/**
|
|
66
63
|
* Error message patterns that should trigger cache clearing
|
|
67
64
|
*/
|
|
68
|
-
const CACHE_CLEARING_PATTERNS = [
|
|
69
|
-
/timeout/i,
|
|
70
|
-
/connection/i
|
|
71
|
-
] as const;
|
|
65
|
+
const CACHE_CLEARING_PATTERNS = [/timeout/i, /connection/i] as const;
|
|
72
66
|
|
|
73
67
|
// ============================================================================
|
|
74
68
|
// CACHE MANAGEMENT UTILITIES
|
|
@@ -87,7 +81,10 @@ function shouldClearCacheOnError(error: any): boolean {
|
|
|
87
81
|
return false;
|
|
88
82
|
}
|
|
89
83
|
|
|
90
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
error?.message &&
|
|
86
|
+
NON_CACHE_CLEARING_PATTERNS.some((pattern) => pattern.test(error.message))
|
|
87
|
+
) {
|
|
91
88
|
return false;
|
|
92
89
|
}
|
|
93
90
|
|
|
@@ -96,7 +93,7 @@ function shouldClearCacheOnError(error: any): boolean {
|
|
|
96
93
|
return true;
|
|
97
94
|
}
|
|
98
95
|
|
|
99
|
-
if (error?.message && CACHE_CLEARING_PATTERNS.some(pattern => pattern.test(error.message))) {
|
|
96
|
+
if (error?.message && CACHE_CLEARING_PATTERNS.some((pattern) => pattern.test(error.message))) {
|
|
100
97
|
return true;
|
|
101
98
|
}
|
|
102
99
|
|
|
@@ -142,41 +139,82 @@ export type SelectAliasedDistinctCacheableType = <TSelection extends SelectedFie
|
|
|
142
139
|
* Type for select queries from table with field aliasing
|
|
143
140
|
*/
|
|
144
141
|
export type SelectAllFromAliasedType = <T extends MySqlTable>(
|
|
145
|
-
|
|
146
|
-
) =>
|
|
142
|
+
table: T,
|
|
143
|
+
) => MySqlSelectBase<
|
|
144
|
+
GetSelectTableName<T>,
|
|
145
|
+
T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"],
|
|
146
|
+
T["_"]["columns"] extends undefined ? "single" : "partial",
|
|
147
|
+
MySqlRemotePreparedQueryHKT,
|
|
148
|
+
GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {},
|
|
149
|
+
false,
|
|
150
|
+
never,
|
|
151
|
+
any
|
|
152
|
+
>;
|
|
147
153
|
|
|
148
154
|
/**
|
|
149
155
|
* Type for select distinct queries from table with field aliasing
|
|
150
156
|
*/
|
|
151
157
|
export type SelectAllDistinctFromAliasedType = <T extends MySqlTable>(
|
|
152
|
-
|
|
153
|
-
) => MySqlSelectBase<
|
|
158
|
+
table: T,
|
|
159
|
+
) => MySqlSelectBase<
|
|
160
|
+
GetSelectTableName<T>,
|
|
161
|
+
T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"],
|
|
162
|
+
T["_"]["columns"] extends undefined ? "single" : "partial",
|
|
163
|
+
MySqlRemotePreparedQueryHKT,
|
|
164
|
+
GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {},
|
|
165
|
+
false,
|
|
166
|
+
never,
|
|
167
|
+
any
|
|
168
|
+
>;
|
|
154
169
|
|
|
155
170
|
/**
|
|
156
171
|
* Type for select queries from table with field aliasing and caching
|
|
157
172
|
*/
|
|
158
173
|
export type SelectAllFromCacheableAliasedType = <T extends MySqlTable>(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
) => MySqlSelectBase<
|
|
174
|
+
table: T,
|
|
175
|
+
cacheTtl?: number,
|
|
176
|
+
) => MySqlSelectBase<
|
|
177
|
+
GetSelectTableName<T>,
|
|
178
|
+
T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"],
|
|
179
|
+
T["_"]["columns"] extends undefined ? "single" : "partial",
|
|
180
|
+
MySqlRemotePreparedQueryHKT,
|
|
181
|
+
GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {},
|
|
182
|
+
false,
|
|
183
|
+
never,
|
|
184
|
+
any
|
|
185
|
+
>;
|
|
162
186
|
|
|
163
187
|
/**
|
|
164
188
|
* Type for select distinct queries from table with field aliasing and caching
|
|
165
189
|
*/
|
|
166
190
|
export type SelectAllDistinctFromCacheableAliasedType = <T extends MySqlTable>(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
) => MySqlSelectBase<
|
|
191
|
+
table: T,
|
|
192
|
+
cacheTtl?: number,
|
|
193
|
+
) => MySqlSelectBase<
|
|
194
|
+
GetSelectTableName<T>,
|
|
195
|
+
T["_"]["columns"] extends undefined ? GetSelectTableSelection<T> : T["_"]["columns"],
|
|
196
|
+
T["_"]["columns"] extends undefined ? "single" : "partial",
|
|
197
|
+
MySqlRemotePreparedQueryHKT,
|
|
198
|
+
GetSelectTableName<T> extends string ? Record<string & GetSelectTableName<T>, "not-null"> : {},
|
|
199
|
+
false,
|
|
200
|
+
never,
|
|
201
|
+
any
|
|
202
|
+
>;
|
|
170
203
|
|
|
171
204
|
/**
|
|
172
205
|
* Type for executing raw SQL queries with local cache
|
|
173
206
|
*/
|
|
174
|
-
export type ExecuteQuery = (
|
|
207
|
+
export type ExecuteQuery = (
|
|
208
|
+
query: SQLWrapper | string,
|
|
209
|
+
) => Promise<MySqlQueryResultKind<MySqlRemoteQueryResultHKT, unknown>>;
|
|
175
210
|
|
|
176
211
|
/**
|
|
177
212
|
* Type for executing raw SQL queries with local and global cache
|
|
178
213
|
*/
|
|
179
|
-
export type ExecuteQueryCacheable = (
|
|
214
|
+
export type ExecuteQueryCacheable = (
|
|
215
|
+
query: SQLWrapper | string,
|
|
216
|
+
cacheTtl?: number,
|
|
217
|
+
) => Promise<MySqlQueryResultKind<MySqlRemoteQueryResultHKT, unknown>>;
|
|
180
218
|
|
|
181
219
|
/**
|
|
182
220
|
* Type for insert operations with cache eviction
|
|
@@ -229,8 +267,8 @@ async function handleSuccessfulExecution(
|
|
|
229
267
|
if (shouldClearCacheOnError(error)) {
|
|
230
268
|
await evictLocalCacheQuery(table, options);
|
|
231
269
|
if (isCached) {
|
|
232
|
-
await clearCache(table, options).catch(() => {
|
|
233
|
-
console.warn("Ignore cache clear errors");
|
|
270
|
+
await clearCache(table, options).catch((e) => {
|
|
271
|
+
console.warn("Ignore cache clear errors", e);
|
|
234
272
|
});
|
|
235
273
|
} else {
|
|
236
274
|
await saveTableIfInsideCacheContext(table);
|
|
@@ -406,7 +444,7 @@ async function handleCachedQuery(
|
|
|
406
444
|
onrejected?: any,
|
|
407
445
|
): Promise<any> {
|
|
408
446
|
try {
|
|
409
|
-
const localCached = await getQueryLocalCacheQuery(target);
|
|
447
|
+
const localCached = await getQueryLocalCacheQuery(target, options);
|
|
410
448
|
if (localCached) {
|
|
411
449
|
return onfulfilled?.(localCached);
|
|
412
450
|
}
|
|
@@ -416,7 +454,7 @@ async function handleCachedQuery(
|
|
|
416
454
|
}
|
|
417
455
|
const rows = await target.execute();
|
|
418
456
|
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
419
|
-
await saveQueryLocalCacheQuery(target, transformed);
|
|
457
|
+
await saveQueryLocalCacheQuery(target, transformed, options);
|
|
420
458
|
await setCacheResult(target, options, transformed, cacheTtl).catch((cacheError) => {
|
|
421
459
|
// Log cache error but don't fail the query
|
|
422
460
|
console.warn("Cache set error:", cacheError);
|
|
@@ -432,6 +470,7 @@ async function handleCachedQuery(
|
|
|
432
470
|
* Handles non-cached query execution.
|
|
433
471
|
*
|
|
434
472
|
* @param target - The query target
|
|
473
|
+
* @param options - ForgeSQL ORM options
|
|
435
474
|
* @param selections - Field selections
|
|
436
475
|
* @param aliasMap - Field alias mapping
|
|
437
476
|
* @param onfulfilled - Success callback
|
|
@@ -440,19 +479,20 @@ async function handleCachedQuery(
|
|
|
440
479
|
*/
|
|
441
480
|
async function handleNonCachedQuery(
|
|
442
481
|
target: any,
|
|
482
|
+
options: any,
|
|
443
483
|
selections: any,
|
|
444
484
|
aliasMap: any,
|
|
445
485
|
onfulfilled?: any,
|
|
446
486
|
onrejected?: any,
|
|
447
487
|
): Promise<any> {
|
|
448
488
|
try {
|
|
449
|
-
const localCached = await getQueryLocalCacheQuery(target);
|
|
489
|
+
const localCached = await getQueryLocalCacheQuery(target, options);
|
|
450
490
|
if (localCached) {
|
|
451
491
|
return onfulfilled?.(localCached);
|
|
452
492
|
}
|
|
453
493
|
const rows = await target.execute();
|
|
454
494
|
const transformed = applyFromDriverTransform(rows, selections, aliasMap);
|
|
455
|
-
await saveQueryLocalCacheQuery(target, transformed);
|
|
495
|
+
await saveQueryLocalCacheQuery(target, transformed, options);
|
|
456
496
|
return onfulfilled?.(transformed);
|
|
457
497
|
} catch (error) {
|
|
458
498
|
return onrejected?.(error);
|
|
@@ -505,7 +545,14 @@ function createAliasedSelectBuilder<TSelection extends SelectedFields>(
|
|
|
505
545
|
onrejected,
|
|
506
546
|
);
|
|
507
547
|
} else {
|
|
508
|
-
return handleNonCachedQuery(
|
|
548
|
+
return handleNonCachedQuery(
|
|
549
|
+
target,
|
|
550
|
+
options,
|
|
551
|
+
selections,
|
|
552
|
+
aliasMap,
|
|
553
|
+
onfulfilled,
|
|
554
|
+
onrejected,
|
|
555
|
+
);
|
|
509
556
|
}
|
|
510
557
|
};
|
|
511
558
|
}
|
|
@@ -553,34 +600,36 @@ const DEFAULT_OPTIONS: ForgeSqlOrmOptions = {
|
|
|
553
600
|
// QUERY BUILDER FACTORIES
|
|
554
601
|
// ============================================================================
|
|
555
602
|
|
|
556
|
-
|
|
557
603
|
/**
|
|
558
604
|
* Creates a raw SQL query executor with caching support
|
|
559
605
|
*/
|
|
560
606
|
function createRawQueryExecutor(
|
|
561
607
|
db: MySqlRemoteDatabase<any>,
|
|
562
608
|
options: ForgeSqlOrmOptions,
|
|
563
|
-
useGlobalCache: boolean = false
|
|
609
|
+
useGlobalCache: boolean = false,
|
|
564
610
|
) {
|
|
565
611
|
return async function <T extends { [column: string]: any }>(
|
|
566
612
|
query: SQLWrapper | string,
|
|
567
|
-
cacheTtl?: number
|
|
613
|
+
cacheTtl?: number,
|
|
568
614
|
): Promise<MySqlRawQueryResult> {
|
|
569
615
|
let sql: Query;
|
|
570
|
-
|
|
616
|
+
|
|
571
617
|
if (isSQLWrapper(query)) {
|
|
572
618
|
const sqlWrapper = query as SQLWrapper;
|
|
573
|
-
sql = sqlWrapper
|
|
574
|
-
.
|
|
619
|
+
sql = sqlWrapper
|
|
620
|
+
.getSQL()
|
|
621
|
+
.toQuery(
|
|
622
|
+
(db as unknown as { dialect: MySqlDialect }).dialect as unknown as BuildQueryConfig,
|
|
623
|
+
);
|
|
575
624
|
} else {
|
|
576
625
|
sql = {
|
|
577
626
|
sql: query,
|
|
578
|
-
params: []
|
|
627
|
+
params: [],
|
|
579
628
|
};
|
|
580
629
|
}
|
|
581
630
|
|
|
582
631
|
// Check local cache first
|
|
583
|
-
const localCacheResult = await getQueryLocalCacheQuery(sql);
|
|
632
|
+
const localCacheResult = await getQueryLocalCacheQuery(sql, options);
|
|
584
633
|
if (localCacheResult) {
|
|
585
634
|
return localCacheResult as MySqlRawQueryResult;
|
|
586
635
|
}
|
|
@@ -595,20 +644,20 @@ function createRawQueryExecutor(
|
|
|
595
644
|
|
|
596
645
|
// Execute query
|
|
597
646
|
const results = await db.execute<T>(query);
|
|
598
|
-
|
|
647
|
+
|
|
599
648
|
// Save to local cache
|
|
600
|
-
await saveQueryLocalCacheQuery(sql, results);
|
|
601
|
-
|
|
649
|
+
await saveQueryLocalCacheQuery(sql, results, options);
|
|
650
|
+
|
|
602
651
|
// Save to global cache if enabled
|
|
603
652
|
if (useGlobalCache) {
|
|
604
653
|
await setCacheResult(
|
|
605
|
-
{ toSQL: () => sql },
|
|
606
|
-
options,
|
|
607
|
-
results,
|
|
608
|
-
cacheTtl ?? options.cacheTTL ?? 120
|
|
654
|
+
{ toSQL: () => sql },
|
|
655
|
+
options,
|
|
656
|
+
results,
|
|
657
|
+
cacheTtl ?? options.cacheTTL ?? 120,
|
|
609
658
|
);
|
|
610
659
|
}
|
|
611
|
-
|
|
660
|
+
|
|
612
661
|
return results;
|
|
613
662
|
};
|
|
614
663
|
}
|
|
@@ -821,7 +870,7 @@ export function patchDbWithSelectAliased(
|
|
|
821
870
|
* ```typescript
|
|
822
871
|
* // Using SQLWrapper
|
|
823
872
|
* const result = await db.executeQuery(sql`SELECT * FROM users WHERE id = ${userId}`);
|
|
824
|
-
*
|
|
873
|
+
*
|
|
825
874
|
* // Using string
|
|
826
875
|
* const result = await db.executeQuery("SELECT * FROM users WHERE status = 'active'");
|
|
827
876
|
* ```
|
|
@@ -841,7 +890,7 @@ export function patchDbWithSelectAliased(
|
|
|
841
890
|
* ```typescript
|
|
842
891
|
* // Using SQLWrapper with custom TTL
|
|
843
892
|
* const result = await db.executeQueryCacheable(sql`SELECT * FROM users WHERE id = ${userId}`, 300);
|
|
844
|
-
*
|
|
893
|
+
*
|
|
845
894
|
* // Using string with default TTL
|
|
846
895
|
* const result = await db.executeQueryCacheable("SELECT * FROM users WHERE status = 'active'");
|
|
847
896
|
* ```
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
DeleteAndEvictCacheType,
|
|
3
|
+
ExecuteQuery,
|
|
4
|
+
ExecuteQueryCacheable,
|
|
5
|
+
InsertAndEvictCacheType,
|
|
6
|
+
SelectAliasedCacheableType,
|
|
7
|
+
SelectAliasedDistinctCacheableType,
|
|
8
|
+
SelectAliasedDistinctType,
|
|
9
|
+
SelectAliasedType,
|
|
10
|
+
SelectAllDistinctFromAliasedType,
|
|
11
|
+
SelectAllDistinctFromCacheableAliasedType,
|
|
12
|
+
SelectAllFromAliasedType,
|
|
13
|
+
SelectAllFromCacheableAliasedType,
|
|
14
|
+
UpdateAndEvictCacheType,
|
|
10
15
|
} from "./additionalActions";
|
|
11
16
|
|
|
12
17
|
declare module "drizzle-orm/mysql-proxy" {
|
|
@@ -25,7 +30,7 @@ declare module "drizzle-orm/mysql-proxy" {
|
|
|
25
30
|
* Select with field aliasing support for all table columns
|
|
26
31
|
*/
|
|
27
32
|
selectFrom: SelectAllFromAliasedType;
|
|
28
|
-
|
|
33
|
+
|
|
29
34
|
/**
|
|
30
35
|
* Select distinct with field aliasing support for all table columns
|
|
31
36
|
*/
|
|
@@ -50,12 +55,12 @@ declare module "drizzle-orm/mysql-proxy" {
|
|
|
50
55
|
* Select with field aliasing and caching support for all table columns
|
|
51
56
|
*/
|
|
52
57
|
selectFromCacheable: SelectAllFromCacheableAliasedType;
|
|
53
|
-
|
|
58
|
+
|
|
54
59
|
/**
|
|
55
60
|
* Select distinct with field aliasing and caching support for all table columns
|
|
56
61
|
*/
|
|
57
62
|
selectDistinctFromCacheable: SelectAllDistinctFromCacheableAliasedType;
|
|
58
|
-
|
|
63
|
+
|
|
59
64
|
/**
|
|
60
65
|
* Execute raw SQL query with both local and global cache support
|
|
61
66
|
*/
|
|
@@ -78,6 +78,7 @@ export async function saveTableIfInsideCacheContext<T extends AnyMySqlTable>(
|
|
|
78
78
|
*
|
|
79
79
|
* @param query - The Drizzle query to cache
|
|
80
80
|
* @param rows - The query result data to cache
|
|
81
|
+
* @param options - ForgeSqlOrm options
|
|
81
82
|
* @returns Promise that resolves when the data is saved to local cache
|
|
82
83
|
*
|
|
83
84
|
* @example
|
|
@@ -89,7 +90,7 @@ export async function saveTableIfInsideCacheContext<T extends AnyMySqlTable>(
|
|
|
89
90
|
*/
|
|
90
91
|
export async function saveQueryLocalCacheQuery<
|
|
91
92
|
T extends MySqlSelectDynamic<AnyMySqlSelectQueryBuilder>,
|
|
92
|
-
>(query: T, rows: unknown[]): Promise<void> {
|
|
93
|
+
>(query: T, rows: unknown[], options: ForgeSqlOrmOptions): Promise<void> {
|
|
93
94
|
const context = localCacheApplicationContext.getStore();
|
|
94
95
|
if (context) {
|
|
95
96
|
if (!context.cache) {
|
|
@@ -101,6 +102,12 @@ export async function saveQueryLocalCacheQuery<
|
|
|
101
102
|
sql: sql.toSQL().sql.toLowerCase(),
|
|
102
103
|
data: rows,
|
|
103
104
|
};
|
|
105
|
+
if (options.logRawSqlQuery) {
|
|
106
|
+
const q = sql.toSQL();
|
|
107
|
+
console.debug(
|
|
108
|
+
`[forge-sql-orm][local-cache][SAVE] Stored result in cache. sql="${q.sql}", params=${JSON.stringify(q.params)}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
113
|
|
|
@@ -109,6 +116,7 @@ export async function saveQueryLocalCacheQuery<
|
|
|
109
116
|
* This function checks if a query result is already cached in memory.
|
|
110
117
|
*
|
|
111
118
|
* @param query - The Drizzle query to check for cached results
|
|
119
|
+
* @param options - Option Property
|
|
112
120
|
* @returns Promise that resolves to cached data if found, undefined otherwise
|
|
113
121
|
*
|
|
114
122
|
* @example
|
|
@@ -123,7 +131,7 @@ export async function saveQueryLocalCacheQuery<
|
|
|
123
131
|
*/
|
|
124
132
|
export async function getQueryLocalCacheQuery<
|
|
125
133
|
T extends MySqlSelectDynamic<AnyMySqlSelectQueryBuilder>,
|
|
126
|
-
>(query: T): Promise<unknown[] | undefined> {
|
|
134
|
+
>(query: T, options: ForgeSqlOrmOptions): Promise<unknown[] | undefined> {
|
|
127
135
|
const context = localCacheApplicationContext.getStore();
|
|
128
136
|
if (context) {
|
|
129
137
|
if (!context.cache) {
|
|
@@ -132,14 +140,18 @@ export async function getQueryLocalCacheQuery<
|
|
|
132
140
|
const sql = query as { toSQL: () => Query };
|
|
133
141
|
const key = hashKey(sql.toSQL());
|
|
134
142
|
if (context.cache[key] && context.cache[key].sql === sql.toSQL().sql.toLowerCase()) {
|
|
143
|
+
if (options.logRawSqlQuery) {
|
|
144
|
+
const q = sql.toSQL();
|
|
145
|
+
console.debug(
|
|
146
|
+
`[forge-sql-orm][local-cache][HIT] Returned cached result. sql="${q.sql}", params=${JSON.stringify(q.params)}`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
135
149
|
return context.cache[key].data;
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
return undefined;
|
|
139
153
|
}
|
|
140
154
|
|
|
141
|
-
|
|
142
|
-
|
|
143
155
|
/**
|
|
144
156
|
* Evicts cached queries from the local cache context that involve the specified table.
|
|
145
157
|
* This function removes cached query results that contain the given table name.
|
package/src/utils/cacheUtils.ts
CHANGED
|
@@ -287,7 +287,9 @@ export async function clearExpiredCache(options: ForgeSqlOrmOptions): Promise<vo
|
|
|
287
287
|
);
|
|
288
288
|
} finally {
|
|
289
289
|
const duration = DateTime.now().toSeconds() - startTime.toSeconds();
|
|
290
|
-
|
|
290
|
+
if (options?.logRawSqlQuery) {
|
|
291
|
+
console.debug(`Cleared ${totalRecords} expired cache records in ${duration} seconds`);
|
|
292
|
+
}
|
|
291
293
|
}
|
|
292
294
|
}
|
|
293
295
|
|
package/src/utils/forgeDriver.ts
CHANGED
|
@@ -13,27 +13,22 @@ export const forgeDriver = async (
|
|
|
13
13
|
insertId?: number;
|
|
14
14
|
affectedRows?: number;
|
|
15
15
|
}> => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
sqlStatement.bindParams(...params);
|
|
21
|
-
}
|
|
22
|
-
const updateQueryResponseResults = await sqlStatement.execute();
|
|
23
|
-
let result = updateQueryResponseResults.rows as any;
|
|
24
|
-
return { ...result, rows: [result] };
|
|
25
|
-
} else {
|
|
26
|
-
const sqlStatement = await sql.prepare<unknown>(query);
|
|
27
|
-
if (params) {
|
|
28
|
-
await sqlStatement.bindParams(...params);
|
|
29
|
-
}
|
|
30
|
-
const result = (await sqlStatement.execute()) as ForgeSQLResult;
|
|
31
|
-
let rows;
|
|
32
|
-
rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
|
|
33
|
-
return { rows: rows };
|
|
16
|
+
if (method == "execute") {
|
|
17
|
+
const sqlStatement = sql.prepare<UpdateQueryResponse>(query);
|
|
18
|
+
if (params) {
|
|
19
|
+
sqlStatement.bindParams(...params);
|
|
34
20
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
const updateQueryResponseResults = await sqlStatement.execute();
|
|
22
|
+
let result = updateQueryResponseResults.rows as any;
|
|
23
|
+
return { ...result, rows: [result] };
|
|
24
|
+
} else {
|
|
25
|
+
const sqlStatement = await sql.prepare<unknown>(query);
|
|
26
|
+
if (params) {
|
|
27
|
+
await sqlStatement.bindParams(...params);
|
|
28
|
+
}
|
|
29
|
+
const result = (await sqlStatement.execute()) as ForgeSQLResult;
|
|
30
|
+
let rows;
|
|
31
|
+
rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
|
|
32
|
+
return { rows: rows };
|
|
38
33
|
}
|
|
39
34
|
};
|
|
@@ -19,9 +19,16 @@ export function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: bool
|
|
|
19
19
|
const modifiedQuery = injectSqlHints(query, options);
|
|
20
20
|
|
|
21
21
|
if (options && logRawSqlQuery && modifiedQuery !== query) {
|
|
22
|
-
console.
|
|
22
|
+
console.debug("injected Hints: " + modifiedQuery);
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
// Call the original forgeDriver with the modified query
|
|
26
|
+
return await forgeDriver(modifiedQuery, params, method);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (logRawSqlQuery) {
|
|
29
|
+
console.debug("SQL Error:", JSON.stringify(error));
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
23
32
|
}
|
|
24
|
-
// Call the original forgeDriver with the modified query
|
|
25
|
-
return forgeDriver(modifiedQuery, params, method);
|
|
26
33
|
};
|
|
27
34
|
}
|