• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vzakharchenko / forge-sql-orm / 17648728887

11 Sep 2025 03:02PM UTC coverage: 68.742% (-18.1%) from 86.81%
17648728887

Pull #627

github

web-flow
Merge ce989a6a5 into 67ed05a12
Pull Request #627: added kvs cache

287 of 357 branches covered (80.39%)

Branch coverage included in aggregate %.

294 of 825 new or added lines in 9 files covered. (35.64%)

1 existing line in 1 file now uncovered.

1336 of 2004 relevant lines covered (66.67%)

8.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.2
/src/core/ForgeSQLORM.ts
1
import { ForgeSQLCrudOperations } from "./ForgeSQLCrudOperations";
1✔
2
import {
3
  VerioningModificationForgeSQL,
4
  ForgeSqlOperation,
5
  ForgeSqlOrmOptions,
6
  SchemaAnalyzeForgeSql,
7
  SchemaSqlForgeSql,
8
} from "./ForgeSQLQueryBuilder";
9
import { ForgeSQLSelectOperations } from "./ForgeSQLSelectOperations";
1✔
10
import {
1✔
11
  drizzle,
12
  MySqlRemoteDatabase,
13
  MySqlRemotePreparedQueryHKT,
14
  MySqlRemoteQueryResultHKT,
15
} from "drizzle-orm/mysql-proxy";
16
import { createForgeDriverProxy } from "../utils/forgeDriverProxy";
1✔
17
import type { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
18
import { MySqlSelectBuilder } from "drizzle-orm/mysql-core";
19
import {
1✔
20
  DeleteAndEvictCacheType,
21
  InsertAndEvictCacheType,
22
  patchDbWithSelectAliased,
23
  SelectAliasedCacheableType,
24
  SelectAliasedDistinctCacheableType,
25
  SelectAliasedDistinctType,
26
  SelectAliasedType,
27
  UpdateAndEvictCacheType,
28
} from "../lib/drizzle/extensions/additionalActions";
29
import { ForgeSQLAnalyseOperation } from "./ForgeSQLAnalyseOperations";
1✔
30
import { ForgeSQLCacheOperations } from "./ForgeSQLCacheOperations";
1✔
31
import type { MySqlTable } from "drizzle-orm/mysql-core/table";
32
import {
33
  MySqlDeleteBase,
34
  MySqlInsertBuilder,
35
  MySqlUpdateBuilder,
36
} from "drizzle-orm/mysql-core/query-builders";
37
import { cacheApplicationContext } from "../utils/cacheContextUtils";
1✔
38
import { clearTablesCache } from "../utils/cacheUtils";
1✔
39

40
/**
41
 * Implementation of ForgeSQLORM that uses Drizzle ORM for query building.
42
 * This class provides a bridge between Forge SQL and Drizzle ORM, allowing
43
 * to use Drizzle's query builder while executing queries through Forge SQL.
44
 */
45
class ForgeSQLORMImpl implements ForgeSqlOperation {
1✔
46
  private static instance: ForgeSQLORMImpl | null = null;
4✔
47
  private readonly drizzle: MySqlRemoteDatabase<any> & {
4✔
48
      selectAliased: SelectAliasedType;
49
      selectAliasedDistinct: SelectAliasedDistinctType;
50
      selectAliasedCacheable: SelectAliasedCacheableType;
51
      selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
52
      insertWithCacheContext: InsertAndEvictCacheType;
53
      insertAndEvictCache: InsertAndEvictCacheType;
54
      updateAndEvictCache: UpdateAndEvictCacheType;
55
      updateWithCacheContext: UpdateAndEvictCacheType;
56
      deleteAndEvictCache: DeleteAndEvictCacheType;
57
      deleteWithCacheContext: DeleteAndEvictCacheType;
58
  };
59
  private readonly crudOperations: VerioningModificationForgeSQL;
4✔
60
  private readonly fetchOperations: SchemaSqlForgeSql;
4✔
61
  private readonly analyzeOperations: SchemaAnalyzeForgeSql;
4✔
62
  private readonly cacheOperations: ForgeSQLCacheOperations;
4✔
63
  private readonly options: ForgeSqlOrmOptions;
4✔
64

65
  /**
66
   * Private constructor to enforce singleton behavior.
67
   * @param options - Options for configuring ForgeSQL ORM behavior.
68
   */
69
  private constructor(options?: ForgeSqlOrmOptions) {
4✔
70
    try {
2✔
71
      const newOptions: ForgeSqlOrmOptions = options ?? {
2!
72
        logRawSqlQuery: false,
×
73
        disableOptimisticLocking: false,
×
NEW
74
        cacheWrapTable: true,
×
NEW
75
        cacheTTL: 120,
×
NEW
76
        cacheEntityQueryName: "sql",
×
NEW
77
        cacheEntityExpirationName: "expiration",
×
NEW
78
        cacheEntityDataName: "data",
×
UNCOV
79
      };
×
80
      this.options = newOptions;
2✔
81
      if (newOptions.logRawSqlQuery) {
2✔
82
        console.debug("Initializing ForgeSQLORM...");
2✔
83
      }
2✔
84
      // Initialize Drizzle instance with our custom driver
85
      const proxiedDriver = createForgeDriverProxy(newOptions.hints, newOptions.logRawSqlQuery);
2✔
86
      this.drizzle = patchDbWithSelectAliased(
2✔
87
        drizzle(proxiedDriver, { logger: newOptions.logRawSqlQuery }),
2✔
88
        newOptions,
2✔
89
      );
2✔
90
      this.crudOperations = new ForgeSQLCrudOperations(this, newOptions);
2✔
91
      this.fetchOperations = new ForgeSQLSelectOperations(newOptions);
2✔
92
      this.analyzeOperations = new ForgeSQLAnalyseOperation(this);
2✔
93
      this.cacheOperations = new ForgeSQLCacheOperations(newOptions, this);
2✔
94
    } catch (error) {
2!
95
      console.error("ForgeSQLORM initialization failed:", error);
×
96
      throw error;
×
97
    }
×
98
  }
2✔
99

100
  /**
101
   * Executes operations within a cache context that collects cache eviction events.
102
   * All clearCache calls within the context are collected and executed in batch at the end.
103
   * Queries executed within this context will bypass cache for tables that were marked for clearing.
104
   * 
105
   * This is useful for:
106
   * - Batch operations that affect multiple tables
107
   * - Transaction-like operations where you want to clear cache only at the end
108
   * - Performance optimization by reducing cache clear operations
109
   * 
110
   * @param cacheContext - Function containing operations that may trigger cache evictions
111
   * @returns Promise that resolves when all operations and cache clearing are complete
112
   * 
113
   * @example
114
   * ```typescript
115
   * await forgeSQL.executeWithCacheContext(async () => {
116
   *   await forgeSQL.modifyWithVersioning().insert(users, userData);
117
   *   await forgeSQL.modifyWithVersioning().insert(orders, orderData);
118
   *   // Cache for both users and orders tables will be cleared at the end
119
   * });
120
   * ```
121
   */
122
  executeWithCacheContext(cacheContext: () => Promise<void>): Promise<void> {
4✔
NEW
123
    return this.executeWithCacheContextAndReturnValue<void>(cacheContext);
×
NEW
124
  }
×
125

126
  /**
127
   * Executes operations within a cache context and returns a value.
128
   * All clearCache calls within the context are collected and executed in batch at the end.
129
   * Queries executed within this context will bypass cache for tables that were marked for clearing.
130
   * 
131
   * @param cacheContext - Function containing operations that may trigger cache evictions
132
   * @returns Promise that resolves to the return value of the cacheContext function
133
   * 
134
   * @example
135
   * ```typescript
136
   * const result = await forgeSQL.executeWithCacheContextAndReturnValue(async () => {
137
   *   await forgeSQL.modifyWithVersioning().insert(users, userData);
138
   *   return await forgeSQL.fetch().executeQueryOnlyOne(selectUserQuery);
139
   * });
140
   * ```
141
   */
142
  async executeWithCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T> {
4✔
NEW
143
    return await cacheApplicationContext.run({ tables: new Set<string>() }, async () => {
×
NEW
144
      try {
×
NEW
145
        return await cacheContext();
×
NEW
146
      } finally {
×
NEW
147
        await clearTablesCache(
×
NEW
148
          Array.from(cacheApplicationContext.getStore()?.tables ?? []),
×
NEW
149
          this.options,
×
NEW
150
        );
×
NEW
151
      }
×
NEW
152
    });
×
NEW
153
  }
×
154

155
  /**
156
   * Creates an insert query builder.
157
   * 
158
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
159
   * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
160
   * 
161
   * @param table - The table to insert into
162
   * @returns Insert query builder (no versioning, no cache management)
163
   */
164
  insert<TTable extends MySqlTable>(
4✔
165
    table: TTable,
8✔
166
  ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
8✔
167
    return this.drizzle.insertWithCacheContext(table);
8✔
168
  }
8✔
169
  /**
170
   * Creates an insert query builder that automatically evicts cache after execution.
171
   *
172
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
173
   * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
174
   *
175
   * @param table - The table to insert into
176
   * @returns Insert query builder with automatic cache eviction (no versioning)
177
   */
178
  insertAndEvictCache<TTable extends MySqlTable>(
4✔
NEW
179
    table: TTable,
×
NEW
180
  ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
181
    return this.drizzle.insertAndEvictCache(table);
×
NEW
182
  }
×
183

184
  /**
185
   * Creates an update query builder that automatically evicts cache after execution.
186
   * 
187
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
188
   * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
189
   * 
190
   * @param table - The table to update
191
   * @returns Update query builder with automatic cache eviction (no versioning)
192
   */
193
  updateAndEvictCache<TTable extends MySqlTable>(
4✔
NEW
194
    table: TTable,
×
NEW
195
  ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
196
    return this.drizzle.updateAndEvictCache(table);
×
NEW
197
  }
×
198

199
  /**
200
   * Creates an update query builder.
201
   *
202
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
203
   * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
204
   *
205
   * @param table - The table to update
206
   * @returns Update query builder (no versioning, no cache management)
207
   */
208
  update<TTable extends MySqlTable>(
4✔
209
    table: TTable,
13✔
210
  ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
13✔
211
    return this.drizzle.updateWithCacheContext(table);
13✔
212
  }
13✔
213

214
  /**
215
   * Creates a delete query builder.
216
   * 
217
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
218
   * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
219
   * 
220
   * @param table - The table to delete from
221
   * @returns Delete query builder (no versioning, no cache management)
222
   */
223
  delete<TTable extends MySqlTable>(
4✔
224
    table: TTable,
3✔
225
  ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
3✔
226
    return this.drizzle.deleteWithCacheContext(table);
3✔
227
  }
3✔
228
  /**
229
   * Creates a delete query builder that automatically evicts cache after execution.
230
   *
231
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
232
   * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
233
   * 
234
   * @param table - The table to delete from
235
   * @returns Delete query builder with automatic cache eviction (no versioning)
236
   */
237
  deleteAndEvictCache<TTable extends MySqlTable>(
4✔
NEW
238
    table: TTable,
×
NEW
239
  ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
240
    return this.drizzle.deleteAndEvictCache(table);
×
NEW
241
  }
×
242

243
  /**
244
   * Create the modify operations instance.
245
   * @returns modify operations.
246
   */
247
  modifyWithVersioning(): VerioningModificationForgeSQL {
4✔
248
    return this.crudOperations;
25✔
249
  }
25✔
250

251
  /**
252
   * Returns the singleton instance of ForgeSQLORMImpl.
253
   * @param options - Options for configuring ForgeSQL ORM behavior.
254
   * @returns The singleton instance of ForgeSQLORMImpl.
255
   */
256
  static getInstance(options?: ForgeSqlOrmOptions): ForgeSqlOperation {
4✔
257
    ForgeSQLORMImpl.instance ??= new ForgeSQLORMImpl(options);
42✔
258
    return ForgeSQLORMImpl.instance;
42✔
259
  }
42✔
260

261
  /**
262
   * Retrieves the fetch operations instance.
263
   * @returns Fetch operations.
264
   */
265
  fetch(): SchemaSqlForgeSql {
4✔
266
    return this.fetchOperations;
6✔
267
  }
6✔
268

269
  /**
270
   * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
271
   * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
272
   */
273
  analyze(): SchemaAnalyzeForgeSql {
4✔
274
    return this.analyzeOperations;
1✔
275
  }
1✔
276

277
  /**
278
   * Provides schema-level SQL operations with optimistic locking/versioning and automatic cache eviction.
279
   * 
280
   * This method returns operations that use `modifyWithVersioning()` internally, providing:
281
   * - Optimistic locking support
282
   * - Automatic version field management
283
   * - Cache eviction after successful operations
284
   * 
285
   * @returns {ForgeSQLCacheOperations} Interface for executing versioned SQL operations with cache management
286
   */
287
  modifyWithVersioningAndEvictCache(): ForgeSQLCacheOperations {
4✔
NEW
288
    return this.cacheOperations;
×
NEW
289
  }
×
290

291
  /**
292
   * Returns a Drizzle query builder instance.
293
   *
294
   * ⚠️ IMPORTANT: This method should be used ONLY for query building purposes.
295
   * The returned instance should NOT be used for direct database connections or query execution.
296
   * All database operations should be performed through Forge SQL's executeRawSQL or executeRawUpdateSQL methods.
297
   *
298
   * @returns A Drizzle query builder instance for query construction only.
299
   */
300
  getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>> & {
4✔
301
      selectAliased: SelectAliasedType;
302
      selectAliasedDistinct: SelectAliasedDistinctType;
303
      selectAliasedCacheable: SelectAliasedCacheableType;
304
      selectAliasedDistinctCacheable: SelectAliasedDistinctCacheableType;
305
      insertWithCacheContext: InsertAndEvictCacheType;
306
      insertAndEvictCache: InsertAndEvictCacheType;
307
      updateAndEvictCache: UpdateAndEvictCacheType;
308
      updateWithCacheContext: UpdateAndEvictCacheType;
309
      deleteAndEvictCache: DeleteAndEvictCacheType;
310
      deleteWithCacheContext: DeleteAndEvictCacheType;
311
  } {
8✔
312
    return this.drizzle;
8✔
313
  }
8✔
314

315
  /**
316
   * Creates a select query with unique field aliases to prevent field name collisions in joins.
317
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
318
   *
319
   * @template TSelection - The type of the selected fields
320
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
321
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
322
   * @throws {Error} If fields parameter is empty
323
   * @example
324
   * ```typescript
325
   * await forgeSQL
326
   *   .select({user: users, order: orders})
327
   *   .from(orders)
328
   *   .innerJoin(users, eq(orders.userId, users.id));
329
   * ```
330
   */
331
  select<TSelection extends SelectedFields>(
4✔
332
    fields: TSelection,
4✔
333
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
4✔
334
    if (!fields) {
4!
335
      throw new Error("fields is empty");
×
336
    }
×
337
    return this.drizzle.selectAliased(fields);
4✔
338
  }
4✔
339

340
  /**
341
   * Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
342
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
343
   *
344
   * @template TSelection - The type of the selected fields
345
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
346
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A distinct select query builder with unique field aliases
347
   * @throws {Error} If fields parameter is empty
348
   * @example
349
   * ```typescript
350
   * await forgeSQL
351
   *   .selectDistinct({user: users, order: orders})
352
   *   .from(orders)
353
   *   .innerJoin(users, eq(orders.userId, users.id));
354
   * ```
355
   */
356
  selectDistinct<TSelection extends SelectedFields>(
4✔
357
    fields: TSelection,
1✔
358
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
1✔
359
    if (!fields) {
1!
360
      throw new Error("fields is empty");
×
361
    }
×
362
    return this.drizzle.selectAliasedDistinct(fields);
1✔
363
  }
1✔
364

365
  /**
366
   * Creates a cacheable select query with unique field aliases to prevent field name collisions in joins.
367
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
368
   *
369
   * @template TSelection - The type of the selected fields
370
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
371
   * @param {number} cacheTTL - cache ttl optional default is 60 sec.
372
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
373
   * @throws {Error} If fields parameter is empty
374
   * @example
375
   * ```typescript
376
   * await forgeSQL
377
   *   .selectCacheable({user: users, order: orders},60)
378
   *   .from(orders)
379
   *   .innerJoin(users, eq(orders.userId, users.id));
380
   * ```
381
   */
382
  selectCacheable<TSelection extends SelectedFields>(
4✔
NEW
383
    fields: TSelection,
×
NEW
384
    cacheTTL?: number,
×
NEW
385
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
×
NEW
386
    if (!fields) {
×
NEW
387
      throw new Error("fields is empty");
×
NEW
388
    }
×
NEW
389
    return this.drizzle.selectAliasedCacheable(fields, cacheTTL);
×
NEW
390
  }
×
391

392
  /**
393
   * Creates a cacheable distinct select query with unique field aliases to prevent field name collisions in joins.
394
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
395
   *
396
   * @template TSelection - The type of the selected fields
397
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
398
   * @param {number} cacheTTL - cache ttl optional default is 60 sec.
399
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A distinct select query builder with unique field aliases
400
   * @throws {Error} If fields parameter is empty
401
   * @example
402
   * ```typescript
403
   * await forgeSQL
404
   *   .selectDistinctCacheable({user: users, order: orders}, 60)
405
   *   .from(orders)
406
   *   .innerJoin(users, eq(orders.userId, users.id));
407
   * ```
408
   */
409
  selectDistinctCacheable<TSelection extends SelectedFields>(
4✔
NEW
410
    fields: TSelection,
×
NEW
411
    cacheTTL?: number,
×
NEW
412
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
×
NEW
413
    if (!fields) {
×
NEW
414
      throw new Error("fields is empty");
×
NEW
415
    }
×
NEW
416
    return this.drizzle.selectAliasedDistinctCacheable(fields, cacheTTL);
×
NEW
417
  }
×
418
}
4✔
419

420
/**
421
 * Public class that acts as a wrapper around the private ForgeSQLORMImpl.
422
 * Provides a clean interface for working with Forge SQL and Drizzle ORM.
423
 */
424
class ForgeSQLORM implements ForgeSqlOperation {
1✔
425
  private readonly ormInstance: ForgeSqlOperation;
42✔
426

427
  constructor(options?: ForgeSqlOrmOptions) {
42✔
428
    this.ormInstance = ForgeSQLORMImpl.getInstance(options);
42✔
429
  }
42✔
430

431
  selectCacheable<TSelection extends SelectedFields>(
42✔
NEW
432
    fields: TSelection,
×
NEW
433
    cacheTTL?: number,
×
NEW
434
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
×
NEW
435
    return this.ormInstance.selectCacheable(fields, cacheTTL);
×
NEW
436
  }
×
437

438
  selectDistinctCacheable<TSelection extends SelectedFields>(
42✔
NEW
439
    fields: TSelection,
×
NEW
440
    cacheTTL?: number,
×
NEW
441
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
×
NEW
442
    return this.ormInstance.selectDistinctCacheable(fields, cacheTTL);
×
NEW
443
  }
×
444

445
  executeWithCacheContext(cacheContext: () => Promise<void>): Promise<void> {
42✔
NEW
446
    return this.ormInstance.executeWithCacheContext(cacheContext);
×
NEW
447
  }
×
448
  executeWithCacheContextAndReturnValue<T>(cacheContext: () => Promise<T>): Promise<T> {
42✔
NEW
449
    return this.ormInstance.executeWithCacheContextAndReturnValue(cacheContext);
×
NEW
450
  }
×
451

452
  /**
453
   * Creates an insert query builder.
454
   * 
455
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
456
   * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
457
   * 
458
   * @param table - The table to insert into
459
   * @returns Insert query builder (no versioning, no cache management)
460
   */
461
  insert<TTable extends MySqlTable>(
42✔
NEW
462
    table: TTable,
×
NEW
463
  ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
464
    return this.ormInstance.insert(table);
×
NEW
465
  }
×
466

467
  /**
468
   * Creates an insert query builder that automatically evicts cache after execution.
469
   *
470
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
471
   * For versioned inserts, use `modifyWithVersioning().insert()` or `modifyWithVersioningAndEvictCache().insert()` instead.
472
   *
473
   * @param table - The table to insert into
474
   * @returns Insert query builder with automatic cache eviction (no versioning)
475
   */
476
  insertAndEvictCache<TTable extends MySqlTable>(
42✔
NEW
477
    table: TTable,
×
NEW
478
  ): MySqlInsertBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
479
    return this.ormInstance.insertAndEvictCache(table);
×
NEW
480
  }
×
481

482
  /**
483
   * Creates an update query builder.
484
   * 
485
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
486
   * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
487
   * 
488
   * @param table - The table to update
489
   * @returns Update query builder (no versioning, no cache management)
490
   */
491
  update<TTable extends MySqlTable>(
42✔
NEW
492
    table: TTable,
×
NEW
493
  ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
494
    return this.ormInstance.update(table);
×
NEW
495
  }
×
496

497
  /**
498
   * Creates an update query builder that automatically evicts cache after execution.
499
   *
500
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
501
   * For versioned updates, use `modifyWithVersioning().updateById()` or `modifyWithVersioningAndEvictCache().updateById()` instead.
502
   *
503
   * @param table - The table to update
504
   * @returns Update query builder with automatic cache eviction (no versioning)
505
   */
506
  updateAndEvictCache<TTable extends MySqlTable>(
42✔
NEW
507
    table: TTable,
×
NEW
508
  ): MySqlUpdateBuilder<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
509
    return this.ormInstance.updateAndEvictCache(table);
×
NEW
510
  }
