
# 修复账本数据库
#### 前提条件
- 系统中需要有审计管理员或者具有审计管理员权限的角色。
- 数据库正常运行，并且对防篡改数据库执行了一系列增、删、改等操作，保证在查询时段内有账本操作记录结果产生。
 
#### 背景信息
- 当前的账本数据库机制为：全局区块表存储在CN端，各个CN数据独立。用户历史表存储在DN端，历史表记录的数据为所在DN防篡改表的数据变化。因此，在触发数据重分布时，可能导致防篡改表和用户历史表数据不一致，此时需要使用ledger_hist_repair(text, text)接口对指定DN节点的用户历史表进行修复，修复后当前DN节点调用历史表校验接口结果为true。在CN剔除、修复的场景下，可能导致全局区块表数据丢失或者与用户历史表不一致，此时需要使用ledger_gchain_repair(text, text)接口对整个集群范围内的全局区块表进行修复，修复后调用全局区块表校验接口结果为true。
- 修复用户历史表的接口为pg_catalog.ledger_hist_repair，操作为：
  ```
  SELECT pg_catalog.ledger_hist_repair(schema_name text,table_name text);
  ```
  如果修复成功，接口返回修复过程中用户历史表hash的增量。
  注：对用户表执行闪回DROP时，可使用该接口恢复用户表和用户历史表名称，请参见[恢复用户表和用户历史表名称]。
  
- 修复全局区块表的接口为pg_catalog.ledger_gchain_repair，操作为：
  ```
  SELECT pg_catalog.ledger_gchain_repair(schema_name text,table_name text);
  ```
  如果修复成功，接口返回修复过程中全局区块表中指定表的hash总和。
  
 
#### 恢复用户历史表数据和全局区块表数据
以omm用户为例进行操作，步骤如下。
1. 以操作系统用户omm登录数据库主节点。
2. 使用EXECUTE DIRECT对某个DN节点进行用户历史表修复操作。 
   ```
   gaussdb=# EXECUTE DIRECT ON (datanode1) 'select pg_catalog.ledger_hist_repair(''ledgernsp'', ''usertable'');';
   ```
   查询结果如下：
   ```
   ledger_hist_repair
   --------------------
    84e8bfc3b974e9cf
   (1 row)
   ```
   该结果表明datanode1节点用户历史表修复成功，修复造成的用户历史表hash增量为84e8bfc3b974e9cf。
   
   
3. 连接CN执行全局区块表修复操作。 
   ```
   gaussdb=# SELECT pg_catalog.ledger_gchain_repair('ledgernsp', 'usertable');
   ```
   查询结果如下：
   ```
   ledger_gchain_repair
   ----------------------
    a41714001181a294
   (1 row)
   ```
   该结果表明，当前集群全局区块表修复成功，且向当前CN节点插入一条修复数据，其hash值为a41714001181a294。
   
   
 
 #### 恢复用户表和用户历史表名称
已通过enable_recyclebin参数和recyclebin_retention_time参数开启闪回DROP功能，恢复用户表和用户历史表名称。示例如下：
- DROP用户表，对用户表执行闪回DROP。使用ledger_hist_repair对用户表、用户历史表进行表名恢复。
  ```
  -- 对用户表执行闪回drop，使用ledger_hist_repair对用户历史表进行表名恢复。
  gaussdb=# CREATE TABLE ledgernsp.tab2(a int, b text);
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'a' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'rec_num' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  CREATE TABLE
  gaussdb=# DROP TABLE ledgernsp.tab2;
  DROP TABLE
  gaussdb=# SELECT rcyrelid, rcyname, rcyoriginname FROM gs_recyclebin;
   rcyrelid |           rcyname            |    rcyoriginname
  ----------+------------------------------+---------------------
      32838 | BIN$39B523388046$55C8400==$0 | tab2
      32846 | BIN$39B52338804E$55C90E8==$0 | gs_hist_tab2_index
      32843 | BIN$39B52338804B$55C96A0==$0 | ledgernsp_tab2_hist
      32841 | BIN$39B523388049$55C9EE0==$0 | pg_toast_32838
  (4 rows)
  -- 对用户表执行闪回drop。
  gaussdb=# TIMECAPSULE TABLE ledgernsp.tab2 TO BEFORE DROP;
  TimeCapsule Table
  -- 使用ledger_hist_repair恢复用户历史表表名。
  gaussdb=# SELECT ledger_hist_repair('ledgernsp', 'tab2');
   ledger_hist_repair
  --------------------
   0000000000000000
  (1 row)
  gaussdb=# \d+ ledgernsp.tab2;
                            Table "ledgernsp.tab2"
     Column    |  Type   | Modifiers | Storage  | Stats target | Description
  -------------+---------+-----------+----------+--------------+-------------
   a           | integer |           | plain    |              |
   b           | text    |           | extended |              |
   hash_1d2d14 | hash16  |           | plain    |              |
  Has OIDs: no
  Distribute By: HASH(a)
  Location Nodes: ALL DATANODES
  Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast
  History table name: ledgernsp_tab2_hist
  -- 对用户表执行闪回drop，使用ledger_hist_repair对用户表进行表名恢复。
  gaussdb=# CREATE TABLE ledgernsp.tab3(a int, b text);
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'a' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'rec_num' as the distribution column by default.
  HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
  CREATE TABLE
  gaussdb=# DROP TABLE ledgernsp.tab3;
  DROP TABLE
  gaussdb=# SELECT rcyrelid, rcyname, rcyoriginname FROM gs_recyclebin;
   rcyrelid |           rcyname            |    rcyoriginname
  ----------+------------------------------+---------------------
      32952 | BIN$80B6233880B8$FECFF98==$0 | tab3
      32960 | BIN$80B6233880C0$FED0C98==$0 | gs_hist_tab3_index
      32957 | BIN$80B6233880BD$FED1250==$0 | ledgernsp_tab3_hist
      32955 | BIN$80B6233880BB$FED1A00==$0 | pg_toast_32952
  (4 rows)
  -- 对用户历史表执行闪回drop。
  gaussdb=# TIMECAPSULE TABLE blockchain.ledgernsp_tab3_hist TO BEFORE DROP;
  TimeCapsule Table
  -- 拿到回收站中用户表对应的rcyname，使用ledger_hist_repair恢复用户表表名。
  gaussdb=# SELECT ledger_hist_repair('ledgernsp', 'BIN$80B6233880B8$FECFF98==$0');
   ledger_hist_repair
  --------------------
   0000000000000000
  (1 row)
  gaussdb=# \d+ ledgernsp.tab3;
                            Table "ledgernsp.tab3"
     Column    |  Type   | Modifiers | Storage  | Stats target | Description
  -------------+---------+-----------+----------+--------------+-------------
   a           | integer |           | plain    |              |
   b           | text    |           | extended |              |
   hash_7a0c87 | hash16  |           | plain    |              |
  Has OIDs: no
  Distribute By: HASH(a)
  Location Nodes: ALL DATANODES
  Options: orientation=row, compression=no, storage_type=USTORE, segment=off, toast.storage_type=USTORE, toast.toast_storage_type=enhanced_toast
  History table name: ledgernsp_tab3_hist
  -- 删除表。
  gaussdb=# DROP TABLE ledgernsp.tab2 PURGE;
  DROP TABLE
  gaussdb=# DROP TABLE ledgernsp.tab3 PURGE;
  DROP TABLE
  ```
  
 
