forge-sql-orm 2.1.0 → 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 +337 -17
- package/dist/ForgeSQLORM.js +396 -20
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +397 -21
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts +144 -0
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +165 -18
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/additionalActions.d.ts +50 -2
- package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
- package/dist/utils/cacheContextUtils.d.ts.map +1 -1
- package/dist/utils/sqlUtils.d.ts +6 -6
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/core/ForgeSQLORM.ts +306 -6
- package/src/core/ForgeSQLQueryBuilder.ts +181 -16
- package/src/lib/drizzle/extensions/additionalActions.ts +350 -46
- package/src/lib/drizzle/extensions/types.d.ts +38 -7
- package/src/utils/cacheContextUtils.ts +2 -0
- package/src/utils/sqlUtils.ts +38 -12
|
@@ -1,32 +1,78 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
MySqlRawQueryResult,
|
|
3
|
+
MySqlRemoteDatabase,
|
|
4
|
+
MySqlRemotePreparedQueryHKT,
|
|
5
|
+
MySqlRemoteQueryResultHKT,
|
|
5
6
|
} from "drizzle-orm/mysql-proxy";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
MySqlUpdateBuilder,
|
|
14
|
-
} from "drizzle-orm/mysql-core/query-builders";
|
|
15
|
-
import { clearCache, getFromCache, setCacheResult } from "../../../utils/cacheUtils";
|
|
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";
|
|
16
14
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
cacheApplicationContext,
|
|
16
|
+
evictLocalCacheQuery,
|
|
17
|
+
getQueryLocalCacheQuery,
|
|
18
|
+
saveQueryLocalCacheQuery,
|
|
19
|
+
saveTableIfInsideCacheContext,
|
|
22
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
|
+
// ============================================================================
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Base interface for query builders that can be executed
|
|
33
|
+
*/
|
|
34
|
+
interface QueryBuilder {
|
|
26
35
|
execute: (...args: any[]) => Promise<any>;
|
|
27
36
|
then?: (onfulfilled?: any, onrejected?: any) => Promise<any>;
|
|
28
37
|
toSQL?: () => any;
|
|
29
|
-
}
|
|
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
|
+
// ============================================================================
|
|
30
76
|
|
|
31
77
|
/**
|
|
32
78
|
* Determines whether cache should be cleared based on the error type.
|
|
@@ -37,22 +83,20 @@ type QueryBuilder = {
|
|
|
37
83
|
*/
|
|
38
84
|
function shouldClearCacheOnError(error: any): boolean {
|
|
39
85
|
// Don't clear cache for client-side errors (validation, etc.)
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
) {
|
|
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))) {
|
|
45
91
|
return false;
|
|
46
92
|
}
|
|
47
93
|
|
|
48
94
|
// Clear cache for database-level errors that might affect data consistency
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
(error?.message && /connection/i.exec(error.message))
|
|
55
|
-
) {
|
|
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))) {
|
|
56
100
|
return true;
|
|
57
101
|
}
|
|
58
102
|
|
|
@@ -60,30 +104,97 @@ function shouldClearCacheOnError(error: any): boolean {
|
|
|
60
104
|
return true;
|
|
61
105
|
}
|
|
62
106
|
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// EXPORTED TYPES
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Type for select queries with field aliasing
|
|
113
|
+
*/
|
|
63
114
|
export type SelectAliasedType = <TSelection extends SelectedFields>(
|
|
64
115
|
fields: TSelection,
|
|
65
116
|
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
66
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Type for select distinct queries with field aliasing
|
|
120
|
+
*/
|
|
67
121
|
export type SelectAliasedDistinctType = <TSelection extends SelectedFields>(
|
|
68
122
|
fields: TSelection,
|
|
69
123
|
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
70
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Type for select queries with field aliasing and caching
|
|
127
|
+
*/
|
|
71
128
|
export type SelectAliasedCacheableType = <TSelection extends SelectedFields>(
|
|
72
129
|
fields: TSelection,
|
|
73
130
|
cacheTtl?: number,
|
|
74
131
|
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
75
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Type for select distinct queries with field aliasing and caching
|
|
135
|
+
*/
|
|
76
136
|
export type SelectAliasedDistinctCacheableType = <TSelection extends SelectedFields>(
|
|
77
137
|
fields: TSelection,
|
|
78
138
|
cacheTtl?: number,
|
|
79
139
|
) => MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
80
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
|
+
*/
|
|
81
184
|
export type InsertAndEvictCacheType = <TTable extends MySqlTable>(
|
|
82
185
|
table: TTable,
|
|
83
186
|
) => MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Type for update operations with cache eviction
|
|
190
|
+
*/
|
|
84
191
|
export type UpdateAndEvictCacheType = <TTable extends MySqlTable>(
|
|
85
192
|
table: TTable,
|
|
86
193
|
) => MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Type for delete operations with cache eviction
|
|
197
|
+
*/
|
|
87
198
|
export type DeleteAndEvictCacheType = <TTable extends MySqlTable>(
|
|
88
199
|
table: TTable,
|
|
89
200
|
) => MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT>;
|
|
@@ -421,7 +532,13 @@ function createAliasedSelectBuilder<TSelection extends SelectedFields>(
|
|
|
421
532
|
return wrapBuilder(builder);
|
|
422
533
|
}
|
|
423
534
|
|
|
424
|
-
//
|
|
535
|
+
// ============================================================================
|
|
536
|
+
// CONFIGURATION AND CONSTANTS
|
|
537
|
+
// ============================================================================
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Default options for ForgeSQL ORM
|
|
541
|
+
*/
|
|
425
542
|
const DEFAULT_OPTIONS: ForgeSqlOrmOptions = {
|
|
426
543
|
logRawSqlQuery: false,
|
|
427
544
|
disableOptimisticLocking: false,
|
|
@@ -430,7 +547,75 @@ const DEFAULT_OPTIONS: ForgeSqlOrmOptions = {
|
|
|
430
547
|
cacheEntityQueryName: "sql",
|
|
431
548
|
cacheEntityExpirationName: "expiration",
|
|
432
549
|
cacheEntityDataName: "data",
|
|
433
|
-
};
|
|
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
|
+
// ============================================================================
|
|
434
619
|
|
|
435
620
|
/**
|
|
436
621
|
* Patches a Drizzle database instance with additional methods for aliased selects and cache management.
|
|
@@ -465,6 +650,10 @@ export function patchDbWithSelectAliased(
|
|
|
465
650
|
} {
|
|
466
651
|
const newOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
467
652
|
|
|
653
|
+
// ============================================================================
|
|
654
|
+
// SELECT METHODS WITH FIELD ALIASING
|
|
655
|
+
// ============================================================================
|
|
656
|
+
|
|
468
657
|
// Select aliased without cache
|
|
469
658
|
db.selectAliased = function <TSelection extends SelectedFields>(fields: TSelection) {
|
|
470
659
|
return createAliasedSelectBuilder(
|
|
@@ -476,17 +665,6 @@ export function patchDbWithSelectAliased(
|
|
|
476
665
|
);
|
|
477
666
|
};
|
|
478
667
|
|
|
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
668
|
// Select aliased with cache
|
|
491
669
|
db.selectAliasedCacheable = function <TSelection extends SelectedFields>(
|
|
492
670
|
fields: TSelection,
|
|
@@ -502,6 +680,17 @@ export function patchDbWithSelectAliased(
|
|
|
502
680
|
);
|
|
503
681
|
};
|
|
504
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
|
+
|
|
505
694
|
// Select aliased distinct with cache
|
|
506
695
|
db.selectAliasedDistinctCacheable = function <TSelection extends SelectedFields>(
|
|
507
696
|
fields: TSelection,
|
|
@@ -517,10 +706,81 @@ export function patchDbWithSelectAliased(
|
|
|
517
706
|
);
|
|
518
707
|
};
|
|
519
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
|
+
|
|
520
779
|
// Insert with cache context support (participates in cache clearing when used within cache context)
|
|
521
780
|
db.insertWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
522
781
|
return insertAndEvictCacheBuilder(db, table, newOptions, false);
|
|
523
782
|
};
|
|
783
|
+
|
|
524
784
|
// Insert with cache eviction
|
|
525
785
|
db.insertAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
526
786
|
return insertAndEvictCacheBuilder(db, table, newOptions, true);
|
|
@@ -530,6 +790,7 @@ export function patchDbWithSelectAliased(
|
|
|
530
790
|
db.updateWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
531
791
|
return updateAndEvictCacheBuilder(db, table, newOptions, false);
|
|
532
792
|
};
|
|
793
|
+
|
|
533
794
|
// Update with cache eviction
|
|
534
795
|
db.updateAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
535
796
|
return updateAndEvictCacheBuilder(db, table, newOptions, true);
|
|
@@ -539,10 +800,53 @@ export function patchDbWithSelectAliased(
|
|
|
539
800
|
db.deleteWithCacheContext = function <TTable extends MySqlTable>(table: TTable) {
|
|
540
801
|
return deleteAndEvictCacheBuilder(db, table, newOptions, false);
|
|
541
802
|
};
|
|
803
|
+
|
|
542
804
|
// Delete with cache eviction
|
|
543
805
|
db.deleteAndEvictCache = function <TTable extends MySqlTable>(table: TTable) {
|
|
544
806
|
return deleteAndEvictCacheBuilder(db, table, newOptions, true);
|
|
545
807
|
};
|
|
546
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
|
+
|
|
547
851
|
return db;
|
|
548
852
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
DeleteAndEvictCacheType, ExecuteQuery, ExecuteQueryCacheable,
|
|
3
|
+
InsertAndEvictCacheType,
|
|
4
|
+
SelectAliasedCacheableType,
|
|
5
|
+
SelectAliasedDistinctCacheableType,
|
|
6
|
+
SelectAliasedDistinctType,
|
|
7
|
+
SelectAliasedType, SelectAllDistinctFromAliasedType,
|
|
8
|
+
SelectAllDistinctFromCacheableAliasedType, SelectAllFromAliasedType, SelectAllFromCacheableAliasedType,
|
|
9
|
+
UpdateAndEvictCacheType,
|
|
9
10
|
} from "./additionalActions";
|
|
10
11
|
|
|
11
12
|
declare module "drizzle-orm/mysql-proxy" {
|
|
@@ -20,6 +21,21 @@ declare module "drizzle-orm/mysql-proxy" {
|
|
|
20
21
|
*/
|
|
21
22
|
selectAliasedDistinct: SelectAliasedDistinctType;
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Select with field aliasing support for all table columns
|
|
26
|
+
*/
|
|
27
|
+
selectFrom: SelectAllFromAliasedType;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Select distinct with field aliasing support for all table columns
|
|
31
|
+
*/
|
|
32
|
+
selectDistinctFrom: SelectAllDistinctFromAliasedType;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Execute raw SQL query with local cache support
|
|
36
|
+
*/
|
|
37
|
+
executeQuery: ExecuteQuery;
|
|
38
|
+
|
|
23
39
|
/**
|
|
24
40
|
* Select with field aliasing and caching support
|
|
25
41
|
*/
|
|
@@ -30,6 +46,21 @@ declare module "drizzle-orm/mysql-proxy" {
|
|
|
30
46
|
*/
|
|
31
47
|
selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
|
|
32
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Select with field aliasing and caching support for all table columns
|
|
51
|
+
*/
|
|
52
|
+
selectFromCacheable: SelectAllFromCacheableAliasedType;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Select distinct with field aliasing and caching support for all table columns
|
|
56
|
+
*/
|
|
57
|
+
selectDistinctFromCacheable: SelectAllDistinctFromCacheableAliasedType;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Execute raw SQL query with both local and global cache support
|
|
61
|
+
*/
|
|
62
|
+
executeQueryCacheable: ExecuteQueryCacheable;
|
|
63
|
+
|
|
33
64
|
/**
|
|
34
65
|
* Insert operation with cache context support.
|
|
35
66
|
* Participates in cache clearing when used within executeWithCacheContext().
|
|
@@ -138,6 +138,8 @@ export async function getQueryLocalCacheQuery<
|
|
|
138
138
|
return undefined;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
|
|
142
|
+
|
|
141
143
|
/**
|
|
142
144
|
* Evicts cached queries from the local cache context that involve the specified table.
|
|
143
145
|
* This function removes cached query results that contain the given table name.
|
package/src/utils/sqlUtils.ts
CHANGED
|
@@ -75,19 +75,45 @@ export const parseDateTime = (value: string | Date, format: string): Date => {
|
|
|
75
75
|
};
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* Helper function to validate and format
|
|
79
|
-
* @param value - Date object
|
|
80
|
-
* @param format - DateTime format string
|
|
81
|
-
* @returns Formatted date string
|
|
82
|
-
* @throws Error if
|
|
78
|
+
* Helper function to validate and format a date-like value using Luxon DateTime.
|
|
79
|
+
* @param value - Date object, ISO/RFC2822/SQL/HTTP string, or timestamp (number|string).
|
|
80
|
+
* @param format - DateTime format string (Luxon format tokens).
|
|
81
|
+
* @returns Formatted date string.
|
|
82
|
+
* @throws Error if value cannot be parsed as a valid date.
|
|
83
83
|
*/
|
|
84
|
-
export function formatDateTime(value: Date, format: string): string {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
export function formatDateTime(value: Date | string | number, format: string): string {
|
|
85
|
+
let dt: DateTime|null = null;
|
|
86
|
+
|
|
87
|
+
if (value instanceof Date) {
|
|
88
|
+
dt = DateTime.fromJSDate(value);
|
|
89
|
+
} else if (typeof value === "string") {
|
|
90
|
+
// Перебор парсеров
|
|
91
|
+
for (const parser of [
|
|
92
|
+
DateTime.fromISO,
|
|
93
|
+
DateTime.fromRFC2822,
|
|
94
|
+
DateTime.fromSQL,
|
|
95
|
+
DateTime.fromHTTP,
|
|
96
|
+
]) {
|
|
97
|
+
dt = parser(value);
|
|
98
|
+
if (dt.isValid) break;
|
|
99
|
+
}
|
|
100
|
+
if (!dt?.isValid) {
|
|
101
|
+
const parsed = Number(value);
|
|
102
|
+
if (!isNaN(parsed)) {
|
|
103
|
+
dt = DateTime.fromMillis(parsed);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} else if (typeof value === "number") {
|
|
107
|
+
dt = DateTime.fromMillis(value);
|
|
108
|
+
} else {
|
|
109
|
+
throw new Error("Unsupported type");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!dt?.isValid) {
|
|
113
|
+
throw new Error("Invalid Date");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return dt.toFormat(format);
|
|
91
117
|
}
|
|
92
118
|
|
|
93
119
|
/**
|