dsh-db-tool 0.1.7 → 0.1.8

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 CHANGED
@@ -36,7 +36,7 @@ dsh plugin --profile web add "dsh-db-tool@github:mengqi1436/dsh-db-tool"
36
36
  dsh plugin --profile web add "link:E:\path\to\dsh-db-tool"
37
37
  ```
38
38
 
39
- GaussDB 官方驱动未发布 npm,需先构建 vendor:`npm run build:gaussdb`(PowerShell)或 `bash scripts/build-gaussdb.sh`;oracledb 安装脚本需 `npm approve-scripts oracledb`。详见 [docs/install.md](docs/install.md)。
39
+ GaussDB 官方驱动未发布 npm,需先构建 vendor:`npm run build:gaussdb`(PowerShell)或 `bash scripts/build-gaussdb.sh`;运行要求 Node ≥ 22.12(产物依赖 p-limit@7 纯 ESM)。oracledb 安装脚本需 `npm approve-scripts oracledb`。详见 [docs/install.md](docs/install.md)。
40
40
 
41
41
  ## 安全模型
42
42
 
@@ -1,8 +1,20 @@
1
1
  /**
2
2
  * GaussDB(openGauss)适配器。
3
3
  *
4
- * ⚠️ 官方驱动 openGauss-connector-nodejs 未发布 npm,按 openGauss 官方 README/文档实现,
5
- * 使用 vendor/gaussdb-pg 构建产物(fork 自 pg 8.7 时代,API 与 pg 完全兼容),未真机验证。
4
+ * ⚠️ 官方驱动 openGauss-connector-nodejs 未发布 npm(华为云 HCS 26.861.0 官方文档
5
+ * 应用程序接口清单仅 JDBC/ODBC/libpq/Psycopg/Go/ECPG,无 Node 驱动),Node 接入基于
6
+ * PostgreSQL 协议兼容自行验证。使用 vendor/gaussdb-pg 构建产物(fork 自 pg 8.6.0,
7
+ * API 与 pg 完全兼容),未真机验证。
8
+ *
9
+ * 已按官方文档对齐(证据见 skills/db-admin/SKILL.md GaussDB 节):
10
+ * - 认证:驱动实现 openGauss SHA256 / MD5-SHA256 / SM3 握手(vendor pg-protocol parser.ts:335-357、
11
+ * rfc5802.js:44),对应服务端 password_encryption_type 默认 2 (sha256);
12
+ * - 默认端口 8000(集中式 DN / 分布式 CN)、ro 会话 `SET SESSION CHARACTERISTICS AS
13
+ * TRANSACTION READ ONLY` 两形态官方支持(rf-dist/gaussdb-08-0400、rf-cent/gaussdb-38-0416);
14
+ * - 已知限制:SERIALIZABLE 功能不支持(等价 REPEATABLE READ,与 PG 的 SSI 行为不同);
15
+ * SSL 仅布尔开关(不支持 sslmode require/verify-ca/verify-full 细分);
16
+ * information_schema.tables/columns 集中式有官方视图文档、分布式参考无原生视图文档
17
+ * (仅 M-Compatibility 模式),跨库浏览在分布式实例上以真机行为为准。
6
18
  *
7
19
  * 构建方式:bash scripts/build-gaussdb.sh(或 npm run build:gaussdb)
8
20
  * 核心逻辑与 postgresql 适配器共享(sql-shared/pg-like.ts)。
@@ -10,9 +10,13 @@ function poolConfig(conn) {
10
10
  }
11
11
  else if (conn.fields) {
12
12
  const f = conn.fields;
13
+ // 兜底默认值按 kind 区分(仅表单字段被清空时触发):GaussDB 官方默认端口 8000
14
+ // (华为云 HCS 26.861.0:集中式 DN 与分布式 CN 均为 8000,devg-cent/gaussdb-42-0020、
15
+ // devg-dist/gaussdb-12-0319)、默认管理用户 gaussdb;postgresql 维持社区默认 5432/postgres 不变。
16
+ const gb = conn.meta.kind === 'gaussdb';
13
17
  base.host = String(f.host ?? '127.0.0.1');
14
- base.port = Number(f.port ?? 5432);
15
- base.user = String(f.user ?? f.username ?? 'postgres');
18
+ base.port = Number(f.port ?? (gb ? 8000 : 5432));
19
+ base.user = String(f.user ?? f.username ?? (gb ? 'gaussdb' : 'postgres'));
16
20
  if (f.password != null)
17
21
  base.password = String(f.password);
18
22
  if (f.database != null)
@@ -28,20 +32,23 @@ function poolConfig(conn) {
28
32
  export async function createPgLikeAdapter(kind, Driver, conn, opts) {
29
33
  const pool = new Driver.Pool(poolConfig(conn));
30
34
  const readOnly = opts?.mode === 'ro';
31
- // 服务器级 ro 强制(官方手段):每个新连接会话设为只读事务。
35
+ // 所有池(主池 + 跨库池)统一走此初始化。
36
+ // pg 约定:release(err) 若无等待者会 emit pool 'error',无监听器会崩进程——
37
+ // 官方 Pool 文档要求必须注册 error 监听(空闲连接因后端故障销毁时经此冒泡),无条件兜底。
38
+ // 服务器级 ro 强制(官方手段):每个新连接会话设为只读事务,仅 ro 模式注册。
32
39
  // fail-closed:SET 失败时 release(err) 让驱动销毁该连接、等待的 acquire 收到错误——
33
40
  // 会话级只读是 ro 的最后防线,不允许静默降级成可写连接。
34
- const setupReadOnly = (p) => {
35
- p.on('connect', (c) => {
36
- void c.query('SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY').catch((err) => {
37
- c.release(err instanceof Error ? err : new Error(String(err)));
38
- });
39
- });
40
- // pg 约定:release(err) 若无等待者会 emit pool 'error',不监听会崩进程
41
+ const setupPool = (p) => {
41
42
  p.on('error', () => { });
43
+ if (readOnly) {
44
+ p.on('connect', (c) => {
45
+ void c.query('SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY').catch((err) => {
46
+ c.release(err instanceof Error ? err : new Error(String(err)));
47
+ });
48
+ });
49
+ }
42
50
  };
43
- if (readOnly)
44
- setupReadOnly(pool);
51
+ setupPool(pool);
45
52
  // 事务激活期间所有 query/execute 路由到同一 client
46
53
  let txClient = null;
47
54
  const run = (sql, params) => txClient ? txClient.query(sql, params) : pool.query(sql, params);
@@ -67,8 +74,7 @@ export async function createPgLikeAdapter(kind, Driver, conn, opts) {
67
74
  if (cached)
68
75
  return cached;
69
76
  const created = new Driver.Pool({ ...poolConfig(conn), database: db });
70
- if (readOnly)
71
- setupReadOnly(created);
77
+ setupPool(created); // 与主池一致:error 兜底 + ro 会话只读
72
78
  dbPools.set(db, created);
73
79
  return created;
74
80
  };
package/dist/index.js CHANGED
@@ -30,7 +30,9 @@ action 说明:
30
30
  - preview:预览行,limit≤50(conn_id, table, database?, limit?)。
31
31
  - run_script:子进程沙箱脚本(conn_id, code),60s 超时强杀,permission model 禁文件
32
32
  访问 + 新 vm realm 双层隔离,仅注入受限 db.query/db.execute 句柄(经完整
33
- guard/审计链路),rw 连接才有写能力。`;
33
+ guard/审计链路),rw 连接才有写能力。句柄签名 db.query(sql, params?, database?):
34
+ 第三参为跨库路由目标(pg/gaussdb 等按库路由,同 Navicat 式),多库统计在脚本内
35
+ 循环逐库调用即可一次完成。`;
34
36
  export function apply(ctx) {
35
37
  const log = ctx.logger ?? { info: () => { }, error: () => { } };
36
38
  const store = new DbToolStore();
package/dist/manager.js CHANGED
@@ -249,8 +249,8 @@ export class DbToolService {
249
249
  try {
250
250
  const result = await runScriptInChild({
251
251
  code: src,
252
- dbQuery: async (sql, params) => this.unwrapForScript(await this.query(key, connId, sql, params, challengeId)),
253
- dbExecute: async (statement, params) => this.unwrapForScript(await this.execute(key, connId, statement, params, challengeId)),
252
+ dbQuery: async (sql, params, database) => this.unwrapForScript(await this.query(key, connId, sql, params, challengeId, database)),
253
+ dbExecute: async (statement, params, database) => this.unwrapForScript(await this.execute(key, connId, statement, params, challengeId, database)),
254
254
  onLog: (level, text) => level === 'error' ? console.error('[db-script]', text) : console.log('[db-script]', text),
255
255
  onTimeout: () => {
256
256
  void sessionAdapter.close().catch(() => { });
@@ -118,11 +118,13 @@ export async function runScriptInChild(opts) {
118
118
  });
119
119
  });
120
120
  async function handleDbCall(method, args) {
121
- const [statement, params] = args;
121
+ const [statement, params, rawDatabase] = args;
122
+ // IPC JSON 序列化会把 undefined 变 null,归一回 undefined 保持与直连调用语义一致
123
+ const database = rawDatabase ?? undefined;
122
124
  if (method === 'query')
123
- return opts.dbQuery(statement, params);
125
+ return opts.dbQuery(statement, params, database);
124
126
  if (method === 'execute')
125
- return opts.dbExecute(statement, params);
127
+ return opts.dbExecute(statement, params, database);
126
128
  throw new DbToolError('INVALID_ARGUMENT', `未知 db 方法: ${method}`);
127
129
  }
128
130
  function errorOf(e) {
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * IPC 协议:
12
12
  * 父→worker {type:'run', code}
13
- * worker→父 {type:'db', reqId, method, args}
13
+ * worker→父 {type:'db', reqId, method, args} // args=[statement, params, database?],database 即跨库路由目标
14
14
  * 父→worker {type:'dbResult', reqId, ok, result, error}
15
15
  * worker→父 {type:'done', ok, result, error}
16
16
  */
@@ -28,11 +28,11 @@ async function runScript(code) {
28
28
  const pending = new Map();
29
29
  let reqSeq = 0;
30
30
 
31
- const dbCall = (method) => (statement, params) =>
31
+ const dbCall = (method) => (statement, params, database) =>
32
32
  new Promise((resolve, reject) => {
33
33
  const reqId = ++reqSeq;
34
34
  pending.set(reqId, { resolve, reject });
35
- process.send({ type: 'db', reqId, method, args: [statement, params] });
35
+ process.send({ type: 'db', reqId, method, args: [statement, params, database] });
36
36
  });
37
37
 
38
38
  const db = {
@@ -0,0 +1,154 @@
1
+ # dsh-db-tool 项目架构图
2
+
3
+ > 由 cbm(codebase-memory)索引数据绘制:843 节点 / 2702 边,模块分层见各图。
4
+ > 索引项目名:`E-Code-dsh-dsh-db-tool`。
5
+
6
+ ## 1. 总体分层架构
7
+
8
+ ```mermaid
9
+ flowchart TB
10
+ subgraph UI["客户端层(DSH 侧边栏)"]
11
+ CLIENT["client/client.js<br/>连接管理 · 对象树浏览 · SQL 控制台 · 授权面板"]
12
+ end
13
+
14
+ subgraph API["接入层"]
15
+ HTTP["lib/http<br/>HTTP API(13 条路由)"]
16
+ TOOL["lib/index.ts<br/>DatabaseManager 工具定义<br/>list_connections / query / execute /<br/>schema / preview / run_script"]
17
+ end
18
+
19
+ subgraph SVC["服务编排层"]
20
+ MGR["lib/manager.ts<br/>ro/rw 项目授权校验 · 适配器生命周期 · 事务编排"]
21
+ end
22
+
23
+ subgraph SEC["安全层"]
24
+ GUARD["lib/guard<br/>SQL/命令分类 · 危险操作识别 ·<br/>一次性 challenge 确认(5 分钟有效)"]
25
+ STORE["lib/store<br/>connections · grants · secrets(脱敏)·<br/>audit 审计 · normalize · io"]
26
+ SCRIPT["lib/script<br/>runner.ts + worker.cjs<br/>子进程沙箱(vm realm · 禁文件 · 60s 超时)"]
27
+ end
28
+
29
+ subgraph ADPT["适配器层 lib/adapters(8 库)"]
30
+ REG["index.ts<br/>kind → 工厂注册表 + driverMissingError"]
31
+ SQLSHARED["sql-shared/<br/>common.ts(标识符/分页/规范化)<br/>pg-like.ts(PostgreSQL + GaussDB 共享)"]
32
+ A1["mysql"]
33
+ A2["postgresql"]
34
+ A3["gaussdb<br/>vendor/gaussdb-pg 驱动加载"]
35
+ A4["sqlite"]
36
+ A5["redis"]
37
+ A6["mongodb"]
38
+ A7["oracle"]
39
+ A8["dmdb"]
40
+ TYPES["types.ts<br/>DatabaseAdapter 契约"]
41
+ end
42
+
43
+ subgraph DRV["驱动层"]
44
+ D1["pg / mysql2 / sqlite3 / redis<br/>mongodb / oracledb / dmdb"]
45
+ D2["vendor/gaussdb-pg<br/>openGauss-connector-nodejs 构建产物"]
46
+ end
47
+
48
+ CLIENT -->|HTTP| HTTP
49
+ CLIENT -.->|注入| TOOL
50
+ TOOL --> MGR
51
+ MGR --> GUARD
52
+ MGR --> STORE
53
+ MGR --> SCRIPT
54
+ MGR --> REG
55
+ REG --> SQLSHARED
56
+ REG --> A1 & A4 & A5 & A6 & A7 & A8
57
+ A2 --> SQLSHARED
58
+ A3 --> SQLSHARED
59
+ SQLSHARED --> TYPES
60
+ A1 & A2 & A3 & A4 & A5 & A6 & A7 & A8 -.-> DRV
61
+ SCRIPT -.->|db.query / db.execute 经完整 guard 链路| MGR
62
+ ```
63
+
64
+ ## 2. 请求时序(query 与危险操作确认流)
65
+
66
+ ```mermaid
67
+ sequenceDiagram
68
+ autonumber
69
+ participant U as 用户(聊天)
70
+ participant T as DatabaseManager 工具
71
+ participant M as lib/manager.ts
72
+ participant G as lib/guard
73
+ participant S as lib/store
74
+ participant A as 适配器(8 库)
75
+ participant DB as 数据库
76
+
77
+ U->>T: query/execute(conn_id, sql)
78
+ T->>S: 授权校验(项目路径 → 连接 → ro/rw)
79
+ alt 未授权
80
+ S-->>T: UNAUTHORIZED_PROJECT
81
+ else ro 授权 + 写操作
82
+ S-->>T: READ_ONLY(拒绝)
83
+ else 通过
84
+ T->>G: 语句分类
85
+ alt 危险操作(DDL/DML/维护命令)
86
+ G-->>T: NEEDS_CONFIRMATION + challengeId(一次性,5 分钟)
87
+ T-->>U: 说明语句与风险,等待明确同意
88
+ U->>T: 携同一 challengeId 重试
89
+ T->>G: 校验 challenge(绑定语句 hash)
90
+ end
91
+ T->>M: 执行
92
+ M->>A: query/execute(可选 database 跨库路由,pg/gaussdb)
93
+ A->>DB: 参数化 SQL(ro 会话级 READ ONLY 双保险)
94
+ DB-->>A: 结果
95
+ A-->>T: 规范化结果(DECIMAL→string 等)
96
+ T->>S: 审计记录
97
+ T-->>U: {columns, rows, rowCount} / {affectedRows, message}
98
+ end
99
+ ```
100
+
101
+ ## 3. 适配器体系(8 库 × 共享实现)
102
+
103
+ ```mermaid
104
+ flowchart LR
105
+ subgraph 契约
106
+ T["types.ts<br/>DatabaseAdapter / TableInfo / ColumnInfo<br/>DbKind = 8 种"]
107
+ end
108
+ subgraph 工厂注册
109
+ R["adapters/index.ts<br/>kind → lazy import 工厂<br/>缺失驱动 → driverMissingError(附构建提示)"]
110
+ end
111
+ subgraph SQL系共享
112
+ PG["pg-like.ts<br/>PostgreSQL + GaussDB<br/>pool · ro 会话 · 跨库路由 ·<br/>库→schema→表 三层浏览"]
113
+ CM["common.ts<br/>assertIdent · quoteIdent<br/>clampLimit/Offset · normalizeCell"]
114
+ end
115
+ subgraph 独立实现
116
+ MY["mysql"]
117
+ SQ["sqlite"]
118
+ RD["redis(SCAN 禁 KEYS)"]
119
+ MG["mongodb(强制显式 filter)"]
120
+ OR["oracle"]
121
+ DM["dmdb"]
122
+ end
123
+ PG --> CM
124
+ MY & SQ & RD & MG & OR & DM -.-> CM
125
+ R --> PG & MY & SQ & RD & MG & OR & DM
126
+ PG -.->|"gaussdb 差异仅驱动加载<br/>createRequire vendor/gaussdb-pg"| G["gaussdb/index.ts"]
127
+ T --- R
128
+ ```
129
+
130
+ ## 4. 模块分层职责(cbm layers 数据)
131
+
132
+ | 层 | 模块 | 特征 |
133
+ |---|---|---|
134
+ | entry | lib、script、e2e-mock | 仅出边(调用下游) |
135
+ | api | http | 含 13 条 HTTP 路由定义 |
136
+ | core | adapters(4 入 0 出)、store(12 入 0 出) | 高扇入核心,不依赖上层 |
137
+ | internal | manager(7 入 12 出)、tool、http | 编排枢纽 |
138
+ | leaf | guard | 仅入边,纯分类/判定 |
139
+
140
+ ## 5. GaussDB 专项链路
141
+
142
+ ```mermaid
143
+ flowchart TB
144
+ T["DatabaseManager(query/execute/schema/preview, database=...)"] --> M["manager"]
145
+ M --> A3["gaussdb/index.ts<br/>loadGaussDriver():createRequire<br/>vendor/gaussdb-pg/packages/pg(CJS)"]
146
+ A3 --> PL["sql-shared/pg-like.ts<br/>kind='gaussdb'"]
147
+ PL -->|poolConfig:host/port 8000/user/password/database/ssl| P["PgLikePool"]
148
+ PL -->|database 参数| PF["poolFor(db):按库缓存池(Navicat 式跨库)"]
149
+ PL -->|ro 模式| SRO["每个新连接 SET SESSION CHARACTERISTICS<br/>AS TRANSACTION READ ONLY(fail-closed)"]
150
+ P --> DB[("GaussDB 服务端<br/>openGauss 协议")]
151
+ ```
152
+
153
+ - 驱动构建:`scripts/build-gaussdb.sh` / `.ps1` → 克隆 openGauss-connector-nodejs → `vendor/gaussdb-pg/packages/pg`(gitignore)。
154
+ - 测试:`tests/adapters-sql/gaussdb.test.ts`(驱动加载/工厂,mock 离线)+ `pg-like.test.ts`(pg/gaussdb 共享行为)。
package/docs/install.md CHANGED
@@ -43,6 +43,8 @@ rm -rf ../gaussdb-pg && cp -r packages/pg ../gaussdb-pg
43
43
 
44
44
  产物为 `vendor/gaussdb-pg`(CJS,结构同 `pg`:`{ Pool, Client, ... }`)。
45
45
 
46
+ > **Node 版本要求**:≥ 22.12。构建脚本会补装 `p-limit@7`(纯 ESM,源码 package.json 未声明),依赖 Node 原生 require(esm);更旧 Node 加载 GaussDB 驱动会抛 `ERR_REQUIRE_ESM`(其余 7 库不受影响)。
47
+
46
48
  ## oracledb approve-scripts
47
49
 
48
50
  `oracledb` 安装时需要运行安装脚本编译/下载二进制。仓库 `package.json` 已配置白名单:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-db-tool",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "DSH community plugin: chat-operated multi-database admin tool with sidebar management, project-scoped grants, ro/rw modes and dangerous-operation confirmation",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -99,7 +99,7 @@ DatabaseManager({ action: "run_script", conn_id: "mongo1", code: '{"find":"users
99
99
  |---|---|---|---|---|
100
100
  | MySQL | `?` | `information_schema` | `LIMIT n OFFSET m` | |
101
101
  | PostgreSQL | `$1, $2…` | `information_schema` / `pg_catalog` | `LIMIT n OFFSET m` | |
102
- | GaussDB | `$1, $2…` | `information_schema` / `pg_catalog` | `LIMIT n OFFSET m` | PG 系 |
102
+ | GaussDB | `$1, $2…` | `information_schema` / `pg_catalog` | `LIMIT n OFFSET m` | PG 系(默认端口 8000,见下方注记) |
103
103
  | SQLite | `?` | `sqlite_master` / `PRAGMA` | `LIMIT n OFFSET m` | 库列表固定 `['main']` |
104
104
  | Redis | 无(命令数组) | `SCAN`(禁用 `KEYS` 遍历) | `SCAN cursor` | `run_script` 传 `["SCAN","0","MATCH","user:*","COUNT","100"]` |
105
105
  | MongoDB | 无(BSON 文档) | `listCollections` | `find().skip().limit()` | `run_script` 传 `{"find":"users","filter":{},"limit":20,"skip":40}` |
@@ -108,6 +108,18 @@ DatabaseManager({ action: "run_script", conn_id: "mongo1", code: '{"find":"users
108
108
 
109
109
  **类型规范化**(工具返回值已自动处理,引用结果时注意):`DECIMAL/NUMERIC` → string(避免精度丢失);`Date/TIMESTAMP` → ISO 8601 string;`LOB/CLOB/BLOB` → string;MongoDB `ObjectId` → string。生成写语句时,DECIMAL 直接传字符串值、日期传 ISO 字符串即可。
110
110
 
111
+ ## GaussDB 官方对齐注记(HCS 26.861.0,集中式/分布式)
112
+
113
+ 依据华为云 GaussDB 官方文档(doc.hcs.huawei.com/db/zh-cn,26.861.0 双形态)与 openGauss 官方文档源仓库对齐;未真机验证处以代码内标注为准。
114
+
115
+ - **认证**:驱动实现 openGauss SHA256 / MD5-SHA256 / SM3 握手,匹配服务端默认 `password_encryption_type = 2`(sha256)。连接报 `Invalid username/password, login denied` 时依次排查:① 认证配置(gs_hba.conf)需含 `host all all 0.0.0.0/0 sha256`;② 服务端密码加密方式变更后必须重设密码或新建用户(旧口令仍是旧加密);③ 账号锁定用 `ALTER USER ... ACCOUNT UNLOCK`。
116
+ - **默认端口 8000**(集中式 DN / 分布式 CN,官方 gsql 示例 `-p 8000`);SSL 默认不强制(服务端可 require_ssl 强制)。本工具 SSL 仅开/关,**不支持** sslmode(require/verify-ca/verify-full)证书校验细分。
117
+ - **ro 只读会话**:`SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY` 为官方语法(集中式/分布式均支持,rf-dist/gaussdb-08-0400、rf-cent/gaussdb-38-0416),连接建立时执行一次即对该连接所有后续事务生效。
118
+ - **隔离级别差异**:READ UNCOMMITTED 等价 READ COMMITTED;**SERIALIZABLE 功能不支持,等价 REPEATABLE READ**——与 PG 真 SSI 行为不同,依赖 PG 串行化语义的脚本注意。
119
+ - **元数据**:`pg_database` / `pg_catalog.pg_namespace` 为官方系统表;`information_schema.tables/columns` 集中式有官方视图文档,分布式参考无原生视图(仅 M-Compatibility 模式有),分布式实例上浏览以真机行为为准。
120
+ - **兼容模式**:openGauss 有 sql_compatibility(A/B/C/PG)兼容模式,部分语法语义随模式变化(如 B 模式 SET SESSION TRANSACTION 需 `b_format_behavior_compat_options` 含 set_session_transaction 才生效),本工具按 PG 语义使用。
121
+ - 驱动/默认值差异的代码内证据与证据 URL 见 `lib/adapters/gaussdb/index.ts` 头注释与 `lib/adapters/sql-shared/pg-like.ts`。
122
+
111
123
  ## 危险操作确认流程(NEEDS_CONFIRMATION)
112
124
 
113
125
  收到形如以下的返回时: