forge-sql-orm 2.1.0 → 2.1.2

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.
@@ -50,12 +50,34 @@ const parseDateTime = (value, format) => {
50
50
  return result;
51
51
  };
52
52
  function formatDateTime(value, format) {
53
- const fromJSDate = luxon.DateTime.fromJSDate(value);
54
- if (fromJSDate.isValid) {
55
- return fromJSDate.toFormat(format);
53
+ let dt = null;
54
+ if (value instanceof Date) {
55
+ dt = luxon.DateTime.fromJSDate(value);
56
+ } else if (typeof value === "string") {
57
+ for (const parser of [
58
+ luxon.DateTime.fromISO,
59
+ luxon.DateTime.fromRFC2822,
60
+ luxon.DateTime.fromSQL,
61
+ luxon.DateTime.fromHTTP
62
+ ]) {
63
+ dt = parser(value);
64
+ if (dt.isValid) break;
65
+ }
66
+ if (!dt?.isValid) {
67
+ const parsed = Number(value);
68
+ if (!isNaN(parsed)) {
69
+ dt = luxon.DateTime.fromMillis(parsed);
70
+ }
71
+ }
72
+ } else if (typeof value === "number") {
73
+ dt = luxon.DateTime.fromMillis(value);
56
74
  } else {
75
+ throw new Error("Unsupported type");
76
+ }
77
+ if (!dt?.isValid) {
57
78
  throw new Error("Invalid Date");
58
79
  }
80
+ return dt.toFormat(format);
59
81
  }
60
82
  function getPrimaryKeys(table2) {
61
83
  const { columns, primaryKeys } = getTableMetadata(table2);
@@ -531,7 +553,7 @@ async function saveTableIfInsideCacheContext(table$1) {
531
553
  context.tables.add(tableName);
532
554
  }
533
555
  }
534
- async function saveQueryLocalCacheQuery(query, rows) {
556
+ async function saveQueryLocalCacheQuery(query, rows, options) {
535
557
  const context = localCacheApplicationContext.getStore();
536
558
  if (context) {
537
559
  if (!context.cache) {
@@ -543,9 +565,15 @@ async function saveQueryLocalCacheQuery(query, rows) {
543
565
  sql: sql2.toSQL().sql.toLowerCase(),
544
566
  data: rows
545
567
  };
568
+ if (options.logRawSqlQuery) {
569
+ const q = sql2.toSQL();
570
+ console.log(
571
+ `[forge-sql-orm][local-cache][SAVE] Stored result in cache. sql="${q.sql}", params=${JSON.stringify(q.params)}`
572
+ );
573
+ }
546
574
  }
547
575
  }
548
- async function getQueryLocalCacheQuery(query) {
576
+ async function getQueryLocalCacheQuery(query, options) {
549
577
  const context = localCacheApplicationContext.getStore();
550
578
  if (context) {
551
579
  if (!context.cache) {
@@ -554,6 +582,12 @@ async function getQueryLocalCacheQuery(query) {
554
582
  const sql2 = query;
555
583
  const key = hashKey(sql2.toSQL());
556
584
  if (context.cache[key] && context.cache[key].sql === sql2.toSQL().sql.toLowerCase()) {
585
+ if (options.logRawSqlQuery) {
586
+ const q = sql2.toSQL();
587
+ console.log(
588
+ `[forge-sql-orm][local-cache][HIT] Returned cached result. sql="${q.sql}", params=${JSON.stringify(q.params)}`
589
+ );
590
+ }
557
591
  return context.cache[key].data;
558
592
  }
559
593
  }
@@ -1043,11 +1077,21 @@ function createForgeDriverProxy(options, logRawSqlQuery) {
1043
1077
  return forgeDriver(modifiedQuery, params, method);
1044
1078
  };
1045
1079
  }
1080
+ const NON_CACHE_CLEARING_ERROR_CODES = ["VALIDATION_ERROR", "CONSTRAINT_ERROR"];
1081
+ const CACHE_CLEARING_ERROR_CODES = ["DEADLOCK", "LOCK_WAIT_TIMEOUT", "CONNECTION_ERROR"];
1082
+ const NON_CACHE_CLEARING_PATTERNS = [/validation/i, /constraint/i];
1083
+ const CACHE_CLEARING_PATTERNS = [/timeout/i, /connection/i];
1046
1084
  function shouldClearCacheOnError(error) {
1047
- if (error?.code === "VALIDATION_ERROR" || error?.code === "CONSTRAINT_ERROR" || error?.message && /validation/i.exec(error.message)) {
1085
+ if (error?.code && NON_CACHE_CLEARING_ERROR_CODES.includes(error.code)) {
1086
+ return false;
1087
+ }
1088
+ if (error?.message && NON_CACHE_CLEARING_PATTERNS.some((pattern) => pattern.test(error.message))) {
1048
1089
  return false;
1049
1090
  }
1050
- if (error?.code === "DEADLOCK" || error?.code === "LOCK_WAIT_TIMEOUT" || error?.code === "CONNECTION_ERROR" || error?.message && /timeout/i.exec(error.message) || error?.message && /connection/i.exec(error.message)) {
1091
+ if (error?.code && CACHE_CLEARING_ERROR_CODES.includes(error.code)) {
1092
+ return true;
1093
+ }
1094
+ if (error?.message && CACHE_CLEARING_PATTERNS.some((pattern) => pattern.test(error.message))) {
1051
1095
  return true;
1052
1096
  }
1053
1097
  return true;
@@ -1128,7 +1172,7 @@ function deleteAndEvictCacheBuilder(db, table2, options, isCached) {
1128
1172
  }
1129
1173
  async function handleCachedQuery(target, options, cacheTtl, selections, aliasMap, onfulfilled, onrejected) {
1130
1174
  try {
1131
- const localCached = await getQueryLocalCacheQuery(target);
1175
+ const localCached = await getQueryLocalCacheQuery(target, options);
1132
1176
  if (localCached) {
1133
1177
  return onfulfilled?.(localCached);
1134
1178
  }
@@ -1138,7 +1182,7 @@ async function handleCachedQuery(target, options, cacheTtl, selections, aliasMap
1138
1182
  }
1139
1183
  const rows = await target.execute();
1140
1184
  const transformed = applyFromDriverTransform(rows, selections, aliasMap);
1141
- await saveQueryLocalCacheQuery(target, transformed);
1185
+ await saveQueryLocalCacheQuery(target, transformed, options);
1142
1186
  await setCacheResult(target, options, transformed, cacheTtl).catch((cacheError) => {
1143
1187
  console.warn("Cache set error:", cacheError);
1144
1188
  });
@@ -1147,15 +1191,15 @@ async function handleCachedQuery(target, options, cacheTtl, selections, aliasMap
1147
1191
  return onrejected?.(error);
1148
1192
  }
1149
1193
  }
1150
- async function handleNonCachedQuery(target, selections, aliasMap, onfulfilled, onrejected) {
1194
+ async function handleNonCachedQuery(target, options, selections, aliasMap, onfulfilled, onrejected) {
1151
1195
  try {
1152
- const localCached = await getQueryLocalCacheQuery(target);
1196
+ const localCached = await getQueryLocalCacheQuery(target, options);
1153
1197
  if (localCached) {
1154
1198
  return onfulfilled?.(localCached);
1155
1199
  }
1156
1200
  const rows = await target.execute();
1157
1201
  const transformed = applyFromDriverTransform(rows, selections, aliasMap);
1158
- await saveQueryLocalCacheQuery(target, transformed);
1202
+ await saveQueryLocalCacheQuery(target, transformed, options);
1159
1203
  return onfulfilled?.(transformed);
1160
1204
  } catch (error) {
1161
1205
  return onrejected?.(error);
@@ -1187,7 +1231,14 @@ function createAliasedSelectBuilder(db, fields, selectFn, useCache, options, cac
1187
1231
  onrejected
1188
1232
  );
1189
1233
  } else {
1190
- return handleNonCachedQuery(target, selections, aliasMap, onfulfilled, onrejected);
1234
+ return handleNonCachedQuery(
1235
+ target,
1236
+ options,
1237
+ selections,
1238
+ aliasMap,
1239
+ onfulfilled,
1240
+ onrejected
1241
+ );
1191
1242
  }
1192
1243
  };
1193
1244
  }
@@ -1216,6 +1267,43 @@ const DEFAULT_OPTIONS = {
1216
1267
  cacheEntityExpirationName: "expiration",
1217
1268
  cacheEntityDataName: "data"
1218
1269
  };
1270
+ function createRawQueryExecutor(db, options, useGlobalCache = false) {
1271
+ return async function(query, cacheTtl) {
1272
+ let sql$12;
1273
+ if (sql.isSQLWrapper(query)) {
1274
+ const sqlWrapper = query;
1275
+ sql$12 = sqlWrapper.getSQL().toQuery(
1276
+ db.dialect
1277
+ );
1278
+ } else {
1279
+ sql$12 = {
1280
+ sql: query,
1281
+ params: []
1282
+ };
1283
+ }
1284
+ const localCacheResult = await getQueryLocalCacheQuery(sql$12, options);
1285
+ if (localCacheResult) {
1286
+ return localCacheResult;
1287
+ }
1288
+ if (useGlobalCache) {
1289
+ const cacheResult = await getFromCache({ toSQL: () => sql$12 }, options);
1290
+ if (cacheResult) {
1291
+ return cacheResult;
1292
+ }
1293
+ }
1294
+ const results = await db.execute(query);
1295
+ await saveQueryLocalCacheQuery(sql$12, results, options);
1296
+ if (useGlobalCache) {
1297
+ await setCacheResult(
1298
+ { toSQL: () => sql$12 },
1299
+ options,
1300
+ results,
1301
+ cacheTtl ?? options.cacheTTL ?? 120
1302
+ );
1303
+ }
1304
+ return results;
1305
+ };
1306
+ }
1219
1307
  function patchDbWithSelectAliased(db, options) {
1220
1308
  const newOptions = { ...DEFAULT_OPTIONS, ...options };
1221
1309
  db.selectAliased = function(fields) {
@@ -1227,15 +1315,6 @@ function patchDbWithSelectAliased(db, options) {
1227
1315
  newOptions
1228
1316
  );
1229
1317
  };
1230
- db.selectAliasedDistinct = function(fields) {
1231
- return createAliasedSelectBuilder(
1232
- db,
1233
- fields,
1234
- (selections) => db.selectDistinct(selections),
1235
- false,
1236
- newOptions
1237
- );
1238
- };
1239
1318
  db.selectAliasedCacheable = function(fields, cacheTtl) {
1240
1319
  return createAliasedSelectBuilder(
1241
1320
  db,
@@ -1246,6 +1325,15 @@ function patchDbWithSelectAliased(db, options) {
1246
1325
  cacheTtl
1247
1326
  );
1248
1327
  };
1328
+ db.selectAliasedDistinct = function(fields) {
1329
+ return createAliasedSelectBuilder(
1330
+ db,
1331
+ fields,
1332
+ (selections) => db.selectDistinct(selections),
1333
+ false,
1334
+ newOptions
1335
+ );
1336
+ };
1249
1337
  db.selectAliasedDistinctCacheable = function(fields, cacheTtl) {
1250
1338
  return createAliasedSelectBuilder(
1251
1339
  db,
@@ -1256,6 +1344,18 @@ function patchDbWithSelectAliased(db, options) {
1256
1344
  cacheTtl
1257
1345
  );
1258
1346
  };
1347
+ db.selectFrom = function(table2) {
1348
+ return db.selectAliased(drizzleOrm.getTableColumns(table2)).from(table2);
1349
+ };
1350
+ db.selectFromCacheable = function(table2, cacheTtl) {
1351
+ return db.selectAliasedCacheable(drizzleOrm.getTableColumns(table2), cacheTtl).from(table2);
1352
+ };
1353
+ db.selectDistinctFrom = function(table2) {
1354
+ return db.selectAliasedDistinct(drizzleOrm.getTableColumns(table2)).from(table2);
1355
+ };
1356
+ db.selectDistinctFromCacheable = function(table2, cacheTtl) {
1357
+ return db.selectAliasedDistinctCacheable(drizzleOrm.getTableColumns(table2), cacheTtl).from(table2);
1358
+ };
1259
1359
  db.insertWithCacheContext = function(table2) {
1260
1360
  return insertAndEvictCacheBuilder(db, table2, newOptions, false);
1261
1361
  };
@@ -1274,6 +1374,8 @@ function patchDbWithSelectAliased(db, options) {
1274
1374
  db.deleteAndEvictCache = function(table2) {
1275
1375
  return deleteAndEvictCacheBuilder(db, table2, newOptions, true);
1276
1376
  };
1377
+ db.executeQuery = createRawQueryExecutor(db, newOptions, false);
1378
+ db.executeQueryCacheable = createRawQueryExecutor(db, newOptions, true);
1277
1379
  return db;
1278
1380
  }
1279
1381
  class ForgeSQLAnalyseOperation {
@@ -1741,16 +1843,19 @@ class ForgeSQLORMImpl {
1741
1843
  */
1742
1844
  async executeWithCacheContextAndReturnValue(cacheContext) {
1743
1845
  return await this.executeWithLocalCacheContextAndReturnValue(
1744
- async () => await cacheApplicationContext.run({ tables: /* @__PURE__ */ new Set() }, async () => {
1745
- try {
1746
- return await cacheContext();
1747
- } finally {
1748
- await clearTablesCache(
1749
- Array.from(cacheApplicationContext.getStore()?.tables ?? []),
1750
- this.options
1751
- );
1846
+ async () => await cacheApplicationContext.run(
1847
+ cacheApplicationContext.getStore() ?? { tables: /* @__PURE__ */ new Set() },
1848
+ async () => {
1849
+ try {
1850
+ return await cacheContext();
1851
+ } finally {
1852
+ await clearTablesCache(
1853
+ Array.from(cacheApplicationContext.getStore()?.tables ?? []),
1854
+ this.options
1855
+ );
1856
+ }
1752
1857
  }
1753
- })
1858
+ )
1754
1859
  );
1755
1860
  }
1756
1861
  /**
@@ -1761,9 +1866,12 @@ class ForgeSQLORMImpl {
1761
1866
  * @returns Promise that resolves to the return value of the cacheContext function
1762
1867
  */
1763
1868
  async executeWithLocalCacheContextAndReturnValue(cacheContext) {
1764
- return await localCacheApplicationContext.run({ cache: {} }, async () => {
1765
- return await cacheContext();
1766
- });
1869
+ return await localCacheApplicationContext.run(
1870
+ localCacheApplicationContext.getStore() ?? { cache: {} },
1871
+ async () => {
1872
+ return await cacheContext();
1873
+ }
1874
+ );
1767
1875
  }
1768
1876
  /**
1769
1877
  * Executes operations within a local cache context.
@@ -1992,6 +2100,147 @@ class ForgeSQLORMImpl {
1992
2100
  }
1993
2101
  return this.drizzle.selectAliasedDistinctCacheable(fields, cacheTTL);
1994
2102
  }
2103
+ /**
2104
+ * Creates a select query builder for all columns from a table with field aliasing support.
2105
+ * This is a convenience method that automatically selects all columns from the specified table.
2106
+ *
2107
+ * @template T - The type of the table
2108
+ * @param table - The table to select from
2109
+ * @returns Select query builder with all table columns and field aliasing support
2110
+ * @example
2111
+ * ```typescript
2112
+ * const users = await forgeSQL.selectFrom(userTable).where(eq(userTable.id, 1));
2113
+ * ```
2114
+ */
2115
+ selectFrom(table2) {
2116
+ return this.drizzle.selectFrom(table2);
2117
+ }
2118
+ /**
2119
+ * Creates a select distinct query builder for all columns from a table with field aliasing support.
2120
+ * This is a convenience method that automatically selects all distinct columns from the specified table.
2121
+ *
2122
+ * @template T - The type of the table
2123
+ * @param table - The table to select from
2124
+ * @returns Select distinct query builder with all table columns and field aliasing support
2125
+ * @example
2126
+ * ```typescript
2127
+ * const uniqueUsers = await forgeSQL.selectDistinctFrom(userTable).where(eq(userTable.status, 'active'));
2128
+ * ```
2129
+ */
2130
+ selectDistinctFrom(table2) {
2131
+ return this.drizzle.selectDistinctFrom(table2);
2132
+ }
2133
+ /**
2134
+ * Creates a cacheable select query builder for all columns from a table with field aliasing and caching support.
2135
+ * This is a convenience method that automatically selects all columns from the specified table with caching enabled.
2136
+ *
2137
+ * @template T - The type of the table
2138
+ * @param table - The table to select from
2139
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
2140
+ * @returns Select query builder with all table columns, field aliasing, and caching support
2141
+ * @example
2142
+ * ```typescript
2143
+ * const users = await forgeSQL.selectCacheableFrom(userTable, 300).where(eq(userTable.id, 1));
2144
+ * ```
2145
+ */
2146
+ selectCacheableFrom(table2, cacheTTL) {
2147
+ return this.drizzle.selectFromCacheable(table2, cacheTTL);
2148
+ }
2149
+ /**
2150
+ * Creates a cacheable select distinct query builder for all columns from a table with field aliasing and caching support.
2151
+ * This is a convenience method that automatically selects all distinct columns from the specified table with caching enabled.
2152
+ *
2153
+ * @template T - The type of the table
2154
+ * @param table - The table to select from
2155
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
2156
+ * @returns Select distinct query builder with all table columns, field aliasing, and caching support
2157
+ * @example
2158
+ * ```typescript
2159
+ * const uniqueUsers = await forgeSQL.selectDistinctCacheableFrom(userTable, 300).where(eq(userTable.status, 'active'));
2160
+ * ```
2161
+ */
2162
+ selectDistinctCacheableFrom(table2, cacheTTL) {
2163
+ return this.drizzle.selectDistinctFromCacheable(table2, cacheTTL);
2164
+ }
2165
+ /**
2166
+ * Executes a raw SQL query with local cache support.
2167
+ * This method provides local caching for raw SQL queries within the current invocation context.
2168
+ * Results are cached locally and will be returned from cache on subsequent identical queries.
2169
+ *
2170
+ * @param query - The SQL query to execute (SQLWrapper or string)
2171
+ * @returns Promise with query results
2172
+ * @example
2173
+ * ```typescript
2174
+ * // Using SQLWrapper
2175
+ * const result = await forgeSQL.execute(sql`SELECT * FROM users WHERE id = ${userId}`);
2176
+ *
2177
+ * // Using string
2178
+ * const result = await forgeSQL.execute("SELECT * FROM users WHERE status = 'active'");
2179
+ * ```
2180
+ */
2181
+ execute(query) {
2182
+ return this.drizzle.executeQuery(query);
2183
+ }
2184
+ /**
2185
+ * Executes a raw SQL query with both local and global cache support.
2186
+ * This method provides comprehensive caching for raw SQL queries:
2187
+ * - Local cache: Within the current invocation context
2188
+ * - Global cache: Cross-invocation caching using @forge/kvs
2189
+ *
2190
+ * @param query - The SQL query to execute (SQLWrapper or string)
2191
+ * @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
2192
+ * @returns Promise with query results
2193
+ * @example
2194
+ * ```typescript
2195
+ * // Using SQLWrapper with custom TTL
2196
+ * const result = await forgeSQL.executeCacheable(sql`SELECT * FROM users WHERE id = ${userId}`, 300);
2197
+ *
2198
+ * // Using string with default TTL
2199
+ * const result = await forgeSQL.executeCacheable("SELECT * FROM users WHERE status = 'active'");
2200
+ * ```
2201
+ */
2202
+ executeCacheable(query, cacheTtl) {
2203
+ return this.drizzle.executeQueryCacheable(query, cacheTtl);
2204
+ }
2205
+ /**
2206
+ * Creates a Common Table Expression (CTE) builder for complex queries.
2207
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
2208
+ *
2209
+ * @returns WithBuilder for creating CTEs
2210
+ * @example
2211
+ * ```typescript
2212
+ * const withQuery = forgeSQL.$with('userStats').as(
2213
+ * forgeSQL.select({ userId: users.id, count: sql<number>`count(*)` })
2214
+ * .from(users)
2215
+ * .groupBy(users.id)
2216
+ * );
2217
+ * ```
2218
+ */
2219
+ get $with() {
2220
+ return this.drizzle.$with;
2221
+ }
2222
+ /**
2223
+ * Creates a query builder that uses Common Table Expressions (CTEs).
2224
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
2225
+ *
2226
+ * @param queries - Array of CTE queries created with $with()
2227
+ * @returns Query builder with CTE support
2228
+ * @example
2229
+ * ```typescript
2230
+ * const withQuery = forgeSQL.$with('userStats').as(
2231
+ * forgeSQL.select({ userId: users.id, count: sql<number>`count(*)` })
2232
+ * .from(users)
2233
+ * .groupBy(users.id)
2234
+ * );
2235
+ *
2236
+ * const result = await forgeSQL.with(withQuery)
2237
+ * .select({ userId: withQuery.userId, count: withQuery.count })
2238
+ * .from(withQuery);
2239
+ * ```
2240
+ */
2241
+ with(...queries) {
2242
+ return this.drizzle.with(...queries);
2243
+ }
1995
2244
  }
1996
2245
  class ForgeSQLORM {
1997
2246
  ormInstance;
@@ -2004,6 +2253,68 @@ class ForgeSQLORM {
2004
2253
  selectDistinctCacheable(fields, cacheTTL) {
2005
2254
  return this.ormInstance.selectDistinctCacheable(fields, cacheTTL);
2006
2255
  }
2256
+ /**
2257
+ * Creates a select query builder for all columns from a table with field aliasing support.
2258
+ * This is a convenience method that automatically selects all columns from the specified table.
2259
+ *
2260
+ * @template T - The type of the table
2261
+ * @param table - The table to select from
2262
+ * @returns Select query builder with all table columns and field aliasing support
2263
+ * @example
2264
+ * ```typescript
2265
+ * const users = await forgeSQL.selectFrom(userTable).where(eq(userTable.id, 1));
2266
+ * ```
2267
+ */
2268
+ selectFrom(table2) {
2269
+ return this.ormInstance.getDrizzleQueryBuilder().selectFrom(table2);
2270
+ }
2271
+ /**
2272
+ * Creates a select distinct query builder for all columns from a table with field aliasing support.
2273
+ * This is a convenience method that automatically selects all distinct columns from the specified table.
2274
+ *
2275
+ * @template T - The type of the table
2276
+ * @param table - The table to select from
2277
+ * @returns Select distinct query builder with all table columns and field aliasing support
2278
+ * @example
2279
+ * ```typescript
2280
+ * const uniqueUsers = await forgeSQL.selectDistinctFrom(userTable).where(eq(userTable.status, 'active'));
2281
+ * ```
2282
+ */
2283
+ selectDistinctFrom(table2) {
2284
+ return this.ormInstance.getDrizzleQueryBuilder().selectDistinctFrom(table2);
2285
+ }
2286
+ /**
2287
+ * Creates a cacheable select query builder for all columns from a table with field aliasing and caching support.
2288
+ * This is a convenience method that automatically selects all columns from the specified table with caching enabled.
2289
+ *
2290
+ * @template T - The type of the table
2291
+ * @param table - The table to select from
2292
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
2293
+ * @returns Select query builder with all table columns, field aliasing, and caching support
2294
+ * @example
2295
+ * ```typescript
2296
+ * const users = await forgeSQL.selectCacheableFrom(userTable, 300).where(eq(userTable.id, 1));
2297
+ * ```
2298
+ */
2299
+ selectCacheableFrom(table2, cacheTTL) {
2300
+ return this.ormInstance.getDrizzleQueryBuilder().selectFromCacheable(table2, cacheTTL);
2301
+ }
2302
+ /**
2303
+ * Creates a cacheable select distinct query builder for all columns from a table with field aliasing and caching support.
2304
+ * This is a convenience method that automatically selects all distinct columns from the specified table with caching enabled.
2305
+ *
2306
+ * @template T - The type of the table
2307
+ * @param table - The table to select from
2308
+ * @param cacheTTL - Optional cache TTL override (defaults to global cache TTL)
2309
+ * @returns Select distinct query builder with all table columns, field aliasing, and caching support
2310
+ * @example
2311
+ * ```typescript
2312
+ * const uniqueUsers = await forgeSQL.selectDistinctCacheableFrom(userTable, 300).where(eq(userTable.status, 'active'));
2313
+ * ```
2314
+ */
2315
+ selectDistinctCacheableFrom(table2, cacheTTL) {
2316
+ return this.ormInstance.getDrizzleQueryBuilder().selectDistinctFromCacheable(table2, cacheTTL);
2317
+ }
2007
2318
  executeWithCacheContext(cacheContext) {
2008
2319
  return this.ormInstance.executeWithCacheContext(cacheContext);
2009
2320
  }
@@ -2176,6 +2487,85 @@ class ForgeSQLORM {
2176
2487
  getDrizzleQueryBuilder() {
2177
2488
  return this.ormInstance.getDrizzleQueryBuilder();
2178
2489
  }
2490
+ /**
2491
+ * Executes a raw SQL query with local cache support.
2492
+ * This method provides local caching for raw SQL queries within the current invocation context.
2493
+ * Results are cached locally and will be returned from cache on subsequent identical queries.
2494
+ *
2495
+ * @param query - The SQL query to execute (SQLWrapper or string)
2496
+ * @returns Promise with query results
2497
+ * @example
2498
+ * ```typescript
2499
+ * // Using SQLWrapper
2500
+ * const result = await forgeSQL.execute(sql`SELECT * FROM users WHERE id = ${userId}`);
2501
+ *
2502
+ * // Using string
2503
+ * const result = await forgeSQL.execute("SELECT * FROM users WHERE status = 'active'");
2504
+ * ```
2505
+ */
2506
+ execute(query) {
2507
+ return this.ormInstance.getDrizzleQueryBuilder().executeQuery(query);
2508
+ }
2509
+ /**
2510
+ * Executes a raw SQL query with both local and global cache support.
2511
+ * This method provides comprehensive caching for raw SQL queries:
2512
+ * - Local cache: Within the current invocation context
2513
+ * - Global cache: Cross-invocation caching using @forge/kvs
2514
+ *
2515
+ * @param query - The SQL query to execute (SQLWrapper or string)
2516
+ * @param cacheTtl - Optional cache TTL override (defaults to global cache TTL)
2517
+ * @returns Promise with query results
2518
+ * @example
2519
+ * ```typescript
2520
+ * // Using SQLWrapper with custom TTL
2521
+ * const result = await forgeSQL.executeCacheable(sql`SELECT * FROM users WHERE id = ${userId}`, 300);
2522
+ *
2523
+ * // Using string with default TTL
2524
+ * const result = await forgeSQL.executeCacheable("SELECT * FROM users WHERE status = 'active'");
2525
+ * ```
2526
+ */
2527
+ executeCacheable(query, cacheTtl) {
2528
+ return this.ormInstance.getDrizzleQueryBuilder().executeQueryCacheable(query, cacheTtl);
2529
+ }
2530
+ /**
2531
+ * Creates a Common Table Expression (CTE) builder for complex queries.
2532
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
2533
+ *
2534
+ * @returns WithBuilder for creating CTEs
2535
+ * @example
2536
+ * ```typescript
2537
+ * const withQuery = forgeSQL.$with('userStats').as(
2538
+ * forgeSQL.select({ userId: users.id, count: sql<number>`count(*)` })
2539
+ * .from(users)
2540
+ * .groupBy(users.id)
2541
+ * );
2542
+ * ```
2543
+ */
2544
+ get $with() {
2545
+ return this.ormInstance.getDrizzleQueryBuilder().$with;
2546
+ }
2547
+ /**
2548
+ * Creates a query builder that uses Common Table Expressions (CTEs).
2549
+ * CTEs allow you to define temporary named result sets that exist within the scope of a single query.
2550
+ *
2551
+ * @param queries - Array of CTE queries created with $with()
2552
+ * @returns Query builder with CTE support
2553
+ * @example
2554
+ * ```typescript
2555
+ * const withQuery = forgeSQL.$with('userStats').as(
2556
+ * forgeSQL.select({ userId: users.id, count: sql<number>`count(*)` })
2557
+ * .from(users)
2558
+ * .groupBy(users.id)
2559
+ * );
2560
+ *
2561
+ * const result = await forgeSQL.with(withQuery)
2562
+ * .select({ userId: withQuery.userId, count: withQuery.count })
2563
+ * .from(withQuery);
2564
+ * ```
2565
+ */
2566
+ with(...queries) {
2567
+ return this.ormInstance.getDrizzleQueryBuilder().with(...queries);
2568
+ }
2179
2569
  }
2180
2570
  const forgeDateTimeString = mysqlCore.customType({
2181
2571
  dataType() {