@h-ai/reldb 0.1.0-alpha.70 → 0.1.0-alpha.72
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.en.md +12 -0
- package/README.ja.md +12 -0
- package/README.md +12 -0
- package/dist/index.d.ts +54 -1
- package/dist/index.js +194 -53
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -594,6 +594,18 @@ Custom SQL methods in `BaseReldbCrudRepository` subclasses must check `await thi
|
|
|
594
594
|
|
|
595
595
|
Production requires PostgreSQL/MySQL. SQLite, including disk files, is for development/explicit tests and does not provide cross-host shared state. Initialization checks apply when NODE_ENV or HAI_CORE_ENV is production; only explicit HAI_E2E=1/HAI_E2E_MOCK=1 tests are exempt. [State, concurrency and recovery (Chinese)](../../.agents/instructions/distributed-state.md)。
|
|
596
596
|
|
|
597
|
+
## Module schema migrations
|
|
598
|
+
|
|
599
|
+
`schemaColumns(reldb, table, sql?)` inspects real columns. `migrateSchema(reldb, module, steps, { dryRun: true })` creates no ledger. Modules own their steps. Execution journals versions in `hai_schema_migrations`; SQLite/PostgreSQL are transactional, while MySQL implicit DDL commits require reentrant steps. Failures do not advance versions; retries inspect real state. Older code rejects newer ledgers. Stop old writers and back up DB/session cache first. Rollback restores compatible database and binaries together. See the [upgrade guide](../../docs/usage/README.en.md).
|
|
600
|
+
|
|
601
|
+
## Error messages and correlated logs
|
|
602
|
+
|
|
603
|
+
Unique constraint violations return `HaiReldbError.DUPLICATE_ENTRY` (409); timeout, pool exhaustion and other existing error codes remain intact. Branch on error codes rather than localized messages or driver text. Logs preserve driver code/schema/routine/constraint and stack line numbers; numeric bound values do not obscure these diagnostic identifiers.
|
|
604
|
+
|
|
605
|
+
HTTP errors expose localized safe messages followed by `(traceId: …)`, with the same ID in `error.traceId` and `X-Trace-Id`. The framework reuses the request ID to correlate browser and server logs. Sanitized exceptions, stacks and operation context remain in logs; internal cause/ext are not exposed. Field validation returns localized field names and messages only.
|
|
606
|
+
|
|
607
|
+
Use `core.error.toPublic(error, traceId?)` at HTTP/UI boundaries and `core.error.formatMessage(error)` after translating messages; formatting is idempotent. Pass complete Error objects to logs instead of discarding their context. Database failures log driver codes, sanitized messages/stacks and table/column context; SQL text, bound values and connection credentials are excluded.
|
|
608
|
+
|
|
597
609
|
## Development and testing
|
|
598
610
|
|
|
599
611
|
```bash
|
package/README.ja.md
CHANGED
|
@@ -594,6 +594,18 @@ if (!result.success) {
|
|
|
594
594
|
|
|
595
595
|
本番は PostgreSQL/MySQL が必須です。ファイルを含む SQLite は開発・明示的テスト用であり、ホスト間の共有状態を保証しません。 NODE_ENV または HAI_CORE_ENV が production の場合、初期化成功前に検証します。明示的な HAI_E2E=1/HAI_E2E_MOCK=1 テストのみ除外します。 [状態・並行制御・復旧範囲(中国語)](../../.agents/instructions/distributed-state.md)。
|
|
596
596
|
|
|
597
|
+
## モジュールのスキーマ移行
|
|
598
|
+
|
|
599
|
+
`schemaColumns(reldb, table, sql?)` は実際の列を読み取ります。`migrateSchema(reldb, module, steps, { dryRun: true })` は台帳を作成しません。各モジュールが手順を所有し、`hai_schema_migrations` に版を記録します。SQLite/PostgreSQL はトランザクション、MySQL の暗黙 DDL コミットには再入可能な手順が必要です。失敗時は版を進めず、実構造を再確認して再試行します。新しい台帳へのダウングレードは拒否します。旧書込を停止し DB/セッションキャッシュをバックアップしてください。復旧は互換 DB とバイナリを一緒に戻します。[更新ガイド](../../docs/usage/README.ja.md)を参照。
|
|
600
|
+
|
|
601
|
+
## エラー表示とログの関連付け
|
|
602
|
+
|
|
603
|
+
一意制約違反は `HaiReldbError.DUPLICATE_ENTRY`(409)に統一し、タイムアウトや待機列満杯など既存のコードは保持します。翻訳済みメッセージやドライバー文字列ではなくコードで分岐してください。ログには code/schema/routine/constraint とスタック行番号を残し、数値のバインド値でこれらの診断識別子を隠しません。
|
|
604
|
+
|
|
605
|
+
HTTP エラーはローカライズ済みの安全な表示に `(traceId: …)` を付け、同じ ID を error.traceId と X-Trace-Id に返します。requestId を traceId として共有し、ブラウザーとサーバーのログを関連付けます。脱機密化した例外・スタック・操作情報はログに残し、内部 cause/ext は公開しません。項目検証は翻訳済みの項目名とメッセージだけを返します。
|
|
606
|
+
|
|
607
|
+
HTTP/UI 境界には core.error.toPublic(error, traceId?)、再翻訳後の表示には core.error.formatMessage(error) を使い、ID を重複追加しません。ログには完全な Error を渡してください。DB ログにはドライバーコード、脱機密化したメッセージ・スタック、表・列を残し、SQL 本文、バインド値と接続認証情報を記録しません。
|
|
608
|
+
|
|
597
609
|
## 開発とテスト
|
|
598
610
|
|
|
599
611
|
```bash
|
package/README.md
CHANGED
|
@@ -594,6 +594,18 @@ if (!result.success) {
|
|
|
594
594
|
|
|
595
595
|
生产仅使用 PostgreSQL/MySQL;SQLite(含磁盘文件)只用于开发/明确测试,不能作为跨主机共享状态。 NODE_ENV 或 HAI_CORE_ENV 为 production 时在初始化发布成功前校验;仅 HAI_E2E=1/HAI_E2E_MOCK=1 明确测试可豁免。 [状态、并发和恢复边界](../../.agents/instructions/distributed-state.md)。
|
|
596
596
|
|
|
597
|
+
## 模块架构迁移
|
|
598
|
+
|
|
599
|
+
`schemaColumns(reldb, table, sql?)` 只读检查真实结构;`migrateSchema(reldb, module, steps, { dryRun: true })` 不创建账本。版本步骤由所属模块维护,业务不修改框架内部表。执行使用 `hai_schema_migrations`,SQLite/PostgreSQL 整体事务,MySQL DDL 隐式提交因此步骤必须可重入;失败不推进版本,重新检查真实结构后续做,拒绝向低版本降级。升级前停止所有旧写入并备份 DB/会话缓存;回退需恢复兼容数据库与旧代码,不能只换二进制。详见 [升级指南](../../docs/usage/README.md)。
|
|
600
|
+
|
|
601
|
+
## 错误提示与关联日志
|
|
602
|
+
|
|
603
|
+
唯一约束冲突统一返回 `HaiReldbError.DUPLICATE_ENTRY`(409);超时、队列已满等已有错误码保持不变。业务按错误码分支,不解析本地化消息或驱动错误文本。日志保留驱动 code/schema/routine/constraint 和堆栈行号;数字绑定值不会误遮蔽这些诊断标识。
|
|
604
|
+
|
|
605
|
+
HTTP 错误只向用户返回本地化的安全提示,末尾显示 `(traceId: …)`;响应同时包含 `error.traceId` 和 `X-Trace-Id`。框架复用本次 requestId 作为 traceId,前后端日志自动关联。底层异常、堆栈和操作上下文经凭据脱敏后写入日志,不通过 cause/ext 向用户公开。字段校验只返回本地化的字段名和错误。
|
|
606
|
+
|
|
607
|
+
`core.error.toPublic(error, traceId?)` 用于接口/UI 边界;`core.error.formatMessage(error)` 用于重新翻译后的提示,重复调用不会重复追加标识。日志传入完整 Error 对象,不能只记录通用标题或提前 String(error) 丢失上下文。数据库失败记录驱动错误码、消息、堆栈及表/列;SQL 原文、绑定值和连接凭据不进入日志。
|
|
608
|
+
|
|
597
609
|
## 开发测试
|
|
598
610
|
|
|
599
611
|
```bash
|
package/dist/index.d.ts
CHANGED
|
@@ -1458,6 +1458,59 @@ declare abstract class BaseReldbCrudRepository<TItem> implements ReldbCrudReposi
|
|
|
1458
1458
|
*/
|
|
1459
1459
|
declare const reldb: ReldbFunctions;
|
|
1460
1460
|
|
|
1461
|
+
/** 模块维护自己的迁移;这里只提供跨数据库列检查、版本账本和执行边界。 */
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* 一个可重入的模块升级步骤;只接收代码中固定的 SQL 与表名。
|
|
1465
|
+
* @example
|
|
1466
|
+
* ```ts
|
|
1467
|
+
* import type { SchemaMigration } from '@h-ai/reldb'
|
|
1468
|
+
* const steps: SchemaMigration[] = []
|
|
1469
|
+
* ```
|
|
1470
|
+
*/
|
|
1471
|
+
interface SchemaMigration {
|
|
1472
|
+
version: number;
|
|
1473
|
+
name: string;
|
|
1474
|
+
needed: (sql: DmlOperations) => Promise<HaiResult<boolean>>;
|
|
1475
|
+
apply: (sql: DmlOperations) => Promise<HaiResult<unknown>>;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* 只读预检结果;生产升级需先停旧写入并备份数据库与共享缓存。
|
|
1479
|
+
* @example
|
|
1480
|
+
* ```ts
|
|
1481
|
+
* import type { SchemaMigrationPlan } from '@h-ai/reldb'
|
|
1482
|
+
* function changes(plan: SchemaMigrationPlan) { return plan.changes }
|
|
1483
|
+
* ```
|
|
1484
|
+
*/
|
|
1485
|
+
interface SchemaMigrationPlan {
|
|
1486
|
+
module: string;
|
|
1487
|
+
currentVersion: number;
|
|
1488
|
+
targetVersion: number;
|
|
1489
|
+
changes: string[];
|
|
1490
|
+
requiresExclusiveAccess: boolean;
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* 查询真实列名;不创建表,不把查询错误当成新安装。
|
|
1494
|
+
* @example
|
|
1495
|
+
* ```ts
|
|
1496
|
+
* import { reldb, schemaColumns } from '@h-ai/reldb'
|
|
1497
|
+
* const columns = await schemaColumns(reldb, 'hai_iam_users')
|
|
1498
|
+
* ```
|
|
1499
|
+
*/
|
|
1500
|
+
declare function schemaColumns(db: ReldbFunctions, table: string, sql?: DmlOperations): Promise<HaiResult<string[]>>;
|
|
1501
|
+
/**
|
|
1502
|
+
* 预检或执行模块维护的版本迁移。SQLite/PostgreSQL 原子提交;MySQL DDL 隐式提交,步骤须可重入。
|
|
1503
|
+
* 失败不记录目标版本;重启重新检查真实结构。新版本账本拒绝旧代码向下迁移。
|
|
1504
|
+
* @example
|
|
1505
|
+
* ```ts
|
|
1506
|
+
* import { reldb, migrateSchema } from '@h-ai/reldb'
|
|
1507
|
+
* const plan = await migrateSchema(reldb, 'example', [], { dryRun: true })
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
1510
|
+
declare function migrateSchema(db: ReldbFunctions, module: string, steps: readonly SchemaMigration[], options?: {
|
|
1511
|
+
dryRun?: boolean;
|
|
1512
|
+
}): Promise<HaiResult<SchemaMigrationPlan>>;
|
|
1513
|
+
|
|
1461
1514
|
/**
|
|
1462
1515
|
* @h-ai/reldb — 安全工具
|
|
1463
1516
|
*
|
|
@@ -1535,4 +1588,4 @@ declare function quoteIdentifier(name: string): string;
|
|
|
1535
1588
|
*/
|
|
1536
1589
|
declare function escapeSqlString(value: string): string;
|
|
1537
1590
|
|
|
1538
|
-
export { BaseReldbCrudRepository, type BaseReldbCrudRepositoryConfig, type ColumnType, type CrudConfig, type CrudManager, type CrudPageOptions, type CrudQueryOptions, DbType, type DdlOperations, type DmlOperations, type DmlWithTxOperations, type ExecuteResult, HaiReldbError, type JsonSqlExpr, type NormalizedPagination, type PaginationOverrides, type PaginationQueryOptions, type QueryRow, type ReldbColumnDef, ReldbConfig, ReldbConfigInput, type ReldbCrudCountOptions, type ReldbCrudFieldDefinition, type ReldbCrudRepository, type ReldbFunctions, type ReldbIndexDef, type ReldbJsonOps, type ReldbTableDef, type TxManager, type TxWrapCallback, createJsonOps, escapeSqlString, parseJsonPath, quoteIdentifier, reldb, validateIdentifier, validateIdentifiers };
|
|
1591
|
+
export { BaseReldbCrudRepository, type BaseReldbCrudRepositoryConfig, type ColumnType, type CrudConfig, type CrudManager, type CrudPageOptions, type CrudQueryOptions, DbType, type DdlOperations, type DmlOperations, type DmlWithTxOperations, type ExecuteResult, HaiReldbError, type JsonSqlExpr, type NormalizedPagination, type PaginationOverrides, type PaginationQueryOptions, type QueryRow, type ReldbColumnDef, ReldbConfig, ReldbConfigInput, type ReldbCrudCountOptions, type ReldbCrudFieldDefinition, type ReldbCrudRepository, type ReldbFunctions, type ReldbIndexDef, type ReldbJsonOps, type ReldbTableDef, type SchemaMigration, type SchemaMigrationPlan, type TxManager, type TxWrapCallback, createJsonOps, escapeSqlString, migrateSchema, parseJsonPath, quoteIdentifier, reldb, schemaColumns, validateIdentifier, validateIdentifiers };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReldbConfigSchema, MysqlPoolConfigSchema } from './chunk-SDS2ZT3V.js';
|
|
2
2
|
export { MysqlConfigSchema, MysqlOptionsSchema, MysqlPoolConfigSchema, MysqlSslConfigSchema, PoolConfigSchema, PostgresqlConfigSchema, ReldbConfigSchema, ReldbOperationLogConfigSchema, ReldbTypeSchema, SqliteConfigSchema, SqliteOptionsSchema, SslConfigSchema } from './chunk-SDS2ZT3V.js';
|
|
3
|
-
import { core, ok, err } from '@h-ai/core';
|
|
3
|
+
import { core, ok, err, HaiCommonError } from '@h-ai/core';
|
|
4
4
|
import process from 'process';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
6
|
import { isIP } from 'net';
|
|
@@ -13,22 +13,23 @@ import { Worker } from 'worker_threads';
|
|
|
13
13
|
var en_US_default = {
|
|
14
14
|
$schema: "https://inlang.com/schema/inlang-message-format",
|
|
15
15
|
reldb_notInitialized: "Database not initialized, please call db.init() first",
|
|
16
|
-
reldb_initFailed: "Failed to initialize database
|
|
17
|
-
reldb_closeFailed: "Failed to close database
|
|
18
|
-
reldb_ddlFailed: "DDL operation failed
|
|
19
|
-
reldb_queryFailed: "Query failed
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
reldb_initFailed: "Failed to initialize database",
|
|
17
|
+
reldb_closeFailed: "Failed to close database",
|
|
18
|
+
reldb_ddlFailed: "DDL operation failed",
|
|
19
|
+
reldb_queryFailed: "Query failed",
|
|
20
|
+
reldb_duplicateEntry: "Record already exists",
|
|
21
|
+
reldb_batchFailed: "Batch execute failed",
|
|
22
|
+
reldb_postgresTxFailed: "Transaction failed",
|
|
22
23
|
reldb_postgresOnlyPostgresql: "PostgreSQL Provider only supports postgresql type",
|
|
23
|
-
reldb_postgresConnectionFailed: "Failed to connect to PostgreSQL
|
|
24
|
+
reldb_postgresConnectionFailed: "Failed to connect to PostgreSQL",
|
|
24
25
|
reldb_sqliteOnlySqlite: "SQLite Provider only supports sqlite type",
|
|
25
26
|
reldb_sqliteNeedPath: "SQLite requires database path (database field)",
|
|
26
|
-
reldb_sqliteConnectionFailed: "Failed to connect to SQLite
|
|
27
|
-
reldb_sqliteTxFailed: "Transaction failed
|
|
28
|
-
reldb_sqliteBatchFailed: "Batch execute failed
|
|
27
|
+
reldb_sqliteConnectionFailed: "Failed to connect to SQLite",
|
|
28
|
+
reldb_sqliteTxFailed: "Transaction failed",
|
|
29
|
+
reldb_sqliteBatchFailed: "Batch execute failed",
|
|
29
30
|
reldb_mysqlOnlyMysql: "MySQL Provider only supports mysql type",
|
|
30
|
-
reldb_mysqlConnectionFailed: "Failed to connect to MySQL
|
|
31
|
-
reldb_mysqlTxFailed: "Transaction failed
|
|
31
|
+
reldb_mysqlConnectionFailed: "Failed to connect to MySQL",
|
|
32
|
+
reldb_mysqlTxFailed: "Transaction failed",
|
|
32
33
|
reldb_mysqlPoolQueueFull: "MySQL connection acquisition queue is full",
|
|
33
34
|
reldb_mysqlPoolAcquireTimeout: "MySQL connection acquisition deadline exceeded",
|
|
34
35
|
reldb_mysqlPoolReleaseFailed: "MySQL connection release failed; close the database before retrying",
|
|
@@ -37,8 +38,8 @@ var en_US_default = {
|
|
|
37
38
|
reldb_crudNoValidColumns: "CRUD payload has no valid columns",
|
|
38
39
|
reldb_crudRecordNotFound: "Record not found: {id}",
|
|
39
40
|
reldb_invalidIdentifier: "Invalid SQL identifier: {name} (only letters, digits and underscores allowed)",
|
|
40
|
-
reldb_configError: "Database config validation failed
|
|
41
|
-
reldb_txFailed: "Transaction failed
|
|
41
|
+
reldb_configError: "Database config validation failed",
|
|
42
|
+
reldb_txFailed: "Transaction failed",
|
|
42
43
|
reldb_sharedBackendRequired: "Production mode requires a shared backend; local files and memory backends are for development or explicit end-to-end tests.",
|
|
43
44
|
reldb_jsonPatchObjectRequired: "JSON merge patch must be an object"
|
|
44
45
|
};
|
|
@@ -47,22 +48,23 @@ var en_US_default = {
|
|
|
47
48
|
var ja_JP_default = {
|
|
48
49
|
$schema: "https://inlang.com/schema/inlang-message-format",
|
|
49
50
|
reldb_notInitialized: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u5148\u306Bdb.init()\u3092\u547C\u3073\u51FA\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
50
|
-
reldb_initFailed: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
51
|
-
reldb_closeFailed: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u7D42\u4E86\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
52
|
-
reldb_ddlFailed: "DDL\u64CD\u4F5C\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
53
|
-
reldb_queryFailed: "\u30AF\u30A8\u30EA\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
reldb_initFailed: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
52
|
+
reldb_closeFailed: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u7D42\u4E86\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
53
|
+
reldb_ddlFailed: "DDL\u64CD\u4F5C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
54
|
+
reldb_queryFailed: "\u30AF\u30A8\u30EA\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
55
|
+
reldb_duplicateEntry: "\u30EC\u30B3\u30FC\u30C9\u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059",
|
|
56
|
+
reldb_batchFailed: "\u30D0\u30C3\u30C1\u5B9F\u884C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
57
|
+
reldb_postgresTxFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
56
58
|
reldb_postgresOnlyPostgresql: "PostgreSQL\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306Fpostgresql\u578B\u306E\u307F\u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059",
|
|
57
|
-
reldb_postgresConnectionFailed: "PostgreSQL\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
59
|
+
reldb_postgresConnectionFailed: "PostgreSQL\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
58
60
|
reldb_sqliteOnlySqlite: "SQLite\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306Fsqlite\u578B\u306E\u307F\u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059",
|
|
59
61
|
reldb_sqliteNeedPath: "SQLite\u306B\u306F\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30D1\u30B9\uFF08database\u30D5\u30A3\u30FC\u30EB\u30C9\uFF09\u304C\u5FC5\u8981\u3067\u3059",
|
|
60
|
-
reldb_sqliteConnectionFailed: "SQLite\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
61
|
-
reldb_sqliteTxFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
62
|
-
reldb_sqliteBatchFailed: "\u30D0\u30C3\u30C1\u5B9F\u884C\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
62
|
+
reldb_sqliteConnectionFailed: "SQLite\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
63
|
+
reldb_sqliteTxFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
64
|
+
reldb_sqliteBatchFailed: "\u30D0\u30C3\u30C1\u5B9F\u884C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
63
65
|
reldb_mysqlOnlyMysql: "MySQL\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u306Fmysql\u578B\u306E\u307F\u306B\u5BFE\u5FDC\u3057\u3066\u3044\u307E\u3059",
|
|
64
|
-
reldb_mysqlConnectionFailed: "MySQL\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
65
|
-
reldb_mysqlTxFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
66
|
+
reldb_mysqlConnectionFailed: "MySQL\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
67
|
+
reldb_mysqlTxFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
66
68
|
reldb_mysqlPoolQueueFull: "MySQL \u63A5\u7D9A\u53D6\u5F97\u30AD\u30E5\u30FC\u304C\u6E80\u676F\u3067\u3059",
|
|
67
69
|
reldb_mysqlPoolAcquireTimeout: "MySQL \u63A5\u7D9A\u53D6\u5F97\u306E\u5F85\u6A5F\u671F\u9650\u3092\u8D85\u904E\u3057\u307E\u3057\u305F",
|
|
68
70
|
reldb_mysqlPoolReleaseFailed: "MySQL \u63A5\u7D9A\u306E\u89E3\u653E\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u518D\u8A66\u884C\u3059\u308B\u524D\u306B\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u7D42\u4E86\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
@@ -71,8 +73,8 @@ var ja_JP_default = {
|
|
|
71
73
|
reldb_crudNoValidColumns: "CRUD\u30DA\u30A4\u30ED\u30FC\u30C9\u306B\u6709\u52B9\u306A\u5217\u304C\u3042\u308A\u307E\u305B\u3093",
|
|
72
74
|
reldb_crudRecordNotFound: "\u30EC\u30B3\u30FC\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: {id}",
|
|
73
75
|
reldb_invalidIdentifier: "SQL\u8B58\u5225\u5B50\u304C\u7121\u52B9\u3067\u3059: {name}\uFF08\u82F1\u6570\u5B57\u3068\u30A2\u30F3\u30C0\u30FC\u30B9\u30B3\u30A2\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\uFF09",
|
|
74
|
-
reldb_configError: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u8A2D\u5B9A\u306E\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
75
|
-
reldb_txFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F
|
|
76
|
+
reldb_configError: "\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u8A2D\u5B9A\u306E\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
77
|
+
reldb_txFailed: "\u30C8\u30E9\u30F3\u30B6\u30AF\u30B7\u30E7\u30F3\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
76
78
|
reldb_sharedBackendRequired: "\u672C\u756A\u74B0\u5883\u306B\u306F\u5171\u6709\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u304C\u5FC5\u8981\u3067\u3059\u3002\u30ED\u30FC\u30AB\u30EB\u30D5\u30A1\u30A4\u30EB\u3068\u30E1\u30E2\u30EA\u30FC\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u306F\u958B\u767A\u74B0\u5883\u307E\u305F\u306F\u660E\u793A\u7684\u306A E2E \u30C6\u30B9\u30C8\u3067\u306E\u307F\u4F7F\u7528\u3067\u304D\u307E\u3059\u3002",
|
|
77
79
|
reldb_jsonPatchObjectRequired: "JSON \u30DE\u30FC\u30B8\u30D1\u30C3\u30C1\u306F\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
78
80
|
};
|
|
@@ -81,22 +83,23 @@ var ja_JP_default = {
|
|
|
81
83
|
var zh_CN_default = {
|
|
82
84
|
$schema: "https://inlang.com/schema/inlang-message-format",
|
|
83
85
|
reldb_notInitialized: "\u6570\u636E\u5E93\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u8C03\u7528 db.init()",
|
|
84
|
-
reldb_initFailed: "\u521D\u59CB\u5316\u6570\u636E\u5E93\u5931\u8D25
|
|
85
|
-
reldb_closeFailed: "\u5173\u95ED\u6570\u636E\u5E93\u5931\u8D25
|
|
86
|
-
reldb_ddlFailed: "DDL \u64CD\u4F5C\u5931\u8D25
|
|
87
|
-
reldb_queryFailed: "\u67E5\u8BE2\u5931\u8D25
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
reldb_initFailed: "\u521D\u59CB\u5316\u6570\u636E\u5E93\u5931\u8D25",
|
|
87
|
+
reldb_closeFailed: "\u5173\u95ED\u6570\u636E\u5E93\u5931\u8D25",
|
|
88
|
+
reldb_ddlFailed: "DDL \u64CD\u4F5C\u5931\u8D25",
|
|
89
|
+
reldb_queryFailed: "\u67E5\u8BE2\u5931\u8D25",
|
|
90
|
+
reldb_duplicateEntry: "\u8BB0\u5F55\u5DF2\u5B58\u5728",
|
|
91
|
+
reldb_batchFailed: "\u6279\u91CF\u6267\u884C\u5931\u8D25",
|
|
92
|
+
reldb_postgresTxFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25",
|
|
90
93
|
reldb_postgresOnlyPostgresql: "PostgreSQL Provider \u4EC5\u652F\u6301 postgresql \u7C7B\u578B",
|
|
91
|
-
reldb_postgresConnectionFailed: "\u8FDE\u63A5 PostgreSQL \u5931\u8D25
|
|
94
|
+
reldb_postgresConnectionFailed: "\u8FDE\u63A5 PostgreSQL \u5931\u8D25",
|
|
92
95
|
reldb_sqliteOnlySqlite: "SQLite Provider \u4EC5\u652F\u6301 sqlite \u7C7B\u578B",
|
|
93
96
|
reldb_sqliteNeedPath: "SQLite \u9700\u8981\u63D0\u4F9B\u6570\u636E\u5E93\u8DEF\u5F84\uFF08database \u5B57\u6BB5\uFF09",
|
|
94
|
-
reldb_sqliteConnectionFailed: "\u8FDE\u63A5 SQLite \u5931\u8D25
|
|
95
|
-
reldb_sqliteTxFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25
|
|
96
|
-
reldb_sqliteBatchFailed: "\u6279\u91CF\u6267\u884C\u5931\u8D25
|
|
97
|
+
reldb_sqliteConnectionFailed: "\u8FDE\u63A5 SQLite \u5931\u8D25",
|
|
98
|
+
reldb_sqliteTxFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25",
|
|
99
|
+
reldb_sqliteBatchFailed: "\u6279\u91CF\u6267\u884C\u5931\u8D25",
|
|
97
100
|
reldb_mysqlOnlyMysql: "MySQL Provider \u4EC5\u652F\u6301 mysql \u7C7B\u578B",
|
|
98
|
-
reldb_mysqlConnectionFailed: "\u8FDE\u63A5 MySQL \u5931\u8D25
|
|
99
|
-
reldb_mysqlTxFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25
|
|
101
|
+
reldb_mysqlConnectionFailed: "\u8FDE\u63A5 MySQL \u5931\u8D25",
|
|
102
|
+
reldb_mysqlTxFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25",
|
|
100
103
|
reldb_mysqlPoolQueueFull: "MySQL \u8FDE\u63A5\u83B7\u53D6\u7B49\u5F85\u961F\u5217\u5DF2\u6EE1",
|
|
101
104
|
reldb_mysqlPoolAcquireTimeout: "MySQL \u8FDE\u63A5\u83B7\u53D6\u5DF2\u8D85\u8FC7\u622A\u6B62\u671F\u9650",
|
|
102
105
|
reldb_mysqlPoolReleaseFailed: "MySQL \u8FDE\u63A5\u5F52\u8FD8\u5931\u8D25\uFF0C\u8BF7\u5173\u95ED\u6570\u636E\u5E93\u540E\u91CD\u8BD5",
|
|
@@ -105,8 +108,8 @@ var zh_CN_default = {
|
|
|
105
108
|
reldb_crudNoValidColumns: "CRUD \u6570\u636E\u6CA1\u6709\u6709\u6548\u5217",
|
|
106
109
|
reldb_crudRecordNotFound: "\u8BB0\u5F55\u4E0D\u5B58\u5728: {id}",
|
|
107
110
|
reldb_invalidIdentifier: "\u975E\u6CD5\u7684 SQL \u6807\u8BC6\u7B26: {name}\uFF08\u4EC5\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u4E0B\u5212\u7EBF\uFF09",
|
|
108
|
-
reldb_configError: "\u6570\u636E\u5E93\u914D\u7F6E\u6821\u9A8C\u5931\u8D25
|
|
109
|
-
reldb_txFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25
|
|
111
|
+
reldb_configError: "\u6570\u636E\u5E93\u914D\u7F6E\u6821\u9A8C\u5931\u8D25",
|
|
112
|
+
reldb_txFailed: "\u4E8B\u52A1\u6267\u884C\u5931\u8D25",
|
|
110
113
|
reldb_sharedBackendRequired: "\u751F\u4EA7\u6A21\u5F0F\u9700\u8981\u8DE8\u8282\u70B9\u5171\u4EAB\u540E\u7AEF\uFF1B\u672C\u673A\u6587\u4EF6\u6216\u5185\u5B58\u540E\u7AEF\u4EC5\u7528\u4E8E\u5F00\u53D1\u548C\u663E\u5F0F\u7AEF\u5230\u7AEF\u6D4B\u8BD5\u3002",
|
|
111
114
|
reldb_jsonPatchObjectRequired: "JSON \u5408\u5E76\u8865\u4E01\u5FC5\u987B\u662F\u5BF9\u8C61"
|
|
112
115
|
};
|
|
@@ -1592,11 +1595,57 @@ async function wrapOp(ctx, fn, errorDef, errorLabel, errorMeta) {
|
|
|
1592
1595
|
if (!ctx.isConnected()) {
|
|
1593
1596
|
return err(HaiReldbError.NOT_INITIALIZED, reldbM("reldb_notInitialized"));
|
|
1594
1597
|
}
|
|
1598
|
+
const failed = (error, original) => {
|
|
1599
|
+
let definition = original ?? errorDef;
|
|
1600
|
+
const traceId = core.context.get().traceId ?? core.context.get().requestId ?? original?.traceId ?? core.id.uuid();
|
|
1601
|
+
const fields = error && typeof error === "object" ? error : {};
|
|
1602
|
+
if (definition.code === HaiReldbError.QUERY_FAILED.code && (["23505", "ER_DUP_ENTRY", "SQLITE_CONSTRAINT_UNIQUE", "SQLITE_CONSTRAINT_PRIMARYKEY"].includes(String(fields.code)) || fields.errno === 1062))
|
|
1603
|
+
definition = HaiReldbError.DUPLICATE_ENTRY;
|
|
1604
|
+
const secrets = /* @__PURE__ */ new Set();
|
|
1605
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1606
|
+
const collect = (value) => {
|
|
1607
|
+
if (typeof value === "string" && value) {
|
|
1608
|
+
secrets.add(value);
|
|
1609
|
+
} else if (typeof value === "number" || typeof value === "bigint") {
|
|
1610
|
+
secrets.add(String(value));
|
|
1611
|
+
} else if (value && typeof value === "object" && !seen.has(value)) {
|
|
1612
|
+
seen.add(value);
|
|
1613
|
+
Object.values(value).forEach(collect);
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
collect(errorMeta?.params);
|
|
1617
|
+
const payloads = [errorMeta, ...Array.isArray(errorMeta?.statements) ? errorMeta.statements : []];
|
|
1618
|
+
for (const payload of payloads) {
|
|
1619
|
+
collect(payload?.params);
|
|
1620
|
+
for (const match of (typeof payload?.sql === "string" ? payload.sql : "").matchAll(/'((?:''|[^'])*)'/g))
|
|
1621
|
+
secrets.add(match[1].replaceAll("''", "'"));
|
|
1622
|
+
}
|
|
1623
|
+
const diagnostic = {};
|
|
1624
|
+
for (const key2 of ["name", "message", "stack", "code", "errno", "sqlState", "detail", "hint", "constraint", "schema", "routine", "severity", "table", "column"]) {
|
|
1625
|
+
const value = error instanceof Error && key2 === "message" ? error.message : error instanceof Error && key2 === "stack" ? error.stack : fields[key2];
|
|
1626
|
+
if (typeof value === "string") {
|
|
1627
|
+
if (["code", "sqlState", "schema", "routine", "severity", "constraint", "table", "column"].includes(key2)) {
|
|
1628
|
+
diagnostic[key2] = core.sanitize.sanitizeText(value);
|
|
1629
|
+
} else {
|
|
1630
|
+
const frames = key2 === "stack" ? value.search(/^\s+at\s/m) : -1;
|
|
1631
|
+
const body = frames < 0 ? value : value.slice(0, frames);
|
|
1632
|
+
diagnostic[key2] = core.sanitize.sanitizeText(body, [...secrets]).replace(/'(?:''|[^'])*'/g, "'[REDACTED]'") + (frames < 0 ? "" : core.sanitize.sanitizeText(value.slice(frames)));
|
|
1633
|
+
}
|
|
1634
|
+
} else if (typeof value === "number") {
|
|
1635
|
+
diagnostic[key2] = value;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
if (!Object.keys(diagnostic).length)
|
|
1639
|
+
diagnostic.message = core.sanitize.sanitizeText(String(error), [...secrets]);
|
|
1640
|
+
ctx.logger.error(errorLabel, { ...summarizeOperationPayload(errorMeta), table: errorMeta?.tableName, column: errorMeta?.columnName, errorCode: definition.code, traceId, error: diagnostic });
|
|
1641
|
+
const key = definition.code === HaiReldbError.DDL_FAILED.code ? "reldb_ddlFailed" : definition.code === HaiReldbError.TRANSACTION_FAILED.code ? "reldb_txFailed" : "reldb_queryFailed";
|
|
1642
|
+
return err({ ...definition, traceId, message: definition.code === HaiReldbError.DUPLICATE_ENTRY.code ? reldbM("reldb_duplicateEntry") : original ? core.sanitize.sanitizeText(original.message, [...secrets]) : reldbM(key), cause: diagnostic });
|
|
1643
|
+
};
|
|
1595
1644
|
try {
|
|
1596
|
-
|
|
1645
|
+
const result = await fn();
|
|
1646
|
+
return result.success ? result : failed(result.error.cause ?? result.error, result.error);
|
|
1597
1647
|
} catch (error) {
|
|
1598
|
-
|
|
1599
|
-
return err(errorDef, reldbM("reldb_queryFailed", { params: { error: String(error) } }), error);
|
|
1648
|
+
return failed(error);
|
|
1600
1649
|
}
|
|
1601
1650
|
}
|
|
1602
1651
|
function createBaseDdlOps(ctx, raw) {
|
|
@@ -1670,7 +1719,7 @@ function createBaseDdlOps(ctx, raw) {
|
|
|
1670
1719
|
},
|
|
1671
1720
|
raw(sql) {
|
|
1672
1721
|
logReldbOperation(ctx, "write", "ddl.raw", { sql });
|
|
1673
|
-
return wrapOp(ctx, () => raw.raw(sql), HaiReldbError.DDL_FAILED, "DDL: raw failed");
|
|
1722
|
+
return wrapOp(ctx, () => raw.raw(sql), HaiReldbError.DDL_FAILED, "DDL: raw failed", { sql });
|
|
1674
1723
|
}
|
|
1675
1724
|
};
|
|
1676
1725
|
}
|
|
@@ -1678,23 +1727,23 @@ function createBaseDmlOps(ctx, raw) {
|
|
|
1678
1727
|
return {
|
|
1679
1728
|
query: (sql, params) => {
|
|
1680
1729
|
logReldbOperation(ctx, "read", "query", { sql, params });
|
|
1681
|
-
return wrapOp(ctx, () => raw.query(sql, params), HaiReldbError.QUERY_FAILED, "DML: query failed");
|
|
1730
|
+
return wrapOp(ctx, () => raw.query(sql, params), HaiReldbError.QUERY_FAILED, "DML: query failed", { sql, params });
|
|
1682
1731
|
},
|
|
1683
1732
|
get: (sql, params) => {
|
|
1684
1733
|
logReldbOperation(ctx, "read", "get", { sql, params });
|
|
1685
|
-
return wrapOp(ctx, () => raw.get(sql, params), HaiReldbError.QUERY_FAILED, "DML: get failed");
|
|
1734
|
+
return wrapOp(ctx, () => raw.get(sql, params), HaiReldbError.QUERY_FAILED, "DML: get failed", { sql, params });
|
|
1686
1735
|
},
|
|
1687
1736
|
execute: (sql, params) => {
|
|
1688
1737
|
logReldbOperation(ctx, "write", "execute", { sql, params });
|
|
1689
|
-
return wrapOp(ctx, () => raw.execute(sql, params), HaiReldbError.QUERY_FAILED, "DML: execute failed");
|
|
1738
|
+
return wrapOp(ctx, () => raw.execute(sql, params), HaiReldbError.QUERY_FAILED, "DML: execute failed", { sql, params });
|
|
1690
1739
|
},
|
|
1691
1740
|
batch: (statements) => {
|
|
1692
1741
|
logReldbOperation(ctx, "write", "batch", { count: statements.length, statements });
|
|
1693
|
-
return wrapOp(ctx, () => raw.batch(statements), HaiReldbError.QUERY_FAILED, "DML: batch failed");
|
|
1742
|
+
return wrapOp(ctx, () => raw.batch(statements), HaiReldbError.QUERY_FAILED, "DML: batch failed", { statements });
|
|
1694
1743
|
},
|
|
1695
1744
|
queryPage: (options) => {
|
|
1696
1745
|
logReldbOperation(ctx, "read", "queryPage", options);
|
|
1697
|
-
return wrapOp(ctx, () => raw.queryPage(options), HaiReldbError.QUERY_FAILED, "DML: queryPage failed");
|
|
1746
|
+
return wrapOp(ctx, () => raw.queryPage(options), HaiReldbError.QUERY_FAILED, "DML: queryPage failed", { ...options });
|
|
1698
1747
|
}
|
|
1699
1748
|
};
|
|
1700
1749
|
}
|
|
@@ -3363,7 +3412,99 @@ var reldb = {
|
|
|
3363
3412
|
return await closingPromise;
|
|
3364
3413
|
}
|
|
3365
3414
|
};
|
|
3415
|
+
async function schemaColumns(db, table, sql = db.sql) {
|
|
3416
|
+
if (!/^[a-z][a-z0-9_]{0,62}$/.test(table))
|
|
3417
|
+
return err(HaiCommonError.VALIDATION_ERROR, core.i18n.coreM("core_errorValidation"));
|
|
3418
|
+
const type = db.config?.type;
|
|
3419
|
+
const found = type === "sqlite" ? await sql.query(`PRAGMA table_info(${table})`) : await sql.query(`SELECT column_name AS name FROM information_schema.columns WHERE table_schema=${type === "mysql" ? "DATABASE()" : "current_schema()"} AND table_name=?`, [table]);
|
|
3420
|
+
return found.success ? ok(found.data.map((row) => row.name)) : found;
|
|
3421
|
+
}
|
|
3422
|
+
async function migrateSchema(db, module, steps, options = {}) {
|
|
3423
|
+
if (!db.isInitialized || !/^[a-z][a-z0-9-]{0,62}$/.test(module) || steps.some((step, index) => step.version !== index + 1))
|
|
3424
|
+
return err(HaiCommonError.VALIDATION_ERROR, core.i18n.coreM("core_errorValidation"));
|
|
3425
|
+
const table = "hai_schema_migrations";
|
|
3426
|
+
const targetVersion = steps.length;
|
|
3427
|
+
const failed = () => err({ ...HaiCommonError.INIT_FAILED, message: core.i18n.coreM("core_runtimeStartFailed", { params: { module } }), ext: { module, phase: "migration" } });
|
|
3428
|
+
async function inspect(sql) {
|
|
3429
|
+
const columns = await schemaColumns(db, table, sql);
|
|
3430
|
+
if (!columns.success)
|
|
3431
|
+
return failed();
|
|
3432
|
+
const row = columns.data.length ? await sql.get(`SELECT version FROM ${table} WHERE module=?`, [module]) : ok(null);
|
|
3433
|
+
if (!row.success)
|
|
3434
|
+
return failed();
|
|
3435
|
+
const currentVersion = Number(row.data?.version ?? 0);
|
|
3436
|
+
if (currentVersion > targetVersion)
|
|
3437
|
+
return failed();
|
|
3438
|
+
const changes = [];
|
|
3439
|
+
for (const step of steps) {
|
|
3440
|
+
const needed = await step.needed(sql);
|
|
3441
|
+
if (!needed.success)
|
|
3442
|
+
return failed();
|
|
3443
|
+
if (needed.data)
|
|
3444
|
+
changes.push(step.name);
|
|
3445
|
+
}
|
|
3446
|
+
return ok({ module, currentVersion, targetVersion, changes, requiresExclusiveAccess: changes.length > 0 });
|
|
3447
|
+
}
|
|
3448
|
+
if (options.dryRun)
|
|
3449
|
+
return inspect(db.sql);
|
|
3450
|
+
const created = await db.sql.execute(`CREATE TABLE IF NOT EXISTS ${table} (module VARCHAR(64) PRIMARY KEY, version INTEGER NOT NULL)`);
|
|
3451
|
+
if (!created.success)
|
|
3452
|
+
return failed();
|
|
3453
|
+
const seeded = await db.sql.execute(db.config?.type === "mysql" ? `INSERT IGNORE INTO ${table}(module,version) VALUES (?,0)` : `INSERT INTO ${table}(module,version) VALUES (?,0) ON CONFLICT(module) DO NOTHING`, [module]);
|
|
3454
|
+
if (!seeded.success)
|
|
3455
|
+
return failed();
|
|
3456
|
+
const started = await db.tx.begin();
|
|
3457
|
+
if (!started.success)
|
|
3458
|
+
return failed();
|
|
3459
|
+
const tx = started.data;
|
|
3460
|
+
let mysqlLock = false;
|
|
3461
|
+
let committed = false;
|
|
3462
|
+
try {
|
|
3463
|
+
if (db.config?.type === "mysql") {
|
|
3464
|
+
const lock = await tx.get("SELECT GET_LOCK(?,30) AS acquired", [`hai-schema:${module}`]);
|
|
3465
|
+
if (!lock.success || Number(lock.data?.acquired) !== 1)
|
|
3466
|
+
return failed();
|
|
3467
|
+
mysqlLock = true;
|
|
3468
|
+
}
|
|
3469
|
+
const locked = await tx.execute(`UPDATE ${table} SET version=version WHERE module=?`, [module]);
|
|
3470
|
+
if (!locked.success)
|
|
3471
|
+
return failed();
|
|
3472
|
+
const plan = await inspect(tx);
|
|
3473
|
+
if (!plan.success)
|
|
3474
|
+
return plan;
|
|
3475
|
+
for (const step of steps) {
|
|
3476
|
+
const needed = await step.needed(tx);
|
|
3477
|
+
if (!needed.success)
|
|
3478
|
+
return failed();
|
|
3479
|
+
if (needed.data) {
|
|
3480
|
+
const applied = await step.apply(tx);
|
|
3481
|
+
if (!applied.success)
|
|
3482
|
+
return failed();
|
|
3483
|
+
const remaining = await step.needed(tx);
|
|
3484
|
+
if (!remaining.success || remaining.data)
|
|
3485
|
+
return failed();
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
const recorded = await tx.execute(`UPDATE ${table} SET version=? WHERE module=?`, [targetVersion, module]);
|
|
3489
|
+
if (!recorded.success)
|
|
3490
|
+
return failed();
|
|
3491
|
+
if (mysqlLock) {
|
|
3492
|
+
await tx.get("SELECT RELEASE_LOCK(?)", [`hai-schema:${module}`]);
|
|
3493
|
+
mysqlLock = false;
|
|
3494
|
+
}
|
|
3495
|
+
const result = await tx.commit();
|
|
3496
|
+
committed = result.success;
|
|
3497
|
+
return result.success ? plan : failed();
|
|
3498
|
+
} catch {
|
|
3499
|
+
return failed();
|
|
3500
|
+
} finally {
|
|
3501
|
+
if (mysqlLock)
|
|
3502
|
+
await tx.get("SELECT RELEASE_LOCK(?)", [`hai-schema:${module}`]);
|
|
3503
|
+
if (!committed)
|
|
3504
|
+
await tx.rollback();
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3366
3507
|
|
|
3367
|
-
export { BaseReldbCrudRepository, HaiReldbError, createJsonOps, escapeSqlString, parseJsonPath, quoteIdentifier, reldb, validateIdentifier, validateIdentifiers };
|
|
3508
|
+
export { BaseReldbCrudRepository, HaiReldbError, createJsonOps, escapeSqlString, migrateSchema, parseJsonPath, quoteIdentifier, reldb, schemaColumns, validateIdentifier, validateIdentifiers };
|
|
3368
3509
|
//# sourceMappingURL=index.js.map
|
|
3369
3510
|
//# sourceMappingURL=index.js.map
|