Ghost in the Datafile: Fixing “Empty Block” Corruption in Oracle

A common frustration for DBAs occurs after identifying a corrupted index or table. You drop the object, expecting the error to vanish, but RMAN backups still fail and Data Guard continues to report a gap.

Even though the block is no longer part of a segment, it remains “physically” corrupted in the eyes of the storage layer. This post explores the “Steamroller” method for forcing a reformat of “unallocated” corrupted blocks—a technique based on Oracle Support KB150053.

The Problem: Why “Drop” Isn’t Enough

When you drop an object, Oracle marks the blocks as “Free” in the space management bitmaps (metadata), but it does not “wipe” the physical disk blocks. RMAN and Standby recovery processes scan every block up to the datafile’s High Water Mark. If they hit a block of zeros (common in TDE environments) or a garbled header, the process fails even if that block is “empty.”

Phase 1: Preparation & Isolation

Before fixing the block, you must ensure it is truly unallocated. If it belongs to a segment, you must move or drop it to clear the space.

1. Detect block corrupt

RMAN> VALIDATE DATAFILE 31;
List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
31 FAILED 0 5622 6400 9727387
File Name: /u02/app/oracle/oradata/test_corr01.dbf
Block Type Blocks Failing Blocks Processed
---------- -------------- ----------------
Data 0 0
Index 0 0
Other 1 778
validate found one or more corrupt blocks
See trace file /u02/app/oracle/diag/rdbms/tlw0002_7203/TLW0002_72031/trace/TLW0002_72031_ora_349264.trc for details
Finished validate at 13-MAY-26
SQL> SELECT * FROM v$database_block_corruption;
FILE# BLOCK# BLOCKS CORRUPTION_CHANGE# CORRUPTIO CON_ID
---------- ---------- ---------- ------------------ --------- ----------
31 776 1 0 ALL ZERO 3
SQL> Select BYTES from dba_free_space where file_id=31 and 776 between block_id and block_id + blocks -1;
no rows selected
SQL> SELECT owner, segment_name, segment_type
FROM dba_extents
WHERE file_id = 31
AND 776 BETWEEN block_id AND block_id + blocks - 1;
OWNER SEGMENT_NAME SEGMENT_TYPE
-------------------- ---------------------------------------- --------------------
TEST_FIX SOURCE_DATA TABLE

2. Evacuate the Data

If the query above returns a table name, move it. If it returns nothing, the block is already “Free Space” but still physically corrupt.

SQL> ALTER TABLE TEST_FIX.SOURCE_DATA MOVE TABLESPACE USERS;
Table altered.
SQL> PURGE TABLESPACE TEST_CORR;
Tablespace purged.
SQL> PURGE RECYCLEBIN;
Recyclebin purged.

Phase 2: The Surgical Reformat (The “Steamroller”)

We cannot format a block that the database doesn’t “own.” We create a dummy table and force it to claim the specific physical real estate where the corruption lives.

1. Create a “Single-Row” Table

We use PCTFREE 99 to ensure that each row we insert consumes an entire block. This allows us to move through the datafile like a steamroller, formatting each block individually.

CREATE TABLE TEST_FIX.dummy_fix (
n NUMBER,
c VARCHAR2(4000)
) NOLOGGING TABLESPACE test_corr PCTFREE 99;

2. Capture the Block

We allocate extents manually until the dummy_fix segment encompasses the corrupted block address.

BEGIN
FOR i IN 1..200 LOOP
EXECUTE IMMEDIATE 'ALTER TABLE TEST_FIX.dummy_fix ALLOCATE EXTENT (SIZE 64K)';
END LOOP;
END;
/

Phase 3: The Monitoring Trigger

We use a trigger to watch the rowid of every insert. Note that in Bigfile Tablespaces, the Relative File Number often returns 0, so focus on the Block ID.

CREATE OR REPLACE TRIGGER TEST_FIX.corrupt_trigger
AFTER INSERT ON TEST_FIX.dummy_fix
FOR EACH ROW
DECLARE
v_block NUMBER := dbms_rowid.rowid_block_number(:new.rowid);
BEGIN
-- Log the progress to the console
DBMS_OUTPUT.PUT_LINE('Writing to Block: ' || v_block);
-- Stop when we hit the corrupted address
IF (v_block >= 776) THEN
RAISE_APPLICATION_ERROR(-20000, 'SUCCESS: Corrupt block reached and reformatted!');
END IF;
END;
/

Phase 4: Execution & Verification

By inserting data, we force Oracle to write a fresh Block Header (including a valid ITL, Checksum, and SCN) over the corruption.

SQL>
SET SERVEROUTPUT ON
BEGIN
-- We only need a few rows because block 776 is the start of the extent
FOR i IN 1..100 LOOP
INSERT INTO TEST_FIX.dummy_fix (n, c) VALUES (i, 'FIXED');
commit;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -20000 THEN
DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
DBMS_OUTPUT.PUT_LINE(SQLERRM);
DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
COMMIT;
ELSE
RAISE;
END IF;
END;
/
Row landed in File: 0 Block: 782
--------------------------------------------------
ORA-20000: SUCCESS: Corrupt block 776 reached and reformatted!
ORA-06512: at "TEST_FIX.CORRUPT_TRIGGER", line 15
ORA-04088: error during execution of trigger 'TEST_FIX.CORRUPT_TRIGGER'
--------------------------------------------------
PL/SQL procedure successfully completed.

Final Validation

Once the trigger fires, the physical bits on disk have been replaced with a valid Oracle structure.

RMAN> VALIDATE DATAFILE 31;
You should see File Status OK and Blocks Failing 0.
List of Datafiles
=================
File Status Marked Corrupt Empty Blocks Blocks Examined High SCN
---- ------ -------------- ------------ --------------- ----------
31 OK 0 131 6400 9683105
File Name: /u02/app/oracle/oradata/test_corr01.dbf
Block Type Blocks Failing Blocks Processed
---------- -------------- ----------------
Data 0 17
Index 0 20
Other 0 6237
Check the View:
SQL> SELECT * FROM v$database_block_corruption;
no rows selected

Conclusion

This procedure (KB150053) is the most reliable way to clear “empty block corruption” that persists after an object is dropped. By leveraging the database’s own write-engine, you transform “Other” (corrupted) blocks back into healthy “Data” blocks, satisfying RMAN and ensuring smooth Data Guard operations.

Published by dbaliw

Highly experienced Oracle Database Administrator and Exadata Specialist with over 15 years of expertise in managing complex database environments. Skilled in cloud technologies, DevOps practices, and automation. Certified Oracle Cloud Infrastructure Architect and Oracle Certified Master with a strong background in performance tuning, high availability solutions, and database migrations.

Leave a comment