forge-sql-orm 2.0.17 → 2.0.19

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.
Files changed (41) hide show
  1. package/README.md +95 -4
  2. package/dist/ForgeSQLORM.js +382 -60
  3. package/dist/ForgeSQLORM.js.map +1 -1
  4. package/dist/ForgeSQLORM.mjs +382 -60
  5. package/dist/ForgeSQLORM.mjs.map +1 -1
  6. package/dist/core/ForgeSQLAnalyseOperations.d.ts +250 -0
  7. package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -0
  8. package/dist/core/ForgeSQLCrudOperations.d.ts +1 -1
  9. package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
  10. package/dist/core/ForgeSQLORM.d.ts +12 -2
  11. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  12. package/dist/core/ForgeSQLQueryBuilder.d.ts +112 -21
  13. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  14. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  15. package/dist/core/SystemTables.d.ts +167 -0
  16. package/dist/core/SystemTables.d.ts.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/utils/forgeDriverProxy.d.ts +11 -0
  20. package/dist/utils/forgeDriverProxy.d.ts.map +1 -0
  21. package/dist/utils/sqlHints.d.ts +21 -0
  22. package/dist/utils/sqlHints.d.ts.map +1 -0
  23. package/dist/utils/sqlUtils.d.ts +2 -8
  24. package/dist/utils/sqlUtils.d.ts.map +1 -1
  25. package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
  26. package/dist/webtriggers/dropMigrationWebTrigger.d.ts +2 -4
  27. package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
  28. package/package.json +4 -12
  29. package/src/core/ForgeSQLAnalyseOperations.ts +461 -0
  30. package/src/core/ForgeSQLCrudOperations.ts +15 -8
  31. package/src/core/ForgeSQLORM.ts +46 -9
  32. package/src/core/ForgeSQLQueryBuilder.ts +129 -32
  33. package/src/core/ForgeSQLSelectOperations.ts +4 -6
  34. package/src/core/SystemTables.ts +175 -0
  35. package/src/index.ts +1 -0
  36. package/src/utils/forgeDriverProxy.ts +27 -0
  37. package/src/utils/sqlHints.ts +63 -0
  38. package/src/utils/sqlUtils.ts +36 -32
  39. package/src/webtriggers/applyMigrationsWebTrigger.ts +32 -16
  40. package/src/webtriggers/dropMigrationWebTrigger.ts +5 -6
  41. package/src/webtriggers/fetchSchemaWebTrigger.ts +2 -10
@@ -10,39 +10,61 @@ import {
10
10
  MySqlSelectDynamic,
11
11
  type SelectedFields,
12
12
  } from "drizzle-orm/mysql-core/query-builders/select.types";
13
- import { InferInsertModel, SQL } from "drizzle-orm";
13
+ import { InferInsertModel, Query, SQL } from "drizzle-orm";
14
14
  import moment from "moment/moment";
15
15
  import { parseDateTime } from "../utils/sqlUtils";
16
16
  import { MySqlRemoteDatabase, MySqlRemotePreparedQueryHKT } from "drizzle-orm/mysql-proxy/index";
17
-
18
- // ============= Core Types =============
17
+ import { SqlHints } from "../utils/sqlHints";
18
+ import {
19
+ ClusterStatementRowCamelCase,
20
+ ExplainAnalyzeRow,
21
+ SlowQueryNormalized,
22
+ } from "./SystemTables";
19
23
 
20
24
  /**
21
- * Interface representing the main ForgeSQL operations.
22
- * Provides access to CRUD operations and schema-level SQL operations.
25
+ * Core interface for ForgeSQL operations.
26
+ * Provides access to CRUD operations, schema-level SQL operations, and query analysis capabilities.
27
+ *
28
+ * @interface ForgeSqlOperation
29
+ * @extends {QueryBuilderForgeSql}
23
30
  */
