Recently encountered a block corruption problem where the type of corruption was “NO LOGGING” and luckily the blocks that were affected were “Free Blocks” . The solution for this scenario is detailed in MOS Note 336133.1 and as it mentions, the block needs to be reformatted in order to mark it as corruption-free.

The proposed solution for this involves creating an interim table, allocating the free blocks to this table, inserting data – which would lead to reformatting of the blocks . The rationale being that the reformatting of blocks occurs just before a DML operation makes modification/use of the block.

The problem we ran into while going through the solution was that we wern’t able to allocate one of the free corrupt blocks to my interim table. The allocation operation would encounter ORA-01653.

In case the datafile that has encountered corruption is the only datafile in the tablespace, then it is strongly recommended to add an additional datafile to your tablespace before you go through the solution described in the MOS Note 336133.1, in order to prevent the tablespace running out of space.

SQL>  Select * from v$database_block_corruption ;

     FILE#     BLOCK#     BLOCKS CORRUPTION_CHANGE# CORRUPTIO
---------- ---------- ---------- ------------------ ---------
         5     323264          8         6.7978E+12 NOLOGGING


set pagesize 2000
set lines 300
col segment_name for a30
col segment_type for a30
col owner for a30
col PARTITION_NAME for a20

SELECT e.owner, e.segment_type, e.segment_name, e.partition_name, c.file#
, greatest(e.block_id, c.block#) corr_start_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(e.block_id+e.blocks-1, c.block#+c.blocks-1)
- greatest(e.block_id, c.block#) + 1 blocks_corrupted
, null description
FROM dba_extents e, v$database_block_corruption c
WHERE e.file_id = c.file#
AND e.block_id <= c.block# + c.blocks - 1
AND e.block_id + e.blocks - 1 >= c.block#
UNION
SELECT s.owner, s.segment_type, s.segment_name, s.partition_name, c.file#
, header_block corr_start_block#
, header_block corr_end_block#
, 1 blocks_corrupted
, 'Segment Header' description
FROM dba_segments s, v$database_block_corruption c
WHERE s.header_file = c.file#
AND s.header_block between c.block# and c.block# + c.blocks - 1
UNION
SELECT null owner, null segment_type, null segment_name, null partition_name, c.file#
, greatest(f.block_id, c.block#) corr_start_block#
, least(f.block_id+f.blocks-1, c.block#+c.blocks-1) corr_end_block#
, least(f.block_id+f.blocks-1, c.block#+c.blocks-1)
- greatest(f.block_id, c.block#) + 1 blocks_corrupted
, 'Free Block' description
FROM dba_free_space f, v$database_block_corruption c
WHERE f.file_id = c.file#
AND f.block_id <= c.block# + c.blocks - 1
AND f.block_id + f.blocks - 1 >= c.block#
ORDER BY file#, corr_start_block#;
  

OWNER                          SEGMENT_TYPE       SEGMENT_NAME                   PARTITION_NAME                      FILE# CORR_START_BLOCK# CORR_END_BLOCK# BLOCKS_CORRUPTED DESCRIPTION
------------------------------ ------------------ ------------------------------ ------------------------------ ---------- ----------------- --------------- ---------------- ------------------------
                                                                                                                                                                                                                                                  5            323264          323271                8 Free Block

      

After running through the initial steps we wereable to create an interim table AK.S and allocate all but one of the corrupt free blocks to it. The block that continued to remain unallocated after multiple allocation attempts was 323264.

06:06:53 SYS >alter table AK.S allocate extent (DATAFILE '/u01/oradata/test/test.dbf' SIZE 1M);
alter table AK.S allocate extent (DATAFILE '/u01/oradata/test/test.dbf' SIZE 1M)
*
ERROR at line 1:
ORA-01653: unable to extend table AK.S by 128 in tablespace TEST
06:07:15 SYS @ >Select * from dba_free_space where file_id= 5   and 323376  between block_id and block_id + blocks -1;

TABLESPACE_NAME                   FILE_ID   BLOCK_ID      BYTES     BLOCKS RELATIVE_FNO
------------------------------ ---------- ---------- ---------- ---------- ------------
TEST                                    5     323264    1048576        128            5

The first thing that was attempted (and which didn’t help) was to reduce the next_extent for the table and then attempt to allocate a smaller chunk of contiguous space to the table. However it appears that the enough contiguous free space was not available & hence it couldn’t be allocated to our table.

06:11:55 SYS @ >select next_extent, pct_increase from dba_Tables where table_name='S' and owner ='AK';

NEXT_EXTENT PCT_INCREASE
----------- ------------
    1048576

06:30:33 SYS @ >aLTER table AK.s STORAGE ( next 128k pctfree 10 );

Table altered.

Elapsed: 00:00:00.00

06:31:49 SYS @ >select next_extent, pct_increase from dba_Tables where table_name='S' and owner ='AK';

NEXT_EXTENT PCT_INCREASE
----------- ------------
     131072


06:31:49 SYS @ >alter table AK.s allocate extent (DATAFILE '/u01/oradata/test/test.dbf' SIZE 1K);
alter table ak.s allocate extent (DATAFILE '/u01/oradata/test/test.dbf' SIZE 1K)
*
ERROR at line 1:
ORA-01653: unable to extend table AK.S by 128 in tablespace TEST

Since this was not helping, the next thing that was attempted was to look at the tablespace block map and determine the segments occupying the adjoining blocks. If those segments could be moved, perhaps it would free up enough contiguous free space, which could then be allocated to the interim table for re-formatting .

A script to help you with this could be Script: To Create Tablespace Block Map (Doc ID 1019474.6)

You can use the output to identify segments in the adjoining blocks. In this case the adjoining objects were table EQUIP.ATTR & an index EQUIP.EXCHANGER_IDX

Tablespace     File  Block Id    Size                  Segment
--------------- ---- ---------- -------- --------------------------------------
TEST              5          1        1
...
....
                        323,128        8 EQUIP.SITE_MAP
                        323,256        8 EQUIP.ATTR
                        323,264      128
                        323,392       16 EQUIP.EXCHANGER_IDX
                        323,408        8 EQUIP.SITE_MAP
...
....

The next bit was to identify a suitable window to move the table and index to free up these blocks.

23:17:14 SYS @ >alter table EQUIP.ATTR move;

Table altered.


Elapsed: 00:00:00.00
23:18:10 SYS @ >alter index EQUIP.EXCHANGER_IDX rebuild;

Index altered.

23:20:12 SYS @ >alter tablespace TEST coalesce;

Tablespace altered.

Tablespace     File  Block Id    Size                  Segment
--------------- ---- ---------- -------- --------------------------------------
TEST              5          1        1
...
....
			323,248        8  EQUIP.SITE_MAP
                        323,256      152
                        323,408        8  EQUIP.SITE_MAP

...
....

After moving these segments the contiguous space had grown and the subsequent attempt to allocate blocks to the table AK.S succeed. Finally the corrupt block was no longer “Free” and had been allocated to our interim Dummy table.

23:21:25 SYS @ >BEGIN
for i in 1..1000000 loop
EXECUTE IMMEDIATE 'alter table AK.s allocate extent (DATAFILE '||'''/u01/oradata/test/test.dbf''' ||'SIZE 1K) ';
end loop;
end ;
/
23:21:25   2  23:21:25   3  23:21:25   4  23:21:25   5  23:21:25   6  BEGIN
*
ERROR at line 1:
ORA-01653: unable to extend table AK.S by 128 in tablespace TEST
ORA-06512: at line 3


Now on rechecking we can see that the space has been allocated to the interim table. So now we could proceed with inserting data into this table to re-format the corrupt blocks.


OWNER   SEGMENT_TYPE   SEGMENT_NAME FILE# CORR_START_BLOCK# CORR_END_BLOCK# BLOCKS_CORRUPTED 
------- ------------   ------------ ----- ----------------- --------------- ---------------- 
  AK        TABLE      S             5     323376           323383            8

We then follow the MOS note and insert data into the table to format all the corrupt blocks and finally run an RMAN validate for the datafile in question to verify that it was indeed now corruption free.


23:27:15 SYS @ >Begin
  FOR i IN 1..1000000000 loop
    for j IN 1..1000000 loop
      Insert into ak.s VALUES(i,'x');
    end loop;
    commit;
  END LOOP;
END;
/23:27:15   2  23:27:15   3  23:27:15   4  23:27:15   5  23:27:15   6  23:27:15   7  23:27:15   8  23:27:15   9
Begin
*
ERROR at line 1:
ORA-20000: Corrupt block has been formatted
ORA-06512: at "SYS.CORRUPT_TRIGGER", line 10
ORA-04088: error during execution of trigger 'SYS.CORRUPT_TRIGGER'
ORA-06512: at line 4


RMAN> Backup validate check logical datafile 5;

Starting backup at 20-DEC-20
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=16 device type=DISK
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00005 name=/u01/oradata/test/test.dbf
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:15
List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
5    OK     0              22417        489728          6797783084585
  File Name: /u01/oradata/test/test.dbf
  Block Type Blocks Failing Blocks Processed
  ---------- -------------- ----------------
  Data       0              185689
  Index      0              272859
  Other      0              8763

Finished backup at 20-DEC-20