befly 3.20.11 → 3.21.0

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.
@@ -1,578 +0,0 @@
1
- import { isNumber } from "../../utils/is.js";
2
- import { snakeCase } from "../../utils/util.js";
3
- import { Logger } from "../logger.js";
4
- import { SqlBuilder } from "../sqlBuilder/index.js";
5
- import { normalizeSqlMetaNumber, quoteIdentMySql } from "./util.js";
6
- import { addDefaultStateFilter, buildInsertRow, buildPartialUpdateData, buildUpdateRow, clearDeep, whereKeysToSnake } from "./builders.js";
7
- import { assertBatchInsertRowsConsistent, assertNoUndefinedInRecord, validateGeneratedBatchId, validateIncrementOptions, validateInsertBatchSize, validateNoLeftJoinReadOptions, validatePageLimitRange, validateSafeFieldName, validateTableBatchDataOptions, validateTableDataOptions, validateTableName, validateTableWhereOptions } from "./validate.js";
8
-
9
- export const dataOpsMethods = {
10
- prepareSingleTableWhere(table, where, useDefaultStateFilter = true) {
11
- const snakeTable = snakeCase(table);
12
- const snakeWhere = whereKeysToSnake(clearDeep(where || {}));
13
-
14
- return {
15
- snakeTable: snakeTable,
16
- whereFiltered: useDefaultStateFilter ? addDefaultStateFilter(snakeWhere, snakeTable, false, this.beflyMode) : snakeWhere
17
- };
18
- },
19
-
20
- async createInsertRows(table, snakeTable, dataList, now) {
21
- if (this.beflyMode === "manual") {
22
- return {
23
- ids: [],
24
- processedList: dataList.map((data) =>
25
- buildInsertRow({
26
- data: data,
27
- now: now,
28
- beflyMode: this.beflyMode
29
- })
30
- )
31
- };
32
- }
33
-
34
- const ids = [];
35
- try {
36
- for (let i = 0; i < dataList.length; i++) {
37
- ids.push(await this.redis.genTimeID());
38
- }
39
- } catch (error) {
40
- if (dataList.length === 1) {
41
- throw new Error(`生成 ID 失败,Redis 可能不可用 (table: ${table})`, {
42
- cause: error,
43
- code: "runtime",
44
- subsystem: "db",
45
- operation: "genTimeId",
46
- table: table
47
- });
48
- }
49
- throw error;
50
- }
51
-
52
- const processedList = dataList.map((data, index) => {
53
- const id = ids[index];
54
- if (dataList.length > 1) {
55
- validateGeneratedBatchId(id, snakeTable, index);
56
- }
57
-
58
- return buildInsertRow({
59
- data: data,
60
- id: id,
61
- now: now,
62
- beflyMode: this.beflyMode
63
- });
64
- });
65
-
66
- return {
67
- ids: ids,
68
- processedList: processedList
69
- };
70
- },
71
-
72
- resolveInsertedData(table, count, executeRes, generatedIds, operation) {
73
- const lastInsertRowidNum = normalizeSqlMetaNumber(executeRes.data?.lastInsertRowid);
74
-
75
- if (this.beflyMode === "manual") {
76
- if (lastInsertRowidNum <= 0) {
77
- throw new Error(operation === "insBatch" ? `批量插入失败:beflyMode=manual 时无法获取 lastInsertRowid (table: ${table})` : `插入失败:beflyMode=manual 时无法获取 lastInsertRowid (table: ${table})`, {
78
- cause: null,
79
- code: "runtime"
80
- });
81
- }
82
-
83
- if (count === 1) {
84
- return lastInsertRowidNum;
85
- }
86
-
87
- const outIds = [];
88
- for (let i = 0; i < count; i++) {
89
- outIds.push(lastInsertRowidNum + i);
90
- }
91
- return outIds;
92
- }
93
-
94
- if (count === 1) {
95
- const generatedId = generatedIds[0];
96
- return (isNumber(generatedId) ? generatedId : 0) || lastInsertRowidNum || 0;
97
- }
98
-
99
- return generatedIds;
100
- },
101
-
102
- // 读取操作
103
- async getCount(options) {
104
- const { prepared, whereFiltered } = await this.prepareReadContext(options, "getCount.options");
105
- const result = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as count");
106
-
107
- return {
108
- data: result.total,
109
- sql: result.sql
110
- };
111
- },
112
-
113
- async getOne(options) {
114
- const { prepared, whereFiltered } = await this.prepareReadContext(options, "getOne.options");
115
- const builder = this.createSqlBuilder().select(prepared.fields).from(prepared.table).where(whereFiltered);
116
- this.applyLeftJoins(builder, prepared.leftJoins);
117
-
118
- const { sql, params } = builder.toSelectSql();
119
- const executeRes = await this.execute(sql, params);
120
- const result = executeRes.data;
121
-
122
- const data = this.normalizeRowData(result?.[0] || null);
123
- return {
124
- data: data,
125
- sql: executeRes.sql
126
- };
127
- },
128
-
129
- async getDetail(options) {
130
- return await this.getOne(options);
131
- },
132
-
133
- async getList(options) {
134
- const { prepared, whereFiltered } = await this.prepareReadContext(options, "getList.options");
135
- validatePageLimitRange(prepared, options.table);
136
- const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
137
- const total = countResult.total;
138
-
139
- const offset = (prepared.page - 1) * prepared.limit;
140
- const dataBuilder = this.createListBuilder(prepared, whereFiltered, prepared.limit, offset);
141
- const { sql: dataSql, params: dataParams } = dataBuilder.toSelectSql();
142
-
143
- if (total === 0) {
144
- return {
145
- data: {
146
- lists: [],
147
- total: 0,
148
- page: prepared.page,
149
- limit: prepared.limit,
150
- pages: 0
151
- },
152
- sql: {
153
- count: countResult.sql,
154
- data: {
155
- sql: dataSql,
156
- params: dataParams,
157
- duration: 0
158
- }
159
- }
160
- };
161
- }
162
- const listExecuteRes = await this.execute(dataSql, dataParams);
163
- const list = listExecuteRes.data || [];
164
-
165
- return {
166
- data: {
167
- lists: this.normalizeListData(list),
168
- total: total,
169
- page: prepared.page,
170
- limit: prepared.limit,
171
- pages: Math.ceil(total / prepared.limit)
172
- },
173
- sql: {
174
- count: countResult.sql,
175
- data: listExecuteRes.sql
176
- }
177
- };
178
- },
179
-
180
- async getAll(options) {
181
- const MAX_LIMIT = 100000;
182
- const WARNING_LIMIT = 10000;
183
- const prepareOptions = this.buildQueryOptions(options, { page: 1, limit: 10 });
184
- const { prepared, whereFiltered } = await this.prepareReadContext(prepareOptions, "getAll.options");
185
- const countResult = await this.fetchCount(prepared, whereFiltered, "COUNT(*) as total");
186
- const total = countResult.total;
187
-
188
- if (total === 0) {
189
- return {
190
- data: {
191
- lists: [],
192
- total: 0
193
- },
194
- sql: {
195
- count: countResult.sql
196
- }
197
- };
198
- }
199
-
200
- const dataBuilder = this.createListBuilder(prepared, whereFiltered, MAX_LIMIT, null);
201
-
202
- const { sql: dataSql, params: dataParams } = dataBuilder.toSelectSql();
203
- const listExecuteRes = await this.execute(dataSql, dataParams);
204
- const result = listExecuteRes.data || [];
205
-
206
- if (result.length >= WARNING_LIMIT) {
207
- Logger.warn("getAll 返回数据过多,建议使用 getList 分页查询", { table: options.table, count: result.length, total: total });
208
- }
209
-
210
- if (result.length >= MAX_LIMIT) {
211
- Logger.warn(`getAll 达到最大限制 ${MAX_LIMIT},实际总数 ${total},只返回前 ${MAX_LIMIT} 条`, { table: options.table, limit: MAX_LIMIT, total: total });
212
- }
213
-
214
- const lists = this.normalizeListData(result);
215
-
216
- return {
217
- data: {
218
- lists: lists,
219
- total: total
220
- },
221
- sql: {
222
- count: countResult.sql,
223
- data: listExecuteRes.sql
224
- }
225
- };
226
- },
227
-
228
- async exists(options) {
229
- validateNoLeftJoinReadOptions(options, "exists", "exists 不支持 leftJoin(请使用显式 query 或拆分查询)");
230
- const prepared = this.prepareSingleTableWhere(options.table, options.where, true);
231
-
232
- const builder = this.createSqlBuilder().selectRaw("COUNT(1) as cnt").from(prepared.snakeTable).where(prepared.whereFiltered).limit(1);
233
- const { sql, params } = builder.toSelectSql();
234
- const executeRes = await this.execute(sql, params);
235
- const exists = (executeRes.data?.[0]?.cnt || 0) > 0;
236
- return { data: exists, sql: executeRes.sql };
237
- },
238
-
239
- async getFieldValue(options) {
240
- validateNoLeftJoinReadOptions(options, "getFieldValue", "getFieldValue 不支持 leftJoin(请使用 getOne/getList 并自行取字段)");
241
- const field = options.field;
242
- validateSafeFieldName(field);
243
-
244
- const oneOptions = {
245
- table: options.table
246
- };
247
- if (options.where !== undefined) oneOptions.where = options.where;
248
- if (options.leftJoin !== undefined) oneOptions.leftJoin = options.leftJoin;
249
- if (options.orderBy !== undefined) oneOptions.orderBy = options.orderBy;
250
- if (options.page !== undefined) oneOptions.page = options.page;
251
- if (options.limit !== undefined) oneOptions.limit = options.limit;
252
- oneOptions.fields = [field];
253
-
254
- const oneRes = await this.getOne(oneOptions);
255
-
256
- const result = oneRes.data;
257
- const value = this.resolveFieldValue(result, field);
258
- return {
259
- data: value,
260
- sql: oneRes.sql
261
- };
262
- },
263
-
264
- // 写入操作
265
-
266
- async insData(options) {
267
- validateTableDataOptions(options, "insData");
268
- const { table, data } = options;
269
- const snakeTable = snakeCase(table);
270
- const now = Date.now();
271
- const insertRows = await this.createInsertRows(table, snakeTable, [data], now);
272
- const processed = insertRows.processedList[0];
273
-
274
- assertNoUndefinedInRecord(processed, `insData 插入数据 (table: ${snakeTable})`);
275
-
276
- const builder = this.createSqlBuilder();
277
- const { sql, params } = builder.toInsertSql(snakeTable, processed);
278
- const executeRes = await this.execute(sql, params);
279
-
280
- return {
281
- data: this.resolveInsertedData(table, 1, executeRes, insertRows.ids, "insData"),
282
- sql: executeRes.sql
283
- };
284
- },
285
-
286
- async insBatch(table, dataList) {
287
- validateTableBatchDataOptions(table, dataList, "insBatch");
288
- if (dataList.length === 0) {
289
- return {
290
- data: [],
291
- sql: { sql: "", params: [], duration: 0 }
292
- };
293
- }
294
-
295
- const MAX_BATCH_SIZE = 1000;
296
- validateInsertBatchSize(dataList.length, MAX_BATCH_SIZE);
297
-
298
- const snakeTable = snakeCase(table);
299
- const now = Date.now();
300
- const insertRows = await this.createInsertRows(table, snakeTable, dataList, now);
301
- const processedList = insertRows.processedList;
302
-
303
- const insertFields = assertBatchInsertRowsConsistent(processedList, { table: snakeTable });
304
- const builder = this.createSqlBuilder();
305
- const { sql, params } = builder.toInsertSql(snakeTable, processedList);
306
-
307
- try {
308
- const executeRes = await this.execute(sql, params);
309
- return {
310
- data: this.resolveInsertedData(table, dataList.length, executeRes, insertRows.ids, "insBatch"),
311
- sql: executeRes.sql
312
- };
313
- } catch (error) {
314
- Logger.error("批量插入失败", error, {
315
- table: table,
316
- snakeTable: snakeTable,
317
- count: dataList.length,
318
- fields: insertFields
319
- });
320
- throw new Error("批量插入失败", {
321
- cause: error,
322
- code: "runtime",
323
- subsystem: "sql",
324
- operation: "insBatch",
325
- table: table,
326
- snakeTable: snakeTable,
327
- count: dataList.length,
328
- fields: insertFields
329
- });
330
- }
331
- },
332
-
333
- async delForceBatch(table, ids) {
334
- validateTableName(table, "delForceBatch.table");
335
- if (ids.length === 0) {
336
- return {
337
- data: 0,
338
- sql: { sql: "", params: [], duration: 0 }
339
- };
340
- }
341
-
342
- const snakeTable = snakeCase(table);
343
-
344
- const query = SqlBuilder.toDeleteInSql({
345
- table: snakeTable,
346
- idField: "id",
347
- ids: ids,
348
- quoteIdent: quoteIdentMySql
349
- });
350
- const executeRes = await this.execute(query.sql, query.params);
351
- const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
352
- return {
353
- data: changes,
354
- sql: executeRes.sql
355
- };
356
- },
357
-
358
- async updBatch(table, dataList) {
359
- validateTableBatchDataOptions(table, dataList, "updBatch");
360
- if (dataList.length === 0) {
361
- return {
362
- data: 0,
363
- sql: { sql: "", params: [], duration: 0 }
364
- };
365
- }
366
-
367
- const snakeTable = snakeCase(table);
368
- const now = Date.now();
369
-
370
- const processedList = [];
371
- const fieldSet = new Set();
372
-
373
- for (const item of dataList) {
374
- const userData = buildPartialUpdateData({ data: item.data, allowState: true });
375
-
376
- for (const key of Object.keys(userData)) {
377
- fieldSet.add(key);
378
- }
379
-
380
- processedList.push({ id: item.id, data: userData });
381
- }
382
-
383
- const fields = Array.from(fieldSet).sort();
384
- if (fields.length === 0) {
385
- return {
386
- data: 0,
387
- sql: { sql: "", params: [], duration: 0 }
388
- };
389
- }
390
-
391
- const query = SqlBuilder.toUpdateCaseByIdSql({
392
- table: snakeTable,
393
- idField: "id",
394
- rows: processedList,
395
- fields: fields,
396
- quoteIdent: quoteIdentMySql,
397
- updatedAtField: this.beflyMode === "auto" ? "updated_at" : "",
398
- updatedAtValue: this.beflyMode === "auto" ? now : null,
399
- stateField: "state",
400
- stateGtZero: this.beflyMode === "auto"
401
- });
402
-
403
- const executeRes = await this.execute(query.sql, query.params);
404
- const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
405
- return {
406
- data: changes,
407
- sql: executeRes.sql
408
- };
409
- },
410
-
411
- async updData(options) {
412
- validateTableDataOptions(options, "updData");
413
- validateTableWhereOptions(options, "updData", true);
414
- const { table, data, where } = options;
415
- const prepared = this.prepareSingleTableWhere(table, where, true);
416
-
417
- const processed = buildUpdateRow({ data: data, now: Date.now(), allowState: true, beflyMode: this.beflyMode });
418
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
419
- const { sql, params } = builder.toUpdateSql(prepared.snakeTable, processed);
420
-
421
- const executeRes = await this.execute(sql, params);
422
- const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
423
- return {
424
- data: changes,
425
- sql: executeRes.sql
426
- };
427
- },
428
-
429
- async delData(options) {
430
- validateTableWhereOptions(options, "delData", true);
431
- const { table, where } = options;
432
- const prepared = this.prepareSingleTableWhere(table, where, true);
433
- const now = Date.now();
434
- const processed = {
435
- state: 0,
436
- deleted_at: now
437
- };
438
-
439
- if (this.beflyMode === "auto") {
440
- processed.updated_at = now;
441
- }
442
-
443
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
444
- const { sql, params } = builder.toUpdateSql(prepared.snakeTable, processed);
445
- const executeRes = await this.execute(sql, params);
446
- const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
447
-
448
- return {
449
- data: changes,
450
- sql: executeRes.sql
451
- };
452
- },
453
-
454
- async delForce(options) {
455
- validateTableWhereOptions(options, "delForce", true);
456
- const { table, where } = options;
457
-
458
- const prepared = this.prepareSingleTableWhere(table, where, false);
459
-
460
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
461
- const { sql, params } = builder.toDeleteSql(prepared.snakeTable);
462
-
463
- const executeRes = await this.execute(sql, params);
464
- const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
465
- return {
466
- data: changes,
467
- sql: executeRes.sql
468
- };
469
- },
470
-
471
- async disableData(options) {
472
- validateTableWhereOptions(options, "disableData", true);
473
- const { table, where } = options;
474
-
475
- return await this.updData({
476
- table: table,
477
- data: {
478
- state: 2
479
- },
480
- where: where
481
- });
482
- },
483
-
484
- async enableData(options) {
485
- validateTableWhereOptions(options, "enableData", true);
486
- const { table, where } = options;
487
-
488
- return await this.updData({
489
- table: table,
490
- data: {
491
- state: 1
492
- },
493
- where: where
494
- });
495
- },
496
-
497
- async increment(table, field, where, value = 1) {
498
- validateIncrementOptions(table, field, where, value, "increment");
499
- const prepared = this.prepareSingleTableWhere(table, where, true);
500
- const snakeField = snakeCase(field);
501
-
502
- const builder = this.createSqlBuilder().where(prepared.whereFiltered);
503
- const { sql: whereClause, params: whereParams } = builder.getWhereConditions();
504
-
505
- const quotedTable = quoteIdentMySql(prepared.snakeTable);
506
- const quotedField = quoteIdentMySql(snakeField);
507
- const sql = whereClause ? `UPDATE ${quotedTable} SET ${quotedField} = ${quotedField} + ? WHERE ${whereClause}` : `UPDATE ${quotedTable} SET ${quotedField} = ${quotedField} + ?`;
508
-
509
- const params = [value];
510
- for (const param of whereParams) {
511
- params.push(param);
512
- }
513
-
514
- const executeRes = await this.execute(sql, params);
515
- const changes = normalizeSqlMetaNumber(executeRes.data?.affectedRows);
516
- return {
517
- data: changes,
518
- sql: executeRes.sql
519
- };
520
- },
521
-
522
- async decrement(table, field, where, value = 1) {
523
- return await this.increment(table, field, where, -value);
524
- },
525
-
526
- async trans(callback) {
527
- const abortMark = "__beflyTransAbort";
528
-
529
- if (this.isTransaction) {
530
- const result = await callback(this);
531
- if (result?.code !== undefined && result.code !== 0) {
532
- const abortError = new Error("TRANSACTION_ABORT", {
533
- cause: null,
534
- code: "runtime"
535
- });
536
- abortError[abortMark] = true;
537
- abortError.payload = result;
538
- throw abortError;
539
- }
540
- return result;
541
- }
542
-
543
- if (typeof this.sql?.begin !== "function") {
544
- throw new Error("不支持事务 begin() 方法", {
545
- cause: null,
546
- code: "runtime"
547
- });
548
- }
549
-
550
- try {
551
- return await this.sql.begin(async (tx) => {
552
- const trans = new this.constructor({
553
- redis: this.redis,
554
- dbName: this.dbName,
555
- sql: tx,
556
- isTransaction: true,
557
- beflyMode: this.beflyMode
558
- });
559
- const result = await callback(trans);
560
- if (result?.code !== undefined && result.code !== 0) {
561
- const abortError = new Error("TRANSACTION_ABORT", {
562
- cause: null,
563
- code: "runtime"
564
- });
565
- abortError[abortMark] = true;
566
- abortError.payload = result;
567
- throw abortError;
568
- }
569
- return result;
570
- });
571
- } catch (error) {
572
- if (error && error[abortMark] === true) {
573
- return error.payload;
574
- }
575
- throw error;
576
- }
577
- }
578
- };
@@ -1,128 +0,0 @@
1
- import { toSqlParams } from "../sqlBuilder/util.js";
2
- import { snakeCase } from "../../utils/util.js";
3
- import { validateExecuteSql } from "./validate.js";
4
-
5
- function safeStringify(value) {
6
- try {
7
- return JSON.stringify(value);
8
- } catch {
9
- try {
10
- return String(value);
11
- } catch {
12
- return "[Unserializable]";
13
- }
14
- }
15
- }
16
-
17
- function getExecuteErrorMessage(error) {
18
- if (error instanceof Error && typeof error.message === "string" && error.message.trim().length > 0) {
19
- return error.message.trim();
20
- }
21
-
22
- if (!error || typeof error !== "object") {
23
- return String(error);
24
- }
25
-
26
- const message = typeof error.message === "string" && error.message.trim().length > 0 ? error.message.trim() : typeof error.sqlMessage === "string" && error.sqlMessage.trim().length > 0 ? error.sqlMessage.trim() : "";
27
- const detail = {};
28
-
29
- if (typeof error.code === "string" && error.code.trim().length > 0) {
30
- detail.code = error.code;
31
- }
32
- if (typeof error.errno === "number") {
33
- detail.errno = error.errno;
34
- }
35
- if (typeof error.sqlState === "string" && error.sqlState.trim().length > 0) {
36
- detail.sqlState = error.sqlState;
37
- }
38
- if (typeof error.sqlMessage === "string" && error.sqlMessage.trim().length > 0) {
39
- detail.sqlMessage = error.sqlMessage;
40
- }
41
- if (typeof error.detail === "string" && error.detail.trim().length > 0) {
42
- detail.detail = error.detail;
43
- }
44
-
45
- const detailText = Object.keys(detail).length > 0 ? safeStringify(detail) : safeStringify(error);
46
- if (message && detailText && detailText !== "{}") {
47
- return `${message} | ${detailText}`;
48
- }
49
- if (message) {
50
- return message;
51
- }
52
- return detailText;
53
- }
54
-
55
- export const executeMethods = {
56
- async execute(sql, params) {
57
- if (!this.sql) {
58
- throw new Error("数据库连接未初始化", {
59
- cause: null,
60
- code: "runtime"
61
- });
62
- }
63
-
64
- validateExecuteSql(sql);
65
-
66
- const startTime = Date.now();
67
- const safeParams = toSqlParams(params);
68
-
69
- try {
70
- let queryResult;
71
- // Bun 事件循环 bug workaround:
72
- // 当事件循环中只有一个 I/O 源(SQL socket)时,Bun 会走单源快速路径,
73
- // 在 I/O 回调内直接唤醒 JS Promise,跳过正常调度,导致 SQL 连接状态
74
- // 未完全重置,下一条查询的 Promise resolver 永久丢失(远程 TCP 连接必现)。
75
- // 预注册一个 timer 使事件循环中同时存在两种事件源,强制 Bun 走正常多源调度路径。
76
- // timer 时长无关紧要,查询完成后会被 clearTimeout 清理。
77
- const _timer = setTimeout(() => {}, 2147483647);
78
- const queryPromise = safeParams.length > 0 ? this.sql.unsafe(sql, safeParams) : this.sql.unsafe(sql);
79
- try {
80
- queryResult = await queryPromise;
81
- } finally {
82
- clearTimeout(_timer);
83
- }
84
-
85
- const duration = Date.now() - startTime;
86
-
87
- return {
88
- data: queryResult,
89
- sql: {
90
- sql: sql,
91
- params: safeParams,
92
- duration: duration
93
- }
94
- };
95
- } catch (error) {
96
- const duration = Date.now() - startTime;
97
- const msg = getExecuteErrorMessage(error);
98
- const sqlInfo = {
99
- sql: sql,
100
- params: safeParams,
101
- duration: duration
102
- };
103
- const wrappedError = new Error(`SQL执行失败: ${msg}`, {
104
- cause: error
105
- });
106
-
107
- wrappedError.code = "runtime";
108
- wrappedError.subsystem = "sql";
109
- wrappedError.operation = "execute";
110
- wrappedError.params = safeParams;
111
- wrappedError.duration = duration;
112
- wrappedError.sqlInfo = sqlInfo;
113
-
114
- throw wrappedError;
115
- }
116
- },
117
-
118
- async tableExists(tableName) {
119
- const snakeTableName = snakeCase(tableName);
120
- const executeRes = await this.execute("SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?", [snakeTableName]);
121
- const exists = (executeRes.data?.[0]?.count || 0) > 0;
122
-
123
- return {
124
- data: exists,
125
- sql: executeRes.sql
126
- };
127
- }
128
- };