24
31
  export interface ForgeSqlOperation extends QueryBuilderForgeSql {
25
32
  /**
26
- * Provides CRUD (Create, Read, Update, Delete) operations.
33
+ * Provides CRUD (Create, Update, Delete) operations.
34
+ * @deprecated Use modify() instead for better type safety and consistency
27
35
  * @returns {CRUDForgeSQL} Interface for performing CRUD operations
28
36
  */
29
37
  crud(): CRUDForgeSQL;
30
38
 
31
39
  /**
32
- * Provides schema-level SQL fetch operations.
40
+ * Provides modify (Create, Update, Delete) operations with optimistic locking support.
41
+ * @returns {CRUDForgeSQL} Interface for performing CRUD operations
42
+ */
43
+ modify(): CRUDForgeSQL;
44
+
45
+ /**
46
+ * Provides schema-level SQL fetch operations with type safety.
33
47
  * @returns {SchemaSqlForgeSql} Interface for executing schema-bound SQL queries
34
48
  */
35
49
  fetch(): SchemaSqlForgeSql;
50
+
51
+ /**
52
+ * Provides query analysis capabilities including EXPLAIN ANALYZE and slow query analysis.
53
+ * @returns {SchemaAnalyzeForgeSql} Interface for analyzing query performance
54
+ */
55
+ analyze(): SchemaAnalyzeForgeSql;
36
56
  }
37
57
 
38
58
  /**
39
59
  * Interface for Query Builder operations.
40
- * Provides access to the underlying Drizzle ORM query builder.
60
+ * Provides access to the underlying Drizzle ORM query builder with enhanced functionality.
61
+ *
62
+ * @interface QueryBuilderForgeSql
41
63
  */
42
64
  export interface QueryBuilderForgeSql {
43
65
  /**
44
66
  * Creates a new query builder for the given entity.
45
- * @returns {MySql2Database<Record<string, unknown>>} The Drizzle database instance for building queries
67
+ * @returns {MySqlRemoteDatabase<Record<string, unknown>>} The Drizzle database instance for building queries
46
68
  */
47
69
  getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>>;
48
70
 
@@ -87,25 +109,25 @@ export interface QueryBuilderForgeSql {
87
109
  ): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
88
110
  }
89
111
 
90
- // ============= CRUD Operations =============
91
-
92
112
  /**
93
- * Interface for CRUD (Create, Read, Update, Delete) operations.
113
+ * Interface for Modify (Create, Update, Delete) operations.
94
114
  * Provides methods for basic database operations with support for optimistic locking.
115
+ *
116
+ * @interface CRUDForgeSQL
95
117
  */
96
118
  export interface CRUDForgeSQL {
97
119
  /**
98
120
  * Inserts multiple records into the database.
99
121
  * @template T - The type of the table schema
100
122
  * @param {T} schema - The entity schema
101
- * @param {Partial<InferInsertModel<T>>[]} models - The list of entities to insert
123
+ * @param {InferInsertModel<T>[]} models - The list of entities to insert
102
124
  * @param {boolean} [updateIfExists] - Whether to update the row if it already exists (default: false)
103
125
  * @returns {Promise<number>} The number of inserted rows
104
126
  * @throws {Error} If the insert operation fails
105
127
  */
106
128
  insert<T extends AnyMySqlTable>(
107
129
  schema: T,
108
- models: Partial<InferInsertModel<T>>[],
130
+ models: InferInsertModel<T>[],
109
131
  updateIfExists?: boolean,
110
132
  ): Promise<number>;
111
133
 
@@ -158,11 +180,81 @@ export interface CRUDForgeSQL {
158
180
  ): Promise<number>;
159
181
  }
160
182
 