×
511

512
  /**
513
   * Creates a delete query builder.
514
   * 
515
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
516
   * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
517
   * 
518
   * @param table - The table to delete from
519
   * @returns Delete query builder (no versioning, no cache management)
520
   */
521
  delete<TTable extends MySqlTable>(
42✔
NEW
522
    table: TTable,
×
NEW
523
  ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
524
    return this.ormInstance.delete(table);
×
NEW
525
  }
×
526

527
  /**
528
   * Creates a delete query builder that automatically evicts cache after execution.
529
   *
530
   * ⚠️ **IMPORTANT**: This method does NOT support optimistic locking/versioning.
531
   * For versioned deletes, use `modifyWithVersioning().deleteById()` or `modifyWithVersioningAndEvictCache().deleteById()` instead.
532
   * 
533
   * @param table - The table to delete from
534
   * @returns Delete query builder with automatic cache eviction (no versioning)
535
   */
536
  deleteAndEvictCache<TTable extends MySqlTable>(
42✔
NEW
537
    table: TTable,
×
NEW
538
  ): MySqlDeleteBase<TTable, MySqlRemoteQueryResultHKT, MySqlRemotePreparedQueryHKT> {
×
NEW
539
    return this.ormInstance.deleteAndEvictCache(table);
×
NEW
540
  }
