forge-sql-orm 2.0.18 → 2.0.20
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 +95 -4
- package/dist/ForgeSQLORM.js +315 -49
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +315 -49
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLAnalyseOperations.d.ts +250 -0
- package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -0
- package/dist/core/ForgeSQLORM.d.ts +12 -2
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +105 -9
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
- package/dist/core/SystemTables.d.ts +167 -0
- package/dist/core/SystemTables.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/utils/sqlUtils.d.ts +2 -2
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/dropMigrationWebTrigger.d.ts +2 -4
- package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
- package/package.json +11 -19
- package/src/core/ForgeSQLAnalyseOperations.ts +462 -0
- package/src/core/ForgeSQLORM.ts +43 -7
- package/src/core/ForgeSQLQueryBuilder.ts +121 -18
- package/src/core/ForgeSQLSelectOperations.ts +4 -6
- package/src/core/SystemTables.ts +175 -0
- package/src/index.ts +1 -0
- package/src/utils/forgeDriverProxy.ts +1 -1
- package/src/utils/sqlUtils.ts +10 -16
- package/src/webtriggers/applyMigrationsWebTrigger.ts +32 -16
- package/src/webtriggers/dropMigrationWebTrigger.ts +5 -6
- package/src/webtriggers/fetchSchemaWebTrigger.ts +2 -10
|
@@ -10,40 +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
17
|
import { SqlHints } from "../utils/sqlHints";
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
import {
|
|
19
|
+
ClusterStatementRowCamelCase,
|
|
20
|
+
ExplainAnalyzeRow,
|
|
21
|
+
SlowQueryNormalized,
|
|
22
|
+
} from "./SystemTables";
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
|
-
*
|
|
23
|
-
* Provides access to CRUD 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}
|
|
24
30
|
*/
|
|
25
31
|
export interface ForgeSqlOperation extends QueryBuilderForgeSql {
|
|
26
32
|
/**
|
|
27
|
-
* Provides CRUD (Create,
|
|
33
|
+
* Provides CRUD (Create, Update, Delete) operations.
|
|
34
|
+
* @deprecated Use modify() instead for better type safety and consistency
|
|
28
35
|
* @returns {CRUDForgeSQL} Interface for performing CRUD operations
|
|
29
36
|
*/
|
|
30
37
|
crud(): CRUDForgeSQL;
|
|
31
38
|
|
|
32
39
|
/**
|
|
33
|
-
* Provides
|
|
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.
|
|
34
47
|
* @returns {SchemaSqlForgeSql} Interface for executing schema-bound SQL queries
|
|
35
48
|
*/
|
|
36
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;
|
|
37
56
|
}
|
|
38
57
|
|
|
39
58
|
/**
|
|
40
59
|
* Interface for Query Builder operations.
|
|
41
|
-
* 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
|
|
42
63
|
*/
|
|
43
64
|
export interface QueryBuilderForgeSql {
|
|
44
65
|
/**
|
|
45
66
|
* Creates a new query builder for the given entity.
|
|
46
|
-
* @returns {
|
|
67
|
+
* @returns {MySqlRemoteDatabase<Record<string, unknown>>} The Drizzle database instance for building queries
|
|
47
68
|
*/
|
|
48
69
|
getDrizzleQueryBuilder(): MySqlRemoteDatabase<Record<string, unknown>>;
|
|
49
70
|
|
|
@@ -88,18 +109,18 @@ export interface QueryBuilderForgeSql {
|
|
|
88
109
|
): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
|
|
89
110
|
}
|
|
90
111
|
|
|
91
|
-
// ============= CRUD Operations =============
|
|
92
|
-
|
|
93
112
|
/**
|
|
94
|
-
* Interface for
|
|
113
|
+
* Interface for Modify (Create, Update, Delete) operations.
|
|
95
114
|
* Provides methods for basic database operations with support for optimistic locking.
|
|
115
|
+
*
|
|
116
|
+
* @interface CRUDForgeSQL
|
|
96
117
|
*/
|
|
97
118
|
export interface CRUDForgeSQL {
|
|
98
119
|
/**
|
|
99
120
|
* Inserts multiple records into the database.
|
|
100
121
|
* @template T - The type of the table schema
|
|
101
122
|
* @param {T} schema - The entity schema
|
|
102
|
-
* @param {
|
|
123
|
+
* @param {InferInsertModel<T>[]} models - The list of entities to insert
|
|
103
124
|
* @param {boolean} [updateIfExists] - Whether to update the row if it already exists (default: false)
|
|
104
125
|
* @returns {Promise<number>} The number of inserted rows
|
|
105
126
|
* @throws {Error} If the insert operation fails
|
|
@@ -159,11 +180,81 @@ export interface CRUDForgeSQL {
|
|
|
159
180
|
): Promise<number>;
|
|
160
181
|
}
|
|
161
182
|
|
|
162
|
-
|
|
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
|
+
}
|
|
163
252
|
|
|
164
253
|
/**
|
|
165
254
|
* Interface for schema-level SQL operations.
|
|
166
255
|
* Provides methods for executing SQL queries with schema binding and type safety.
|
|
256
|
+
*
|
|
257
|
+
* @interface SchemaSqlForgeSql
|
|
167
258
|
*/
|
|
168
259
|
export interface SchemaSqlForgeSql {
|
|
169
260
|
/**
|
|
@@ -200,11 +291,11 @@ export interface SchemaSqlForgeSql {
|
|
|
200
291
|
executeRawUpdateSQL(query: string, params?: unknown[]): Promise<UpdateQueryResponse>;
|
|
201
292
|
}
|
|
202
293
|
|
|
203
|
-
// ============= Configuration Types =============
|
|
204
|
-
|
|
205
294
|
/**
|
|
206
295
|
* Interface for version field metadata.
|
|
207
296
|
* Defines the configuration for optimistic locking version fields.
|
|
297
|
+
*
|
|
298
|
+
* @interface VersionFieldMetadata
|
|
208
299
|
*/
|
|
209
300
|
export interface VersionFieldMetadata {
|
|
210
301
|
/** Name of the version field */
|
|
@@ -214,6 +305,8 @@ export interface VersionFieldMetadata {
|
|
|
214
305
|
/**
|
|
215
306
|
* Interface for table metadata.
|
|
216
307
|
* Defines the configuration for a specific table.
|
|
308
|
+
*
|
|
309
|
+
* @interface TableMetadata
|
|
217
310
|
*/
|
|
218
311
|
export interface TableMetadata {
|
|
219
312
|
/** Name of the table */
|
|
@@ -225,11 +318,15 @@ export interface TableMetadata {
|
|
|
225
318
|
/**
|
|
226
319
|
* Type for additional metadata configuration.
|
|
227
320
|
* Maps table names to their metadata configuration.
|
|
321
|
+
*
|
|
322
|
+
* @type {AdditionalMetadata}
|
|
228
323
|
*/
|
|
229
324
|
export type AdditionalMetadata = Record<string, TableMetadata>;
|
|
230
325
|
|
|
231
326
|
/**
|
|
232
327
|
* Interface for ForgeSQL ORM options
|
|
328
|
+
*
|
|
329
|
+
* @interface ForgeSqlOrmOptions
|
|
233
330
|
*/
|
|
234
331
|
export interface ForgeSqlOrmOptions {
|
|
235
332
|
/** Whether to log raw SQL queries */
|
|
@@ -259,11 +356,11 @@ export interface ForgeSqlOrmOptions {
|
|
|
259
356
|
additionalMetadata?: AdditionalMetadata;
|
|
260
357
|
}
|
|
261
358
|
|
|
262
|
-
// ============= Custom Types =============
|
|
263
|
-
|
|
264
359
|
/**
|
|
265
360
|
* Custom type for MySQL datetime fields.
|
|
266
361
|
* Handles conversion between JavaScript Date objects and MySQL datetime strings.
|
|
362
|
+
*
|
|
363
|
+
* @type {CustomType}
|
|
267
364
|
*/
|
|
268
365
|
export const forgeDateTimeString = customType<{
|
|
269
366
|
data: Date;
|
|
@@ -285,6 +382,8 @@ export const forgeDateTimeString = customType<{
|
|
|
285
382
|
/**
|
|
286
383
|
* Custom type for MySQL timestamp fields.
|
|
287
384
|
* Handles conversion between JavaScript Date objects and MySQL timestamp strings.
|
|
385
|
+
*
|
|
386
|
+
* @type {CustomType}
|
|
288
387
|
*/
|
|
289
388
|
export const forgeTimestampString = customType<{
|
|
290
389
|
data: Date;
|
|
@@ -306,6 +405,8 @@ export const forgeTimestampString = customType<{
|
|
|
306
405
|
/**
|
|
307
406
|
* Custom type for MySQL date fields.
|
|
308
407
|
* Handles conversion between JavaScript Date objects and MySQL date strings.
|
|
408
|
+
*
|
|
409
|
+
* @type {CustomType}
|
|
309
410
|
*/
|
|
310
411
|
export const forgeDateString = customType<{
|
|
311
412
|
data: Date;
|
|
@@ -327,6 +428,8 @@ export const forgeDateString = customType<{
|
|
|
327
428
|
/**
|
|
328
429
|
* Custom type for MySQL time fields.
|
|
329
430
|
* Handles conversion between JavaScript Date objects and MySQL time strings.
|
|
431
|
+
*
|
|
432
|
+
* @type {CustomType}
|
|
330
433
|
*/
|
|
331
434
|
export const forgeTimeString = customType<{
|
|
332
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
|
-
|
|
61
|
-
|
|
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}` +
|
|
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();
|
package/src/core/SystemTables.ts
CHANGED
|
@@ -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
|
@@ -18,7 +18,7 @@ export function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: bool
|
|
|
18
18
|
// Inject SQL hints into the query
|
|
19
19
|
const modifiedQuery = injectSqlHints(query, options);
|
|
20
20
|
|
|
21
|
-
if (options && logRawSqlQuery) {
|
|
21
|
+
if (options && logRawSqlQuery && modifiedQuery !== query) {
|
|
22
22
|
console.warn("modified query: " + modifiedQuery);
|
|
23
23
|
}
|
|
24
24
|
// Call the original forgeDriver with the modified query
|
package/src/utils/sqlUtils.ts
CHANGED
|
@@ -145,7 +145,7 @@ function processForeignKeys(
|
|
|
145
145
|
const configBuilders = Array.isArray(configBuilderData)
|
|
146
146
|
? configBuilderData
|
|
147
147
|
: Object.values(configBuilderData).map(
|
|
148
|
-
(item) => (item as ConfigBuilderData).value
|
|
148
|
+
(item) => (item as ConfigBuilderData).value ?? item,
|
|
149
149
|
);
|
|
150
150
|
|
|
151
151
|
configBuilders.forEach((builder) => {
|
|
@@ -199,7 +199,7 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
|
|
|
199
199
|
const configBuilders = Array.isArray(configBuilderData)
|
|
200
200
|
? configBuilderData
|
|
201
201
|
: Object.values(configBuilderData).map(
|
|
202
|
-
(item) => (item as ConfigBuilderData).value
|
|
202
|
+
(item) => (item as ConfigBuilderData).value ?? item,
|
|
203
203
|
);
|
|
204
204
|
|
|
205
205
|
// Process each builder
|
|
@@ -240,22 +240,16 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
|
|
|
240
240
|
|
|
241
241
|
/**
|
|
242
242
|
* Generates SQL statements to drop tables
|
|
243
|
-
* @param tables - Array of table
|
|
243
|
+
* @param tables - Array of table names
|
|
244
244
|
* @returns Array of SQL statements for dropping tables
|
|
245
245
|
*/
|
|
246
|
-
export function generateDropTableStatements(tables:
|
|
246
|
+
export function generateDropTableStatements(tables: string[]): string[] {
|
|
247
247
|
const dropStatements: string[] = [];
|
|
248
248
|
|
|
249
|
-
tables.forEach((
|
|
250
|
-
|
|
251
|
-
if (tableMetadata.tableName) {
|
|
252
|
-
dropStatements.push(`DROP TABLE IF EXISTS \`${tableMetadata.tableName}\`;`);
|
|
253
|
-
}
|
|
249
|
+
tables.forEach((tableName) => {
|
|
250
|
+
dropStatements.push(`DROP TABLE IF EXISTS \`${tableName}\`;`);
|
|
254
251
|
});
|
|
255
252
|
|
|
256
|
-
// Add statement to clear migrations table
|
|
257
|
-
dropStatements.push(`DELETE FROM __migrations;`);
|
|
258
|
-
|
|
259
253
|
return dropStatements;
|
|
260
254
|
}
|
|
261
255
|
|
|
@@ -332,9 +326,9 @@ function getAliasFromDrizzleAlias(value: unknown): string | undefined {
|
|
|
332
326
|
const aliasNameChunk = queryChunks[queryChunks.length - 2];
|
|
333
327
|
if (isSQLWrapper(aliasNameChunk) && "queryChunks" in aliasNameChunk) {
|
|
334
328
|
const aliasNameChunkSql = aliasNameChunk as SQL;
|
|
335
|
-
if (aliasNameChunkSql
|
|
329
|
+
if (aliasNameChunkSql.queryChunks?.length === 1 && aliasNameChunkSql.queryChunks[0]) {
|
|
336
330
|
const queryChunksStringChunc = aliasNameChunkSql.queryChunks[0];
|
|
337
|
-
if (
|
|
331
|
+
if ("value" in queryChunksStringChunc) {
|
|
338
332
|
const values = (queryChunksStringChunc as StringChunk).value;
|
|
339
333
|
if (values && values.length === 1) {
|
|
340
334
|
return values[0];
|
|
@@ -407,7 +401,7 @@ export function applyFromDriverTransform<T, TSelection>(
|
|
|
407
401
|
}
|
|
408
402
|
|
|
409
403
|
function processNullBranches(obj: Record<string, unknown>): Record<string, unknown> | null {
|
|
410
|
-
if (obj === null || typeof obj !== "object"
|
|
404
|
+
if (obj === null || typeof obj !== "object") {
|
|
411
405
|
return obj;
|
|
412
406
|
}
|
|
413
407
|
|
|
@@ -425,7 +419,7 @@ function processNullBranches(obj: Record<string, unknown>): Record<string, unkno
|
|
|
425
419
|
continue;
|
|
426
420
|
}
|
|
427
421
|
|
|
428
|
-
if (typeof value === "object"
|
|
422
|
+
if (typeof value === "object") {
|
|
429
423
|
const processed = processNullBranches(value as Record<string, unknown>);
|
|
430
424
|
result[key] = processed;
|
|
431
425
|
if (processed !== null) {
|
|
@@ -27,23 +27,39 @@ import { MigrationRunner } from "@forge/sql/out/migration";
|
|
|
27
27
|
export const applySchemaMigrations = async (
|
|
28
28
|
migration: (migrationRunner: MigrationRunner) => Promise<MigrationRunner>,
|
|
29
29
|
) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const successfulMigrations = await migrations.run();
|
|
35
|
-
console.info("Migrations applied:", successfulMigrations);
|
|
30
|
+
try {
|
|
31
|
+
if (typeof migration !== "function") {
|
|
32
|
+
throw new Error("migration is not a function");
|
|
33
|
+
}
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.
|
|
35
|
+
console.log("Provisioning the database");
|
|
36
|
+
await sql._provision();
|
|
37
|
+
console.info("Running schema migrations");
|
|
38
|
+
const migrations = await migration(migrationRunner);
|
|
39
|
+
const successfulMigrations = await migrations.run();
|
|
40
|
+
console.info("Migrations applied:", successfulMigrations);
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
const migrationList = await migrationRunner.list();
|
|
43
|
+
const migrationHistory =
|
|
44
|
+
Array.isArray(migrationList) && migrationList.length > 0
|
|
45
|
+
? migrationList.map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`).join("\n")
|
|
46
|
+
: "No migrations found";
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
console.info("Migrations history:\nid, name, migrated_at\n", migrationHistory);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
headers: { "Content-Type": ["application/json"] },
|
|
52
|
+
statusCode: 200,
|
|
53
|
+
statusText: "OK",
|
|
54
|
+
body: "Migrations successfully executed",
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error("Error during migration:", error);
|
|
58
|
+
return {
|
|
59
|
+
headers: { "Content-Type": ["application/json"] },
|
|
60
|
+
statusCode: 500,
|
|
61
|
+
statusText: "Internal Server Error",
|
|
62
|
+
body: error instanceof Error ? error.message : "Unknown error during migration",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
49
65
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sql } from "@forge/sql";
|
|
2
|
-
import { AnyMySqlTable } from "drizzle-orm/mysql-core";
|
|
3
2
|
import { generateDropTableStatements as generateStatements } from "../utils/sqlUtils";
|
|
4
3
|
import { getHttpResponse, TriggerResponse } from "./index";
|
|
4
|
+
import { getTables } from "../core/SystemTables";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* ⚠️ DEVELOPMENT ONLY WEB TRIGGER ⚠️
|
|
@@ -15,7 +15,6 @@ import { getHttpResponse, TriggerResponse } from "./index";
|
|
|
15
15
|
* - It may affect application functionality
|
|
16
16
|
* - It could lead to data loss and system instability
|
|
17
17
|
*
|
|
18
|
-
* @param tables - Array of table schemas to drop
|
|
19
18
|
* @returns {Promise<TriggerResponse<string>>} A response containing:
|
|
20
19
|
* - On success: 200 status with warning message about permanent deletion
|
|
21
20
|
* - On failure: 500 status with error message
|
|
@@ -23,14 +22,13 @@ import { getHttpResponse, TriggerResponse } from "./index";
|
|
|
23
22
|
* @example
|
|
24
23
|
* ```typescript
|
|
25
24
|
* // Example usage in development only
|
|
26
|
-
* await dropSchemaMigrations(
|
|
25
|
+
* await dropSchemaMigrations();
|
|
27
26
|
* // ⚠️ Warning: This will permanently delete all data in users and orders tables
|
|
28
27
|
* ```
|
|
29
28
|
*/
|
|
30
|
-
export async function dropSchemaMigrations(
|
|
31
|
-
tables: AnyMySqlTable[],
|
|
32
|
-
): Promise<TriggerResponse<string>> {
|
|
29
|
+
export async function dropSchemaMigrations(): Promise<TriggerResponse<string>> {
|
|
33
30
|
try {
|
|
31
|
+
const tables = await getTables();
|
|
34
32
|
// Generate drop statements
|
|
35
33
|
const dropStatements = generateStatements(tables);
|
|
36
34
|
|
|
@@ -45,6 +43,7 @@ export async function dropSchemaMigrations(
|
|
|
45
43
|
"⚠️ All data in these tables has been permanently deleted. This operation cannot be undone.",
|
|
46
44
|
);
|
|
47
45
|
} catch (error: unknown) {
|
|
46
|
+
console.error(error);
|
|
48
47
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
49
48
|
return getHttpResponse<string>(500, errorMessage);
|
|
50
49
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sql } from "@forge/sql";
|
|
2
2
|
import { getHttpResponse, TriggerResponse } from "./index";
|
|
3
|
-
import { forgeSystemTables } from "../core/SystemTables";
|
|
3
|
+
import { forgeSystemTables, getTables } from "../core/SystemTables";
|
|
4
4
|
import { getTableName } from "drizzle-orm/table";
|
|
5
5
|
|
|
6
6
|
interface CreateTableRow {
|
|
@@ -47,14 +47,6 @@ export async function fetchSchemaWebTrigger(): Promise<TriggerResponse<string>>
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Retrieves all tables from the database
|
|
52
|
-
*/
|
|
53
|
-
async function getTables(): Promise<string[]> {
|
|
54
|
-
const tables = await sql.executeDDL<string>("SHOW TABLES");
|
|
55
|
-
return tables.rows.flatMap((tableInfo) => Object.values(tableInfo));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
50
|
/**
|
|
59
51
|
* Generates CREATE TABLE statements for each table
|
|
60
52
|
*/
|
|
@@ -62,7 +54,7 @@ async function generateCreateTableStatements(tables: string[]): Promise<string[]
|
|
|
62
54
|
const statements: string[] = [];
|
|
63
55
|
|
|
64
56
|
for (const table of tables) {
|
|
65
|
-
const createTableResult = await sql.executeDDL<CreateTableRow>(`SHOW CREATE TABLE ${table}`);
|
|
57
|
+
const createTableResult = await sql.executeDDL<CreateTableRow>(`SHOW CREATE TABLE "${table}"`);
|
|
66
58
|
|
|
67
59
|
const createTableStatements = createTableResult.rows
|
|
68
60
|
.filter((row) => !isSystemTable(row.Table))
|