161
- // ============= Schema SQL Operations =============
183
+ /**
184
+ * Interface for schema analysis operations.
185
+ * Provides methods for analyzing query performance and execution plans.
186
+ *
187
+ * @interface SchemaAnalyzeForgeSql
188
+ */
189
+ export interface SchemaAnalyzeForgeSql {
190
+ /**
191
+ * Executes EXPLAIN on a Drizzle query.
192
+ * @param {{ toSQL: () => Query }} query - The Drizzle query to analyze
193
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
194
+ */
195
+ explain(query: { toSQL: () => Query }): Promise<ExplainAnalyzeRow[]>;
196
+
197
+ /**
198
+ * Executes EXPLAIN on a raw SQL query.
199
+ * @param {string} query - The SQL query to analyze
200
+ * @param {unknown[]} bindParams - The query parameters
201
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
202
+ */
203
+ explainRaw(query: string, bindParams: unknown[]): Promise<ExplainAnalyzeRow[]>;
204
+
205
+ /**
206
+ * Executes EXPLAIN ANALYZE on a Drizzle query.
207
+ * @param {{ toSQL: () => Query }} query - The Drizzle query to analyze
208
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
209
+ */
210
+ explainAnalyze(query: { toSQL: () => Query }): Promise<ExplainAnalyzeRow[]>;
211
+
212
+ /**
213
+ * Executes EXPLAIN ANALYZE on a raw SQL query.
214
+ * @param {string} query - The SQL query to analyze
215
+ * @param {unknown[]} bindParams - The query parameters
216
+ * @returns {Promise<ExplainAnalyzeRow[]>} The execution plan analysis results
217
+ */
218
+ explainAnalyzeRaw(query: string, bindParams: unknown[]): Promise<ExplainAnalyzeRow[]>;
219
+
220
+ /**
221
+ * Analyzes slow queries from the database.
222
+ * @returns {Promise<SlowQueryNormalized[]>} The normalized slow query data
223
+ */
224
+ analyzeSlowQueries(): Promise<SlowQueryNormalized[]>;
225
+
226
+ /**
227
+ * Analyzes query history for specific tables using Drizzle table objects.
228
+ * @param {AnyMySqlTable[]} tables - The Drizzle table objects to analyze
229
+ * @param {Date} [fromDate] - The start date for the analysis
230
+ * @param {Date} [toDate] - The end date for the analysis
231
+ * @returns {Promise<ClusterStatementRowCamelCase[]>} The analyzed query history
232
+ */
233
+ analyzeQueriesHistory(
234
+ tables: AnyMySqlTable[],
235
+ fromDate?: Date,
236
+ toDate?: Date,
237
+ ): Promise<ClusterStatementRowCamelCase[]>;
238
+
239
+ /**
240
+ * Analyzes query history for specific tables using raw table names.
241
+ * @param {string[]} tables - The table names to analyze
242
+ * @param {Date} [fromDate] - The start date for the analysis
243
+ * @param {Date} [toDate] - The end date for the analysis
244
+ * @returns {Promise<ClusterStatementRowCamelCase[]>} The analyzed query history
245
+ */
246
+ analyzeQueriesHistoryRaw(
247
+ tables: string[],
248
+ fromDate?: Date,
249
+ toDate?: Date,
250
+ ): Promise<ClusterStatementRowCamelCase[]>;
251
+ }
162
252
 
163
253
  /**
164
254
  * Interface for schema-level SQL operations.
165
255
  * Provides methods for executing SQL queries with schema binding and type safety.
256
+ *
257
+ * @interface SchemaSqlForgeSql
166
258
  */