×
541

542
  /**
543
   * Creates a select query with unique field aliases to prevent field name collisions in joins.
544
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
545
   *
546
   * @template TSelection - The type of the selected fields
547
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
548
   * @returns {MySqlSelectBuilder<TSelection, MySql2PreparedQueryHKT>} A select query builder with unique field aliases
549
   * @throws {Error} If fields parameter is empty
550
   * @example
551
   * ```typescript
552
   * await forgeSQL
553
   *   .select({user: users, order: orders})
554
   *   .from(orders)
555
   *   .innerJoin(users, eq(orders.userId, users.id));
556
   * ```
557
   */
558
  select<TSelection extends SelectedFields>(
42✔
559
    fields: TSelection,
3✔
560
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
3✔
561
    return this.ormInstance.select(fields);
3✔
562
  }
3✔
563

564
  /**
565
   * Creates a distinct select query with unique field aliases to prevent field name collisions in joins.
566
   * This is particularly useful when working with Atlassian Forge SQL, which collapses fields with the same name in joined tables.
567
   *
568
   * @template TSelection - The type of the selected fields
569
   * @param {TSelection} fields - Object containing the fields to select, with table schemas as values
570
   * @returns {MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>} A distinct select query builder with unique field aliases
571
   * @throws {Error} If fields parameter is empty
572
   * @example
573
   * ```typescript
574
   * await forgeSQL
575
   *   .selectDistinct({user: users, order: orders})
576
   *   .from(orders)
577
   *   .innerJoin(users, eq(orders.userId, users.id));
578
   * ```
579
   */