167
259
  export interface SchemaSqlForgeSql {
168
260
  /**
@@ -199,11 +291,11 @@ export interface SchemaSqlForgeSql {
199
291
  executeRawUpdateSQL(query: string, params?: unknown[]): Promise<UpdateQueryResponse>;
200
292
  }
201
293
 
202
- // ============= Configuration Types =============
203
-
204
294
  /**
205
295
  * Interface for version field metadata.
206
296
  * Defines the configuration for optimistic locking version fields.
297
+ *
298
+ * @interface VersionFieldMetadata
207
299
  */
208
300
  export interface VersionFieldMetadata {
209
301
  /** Name of the version field */
@@ -213,6 +305,8 @@ export interface VersionFieldMetadata {
213
305
  /**
214
306
  * Interface for table metadata.
215
307
  * Defines the configuration for a specific table.
308
+ *
309
+ * @interface TableMetadata
216
310
  */
217
311
  export interface TableMetadata {
218
312
  /** Name of the table */
@@ -224,26 +318,23 @@ export interface TableMetadata {
224
318
  /**
225
319
  * Type for additional metadata configuration.
226
320
  * Maps table names to their metadata configuration.
321
+ *
322
+ * @type {AdditionalMetadata}
227
323
  */
228
324
  export type AdditionalMetadata = Record<string, TableMetadata>;
229
325
 
230
326
  /**
231
- * Options for configuring ForgeSQL ORM behavior.
327
+ * Interface for ForgeSQL ORM options
328
+ *
329
+ * @interface ForgeSqlOrmOptions
232
330
  */
233
331
  export interface ForgeSqlOrmOptions {
234
- /**
235
- * Enables logging of raw SQL queries in the Atlassian Forge Developer Console.
236
- * Useful for debugging and monitoring SQL operations.
237
- * @default false
238
- */
332
+ /** Whether to log raw SQL queries */
239
333
  logRawSqlQuery?: boolean;
240
-
241
- /**
242
- * Disables optimistic locking for all operations.
243
- * When enabled, version checks are skipped during updates.
244
- * @default false
245
- */
334
+ /** Whether to disable optimistic locking */
246
335
  disableOptimisticLocking?: boolean;
336
+ /** SQL hints to be applied to queries */
337
+ hints?: SqlHints;
247
338
 
248
339
  /**
249
340
  * Additional metadata for table configuration.
@@ -265,11 +356,11 @@ export interface ForgeSqlOrmOptions {
265
356
  additionalMetadata?: AdditionalMetadata;
266
357
  }
267
358
 
268
- // ============= Custom Types =============
269
-
270
359
  /**
271
360
  * Custom type for MySQL datetime fields.
272
361
  * Handles conversion between JavaScript Date objects and MySQL datetime strings.
362
+ *
363
+ * @type {CustomType}
273
364
  */
274
365
  export const forgeDateTimeString = customType<{
275
366
  data: Date;
@@ -291,6 +382,8 @@ export const forgeDateTimeString = customType<{
291
382
  /**
292
383
  * Custom type for MySQL timestamp fields.
293
384
  * Handles conversion between JavaScript Date objects and MySQL timestamp strings.
385
+ *
386
+ * @type {CustomType}
294
387
  */
295
388
  export const forgeTimestampString = customType<{
296
389
  data: Date;
@@ -301,7 +394,7 @@ export const forgeTimestampString = customType<{
301
394
  return "timestamp";
302
395
  },
303
396
  toDriver(value: Date) {
304
- return moment(value as Date).format("YYYY-MM-DDTHH:mm:ss.SSS");
397
+ return moment(new Date(value)).format("YYYY-MM-DDTHH:mm:ss.SSS");
305
398
  },
306
399
  fromDriver(value: unknown) {
307
400
  const format = "YYYY-MM-DDTHH:mm:ss.SSS";
@@ -312,6 +405,8 @@ export const forgeTimestampString = customType<{
312
405
  /**
313
406
  * Custom type for MySQL date fields.
314
407
  * Handles conversion between JavaScript Date objects and MySQL date strings.
408
+ *
409
+ * @type {CustomType}
315
410
  */
316
411
  export const forgeDateString = customType<{
317
412
  data: Date;
@@ -333,6 +428,8 @@ export const forgeDateString = customType<{
333
428
  /**
334
429
  * Custom type for MySQL time fields.
335
430
  * Handles conversion between JavaScript Date objects and MySQL time strings.
431
+ *
432
+ * @type {CustomType}
336
433
  */
337
434
  export const forgeTimeString = customType<{
338
435
  data: Date;
@@ -57,9 +57,8 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
57
57
  */
58
58
  async executeRawSQL<T extends object | unknown>(query: string, params?: unknown[]): Promise<T[]> {
59
59
  if (this.options.logRawSqlQuery) {
60
- console.debug(
61
- `Executing with SQL ${query}` + params ? `, with params: ${JSON.stringify(params)}` : "",
62
- );
60
+ const paramsStr = params ? `, with params: ${JSON.stringify(params)}` : "";
61
+ console.debug(`Executing with SQL ${query}${paramsStr}`);
63
62
  }
64
63
  const sqlStatement = sql.prepare<T>(query);
65
64
  if (params) {
@@ -82,9 +81,8 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
82
81
  }
83
82
  if (this.options.logRawSqlQuery) {
84
83
  console.debug(
85
- `Executing Update with SQL ${query}` + params
86
- ? `, with params: ${JSON.stringify(params)}`
87
- : "",
84
+ `Executing Update with SQL ${query}` +
85
+ (params ? `, with params: ${JSON.stringify(params)}` : ""),
88
86
  );
89
87
  }
90
88
  const updateQueryResponseResults = await sqlStatement.execute();
@@ -1,5 +1,6 @@
1
1
  import { bigint, mysqlTable, timestamp, varchar } from "drizzle-orm/mysql-core";
2
2
  import { Table } from "drizzle-orm";
3
+ import { sql } from "@forge/sql";
3
4
 
4
5
  export const migrations = mysqlTable("__migrations", {
5
6
  id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
@@ -7,4 +8,178 @@ export const migrations = mysqlTable("__migrations", {
7
8
  migratedAt: timestamp("migratedAt").defaultNow().notNull(),
8
9
  });
9
10
 
11
+ export interface ExplainAnalyzeRow {
12
+ id: string;
13
+ estRows?: string;
14
+ estCost?: string;
15
+ actRows?: string;
16
+ task?: string;
17
+ accessObject?: string;
18
+ executionInfo?: string;
19
+ operatorInfo?: string;
20
+ memory?: string;
21
+ disk?: string;
22
+ }
23
+
24
+ export interface SlowQueryNormalized {
25
+ time: string;
26
+ txnStartTs: number;
27
+ user: string;
28
+ host: string;
29
+ connId: number;
30
+ db: string;
31
+ query: string;
32
+ digest: string;
33
+ queryTime: number;
34
+ compileTime: number;
35
+ optimizeTime: number;
36
+ processTime: number;
37
+ waitTime: number;
38
+ parseTime: number;
39
+ rewriteTime: number;
40
+ copTime: number;
41
+ copProcAvg: number;
42
+ copProcMax: number;
43
+ copProcP90: number;
44
+ copProcAddr: string;
45
+ copWaitAvg: number;
46
+ copWaitMax: number;
47
+ copWaitP90: number;
48
+ copWaitAddr: string;
49
+ memMax: number;
50
+ diskMax: number;
51
+ totalKeys: number;
52
+ processKeys: number;
53
+ requestCount: number;
54
+ kvTotal: number;
55
+ pdTotal: number;
56
+ resultRows: number;
57
+ rocksdbBlockCacheHitCount: number;
58
+ rocksdbBlockReadCount: number;
59
+ rocksdbBlockReadByte: number;
60
+ plan: string;
61
+ parsedPlan?: ExplainAnalyzeRow[];
62
+ binaryPlan: string;
63
+ planDigest: string;
64
+ }
65
+
66
+ export interface ClusterStatementRowCamelCase {
67
+ instance: string;
68
+ summaryBeginTime: string;
69
+ summaryEndTime: string;
70
+ stmtType: string;
71
+ schemaName: string;
72
+ digest: string;
73
+ digestText: string;
74
+ tableNames: string;
75
+ indexNames: string | null;
76
+ sampleUser: string;
77
+ execCount: number;
78
+ sumErrors: number;
79
+ sumWarnings: number;
80
+ sumLatency: number;
81
+ maxLatency: number;
82
+ minLatency: number;
83
+ avgLatency: number;
84
+ avgParseLatency: number;
85
+ maxParseLatency: number;
86
+ avgCompileLatency: number;
87
+ maxCompileLatency: number;
88
+ sumCopTaskNum: number;
89
+ maxCopProcessTime: number;
90
+ maxCopProcessAddress: string;
91
+ maxCopWaitTime: number;
92
+ maxCopWaitAddress: string;
93
+ avgProcessTime: number;
94
+ maxProcessTime: number;
95
+ avgWaitTime: number;
96
+ maxWaitTime: number;
97
+ avgBackoffTime: number;
98
+ maxBackoffTime: number;
99
+ avgTotalKeys: number;
100
+ maxTotalKeys: number;
101
+ avgProcessedKeys: number;
102
+ maxProcessedKeys: number;
103
+ avgRocksdbDeleteSkippedCount: number;
104
+ maxRocksdbDeleteSkippedCount: number;
105
+ avgRocksdbKeySkippedCount: number;
106
+ maxRocksdbKeySkippedCount: number;
107
+ avgRocksdbBlockCacheHitCount: number;
108
+ maxRocksdbBlockCacheHitCount: number;
109
+ avgRocksdbBlockReadCount: number;
110
+ maxRocksdbBlockReadCount: number;
111
+ avgRocksdbBlockReadByte: number;
112
+ maxRocksdbBlockReadByte: number;
113
+ avgPrewriteTime: number;
114
+ maxPrewriteTime: number;
115
+ avgCommitTime: number;
116
+ maxCommitTime: number;
117
+ avgGetCommitTsTime: number;
118
+ maxGetCommitTsTime: number;
119
+ avgCommitBackoffTime: number;
120
+ maxCommitBackoffTime: number;
121
+ avgResolveLockTime: number;
122
+ maxResolveLockTime: number;
123
+ avgLocalLatchWaitTime: number;
124
+ maxLocalLatchWaitTime: number;
125
+ avgWriteKeys: number;
126
+ maxWriteKeys: number;
127
+ avgWriteSize: number;
128
+ maxWriteSize: number;
129
+ avgPrewriteRegions: number;
130
+ maxPrewriteRegions: number;
131
+ avgTxnRetry: number;
132
+ maxTxnRetry: number;
133
+ sumExecRetry: number;
134
+ sumExecRetryTime: number;
135
+ sumBackoffTimes: number;
136
+ backoffTypes: string | null;
137
+ avgMem: number;
138
+ maxMem: number;
139
+ avgDisk: number;
140
+ maxDisk: number;
141
+ avgKvTime: number;
142
+ avgPdTime: number;
143
+ avgBackoffTotalTime: number;
144
+ avgWriteSqlRespTime: number;
145
+ avgTidbCpuTime: number;
146
+ avgTikvCpuTime: number;
147
+ maxResultRows: number;
148
+ minResultRows: number;
149
+ avgResultRows: number;
150
+ prepared: number;
151
+ avgAffectedRows: number;
152
+ firstSeen: string;
153
+ lastSeen: string;
154
+ planInCache: number;
155
+ planCacheHits: number;
156
+ planInBinding: number;
157
+ querySampleText: string;
158
+ prevSampleText: string;
159
+ planDigest: string;
160
+ plan: string;
161
+ binaryPlan: string;
162
+ charset: string;
163
+ collation: string;
164
+ planHint: string;
165
+ maxRequestUnitRead: number;
166
+ avgRequestUnitRead: number;
167
+ maxRequestUnitWrite: number;
168
+ avgRequestUnitWrite: number;
169
+ maxQueuedRcTime: number;
170
+ avgQueuedRcTime: number;
171
+ resourceGroup: string;
172
+ planCacheUnqualified: number;
173
+ planCacheUnqualifiedLastReason: string;
174
+ parsedPlan?: ExplainAnalyzeRow[];
175
+ }
176
+
177
+ /**
178
+ * Retrieves all tables from the database
179
+ */
180
+ export async function getTables(): Promise<string[]> {
181
+ const tables = await sql.executeDDL<string>("SHOW TABLES");
182
+ return tables.rows.flatMap((tableInfo) => Object.values(tableInfo));
183
+ }
184
+
10
185
  export const forgeSystemTables: Table[] = [migrations];
package/src/index.ts CHANGED
@@ -7,5 +7,6 @@ export * from "./utils/sqlUtils";
7
7
  export * from "./utils/forgeDriver";
8
8
  export * from "./webtriggers";
9
9
  export * from "./lib/drizzle/extensions/selectAliased";
10
+ export * from "./core/SystemTables";
10
11
 
11
12
  export default ForgeSQLORM;
@@ -0,0 +1,27 @@
1
+ import { forgeDriver } from "./forgeDriver";
2
+ import { injectSqlHints, SqlHints } from "./sqlHints";
3
+
4
+ /**
5
+ * Creates a proxy for the forgeDriver that injects SQL hints
6
+ * @returns A proxied version of the forgeDriver
7
+ */
8
+ export function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: boolean) {
9
+ return async (
10
+ query: string,
11
+ params: any[],
12
+ method: "all" | "execute",
13
+ ): Promise<{
14
+ rows: any[];
15
+ insertId?: number;
16
+ affectedRows?: number;
17
+ }> => {
18
+ // Inject SQL hints into the query
19
+ const modifiedQuery = injectSqlHints(query, options);
20
+
21
+ if (options && logRawSqlQuery && modifiedQuery !== query) {
22
+ console.warn("modified query: " + modifiedQuery);
23
+ }
24
+ // Call the original forgeDriver with the modified query
25
+ return forgeDriver(modifiedQuery, params, method);
26
+ };
27
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Interface for SQL hints configuration
3
+ */
4
+ export interface SqlHints {
5
+ /** SQL hints for SELECT queries */
6
+ select?: string[];
7
+ /** SQL hints for INSERT queries */
8
+ insert?: string[];
9
+ /** SQL hints for UPDATE queries */
10
+ update?: string[];
11
+ /** SQL hints for DELETE queries */
12
+ delete?: string[];
13
+ }
14
+
15
+ /**
16
+ * Detects the type of SQL query and injects appropriate hints
17
+ * @param query - The SQL query to analyze
18
+ * @param hints - The hints configuration
19
+ * @returns The modified query with injected hints
20
+ */
21
+ export function injectSqlHints(query: string, hints?: SqlHints): string {
22
+ if (!hints) {
23
+ return query;
24
+ }
25
+
26
+ // Normalize the query for easier matching
27
+ const normalizedQuery = query.trim().toUpperCase();
28
+
29
+ // Get the appropriate hints based on query type
30
+ let queryHints: string[] | undefined;
31
+
32
+ if (normalizedQuery.startsWith("SELECT")) {
33
+ queryHints = hints.select;
34
+ } else if (normalizedQuery.startsWith("INSERT")) {
35
+ queryHints = hints.insert;
36
+ } else if (normalizedQuery.startsWith("UPDATE")) {
37
+ queryHints = hints.update;
38
+ } else if (normalizedQuery.startsWith("DELETE")) {
39
+ queryHints = hints.delete;
40
+ }
41
+
42
+ // If no hints for this query type, return original query
43
+ if (!queryHints || queryHints.length === 0) {
44
+ return query;
45
+ }
46
+
47
+ // Join all hints with spaces
48
+ const hintsString = queryHints.join(" ");
49
+
50
+ // Inject hints into the query
51
+ if (normalizedQuery.startsWith("SELECT")) {
52
+ return `SELECT /*+ ${hintsString} */ ${query.substring(6)}`;
53
+ } else if (normalizedQuery.startsWith("INSERT")) {
54
+ return `INSERT /*+ ${hintsString} */ ${query.substring(6)}`;
55
+ } else if (normalizedQuery.startsWith("UPDATE")) {
56
+ return `UPDATE /*+ ${hintsString} */ ${query.substring(6)}`;
57
+ } else if (normalizedQuery.startsWith("DELETE")) {
58
+ return `DELETE /*+ ${hintsString} */ ${query.substring(6)}`;
59
+ }
60
+
61
+ // If no match found, return original query
62
+ return query;
63
+ }