580
  selectDistinct<TSelection extends SelectedFields>(
42✔
581
    fields: TSelection,
1✔
582
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT> {
1✔
583
    return this.ormInstance.selectDistinct(fields);
1✔
584
  }
1✔
585

586
  /**
587
   * Proxies the `modify` method from `ForgeSQLORMImpl`.
588
   * @returns Modify operations.
589
   */
590
  modifyWithVersioning(): VerioningModificationForgeSQL {
42✔
591
    return this.ormInstance.modifyWithVersioning();
25✔
592
  }
25✔
593

594
  /**
595
   * Proxies the `fetch` method from `ForgeSQLORMImpl`.
596
   * @returns Fetch operations.
597
   */
598
  fetch(): SchemaSqlForgeSql {
42✔
599
    return this.ormInstance.fetch();
5✔
600
  }
5✔
601

602
  /**
603
   * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
604
   * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
605
   */
606
  analyze(): SchemaAnalyzeForgeSql {
42✔
607
    return this.ormInstance.analyze();
1✔
608
  }
1✔
609

610
  /**
611
   * Provides schema-level SQL cacheable operations with type safety.
612
   * @returns {ForgeSQLCacheOperations} Interface for executing schema-bound SQL queries
613
   */
614
  modifyWithVersioningAndEvictCache(): ForgeSQLCacheOperations {
42✔
NEW
615
    return this.ormInstance.modifyWithVersioningAndEvictCache();
×
NEW
616
  }
×
617

618
  /**
619
   * Returns a Drizzle query builder instance.
620
   *
621
   * @returns A Drizzle query builder instance for query construction only.
622
   */
623
  getDrizzleQueryBuilder() {
42✔
624
    return this.ormInstance.getDrizzleQueryBuilder();
8✔
625
  }
8✔
626
}
42✔
627

628
export default ForgeSQLORM;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc