# Erreur ORA-00600 - index, ou blocs, ou lob corrompu

On trouve des erreurs ORA-0600 dans l'alert.log. Il peut s'agir de blocs disque corrompus suite à un problème d'accès disque, ou d'un index désynchronisé, ou d'un LOB corrompu dans la table (liste non exhaustive !).  
<span style="color: #e03e2d;">ORA-00600: internal error code, arguments: \[13011\], \[**117675**\], \[32437212\], \[0\], \[32705125\], \[0\], \[\], \[\], \[\], \[\], \[\], \[\]</span>

NOTE : il existe un décodeur d'ORA-00600 sur Metalink :  
[https://metalink.oracle.com/metalink/plsql/ml2\_documents.showDocument?p\_database\_id=NOT&amp;p\_id=153788.1](https://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=153788.1)

NOTE 2 : Si l'erreur ORA-00600 ne génère pas des codes décodables, en général le fichier trace associé à l'erreur 0600 permet de trouver la requête, donc la table, affectée.

### Trouver la table concernée par le bloc corrompu

Trouver le fichier et la table concernée (voir FIX ORA-00600 (1) pour la liste des paramètres de l'erreur 0600):

```SQL
SELECT owner, object_name, object_type, object_id, data_object_id
FROM dba_objects
WHERE data_object_id = 117675;
```

### ANALYZE et VALIDATE STRUCTURE

D'abord analyser la table :

```SQL
analyze table myschema.mytable validate structure cascade;
```

#### L'analyse de la table ne renvoie pas d'erreur

Si la commande ne renvoie pas d'erreur (00600), il faut faire de même avec ses indexes. Pour trouver les indexes :

```SQL
select index_name from dba_indexes where owner='MYSCHEMA' and tble_name='MYTABLE';
```

Puis, pour chaque index trouvé, valider la structure :

```SQL
analyze index MYSCHEMA.<MYTABLE_IND1> validate structure;
```

**<span style="text-decoration: underline;">Si la table a trop d'index</span>**, on peut essayer de trouver lequel a un problème par :

```SQL
SQL> alter session set max_dump_file_size = unlimited;
SQL> ALTER SESSION SET tracefile_identifier = 'mon_fichier_trace';
SQL> analyze table MYSCHEMA."MYTABLE" validate structure cascade into invalid_rows;
```

Dans 'mon\_fichier\_trace', on trouve une ligne de ce type :

<div class="pointer-container" id="bkmrk-%C2%A0"><div class="pointer anim is-page-editable"><svg class="svg-icon" data-icon="link" role="presentation" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"></svg><div class="input-group inline block"> <button class="button outline icon" data-clipboard-target="#pointer-url" title="Copier le lien" type="button"><svg class="svg-icon" data-icon="copy" role="presentation" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"></svg></button></div><svg class="svg-icon" data-icon="edit" role="presentation" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"></svg></div></div><p class="callout warning">row not found in index tsn: 1 rdba: 0x00817bfa</p>

On recherche l'index correspondant à "00817bfa" :

```SQL
SELECT owner, segment_name, segment_type, partition_name
FROM DBA_SEGMENTS
WHERE header_file = (SELECT file#
FROM v$datafile
WHERE rfile# =
dbms_utility.data_block_address_file(
to_number('00817bfa','XXXXXXXX'))
AND ts#= 1)
AND header_block = dbms_utility.data_block_address_block(
to_number('00817bfa','XXXXXXXX'));
```

<span style="text-decoration: underline;">**Sinon**</span>, on peut essayer de générer un script pour les analyser tous :

```
select 'analyze index '||owner||'.'||index_name||' validate structure' from dba_indexes where owner='MYSCHEMA' and table_name='MYTABLE';
```

#### L'analyse de la table renvoie des erreurs ORA-00600 ou ORA-01499 (échec de référence croisée à la table/index - voir fichier de trace)

```SQL
SQL> analyze table myschema.mytable validate structure cascade;
analyze table myschema.mytable validate structure cascade
*
ERROR at line 1:
ORA-01499: table/index cross reference failure - see trace file
```

la première chose à faire est de valider (par DB Verify : utilitaire "dbv") que ce n'était pas un problème physique sur le fichier du tablespace. Trouver le tablespace et le fichier contenant la table :

```SQL
SQL> select tablespace_name from dba_tables where table_name='MYTABLE';
TABLESPACE_NAME
------------------------------
MYTBLSPCE

SQL> select file_name from dba_data_files where tablespace_name='MYTBLSPCE';
FILE_NAME
---------------------------------
/u01/ORADATA/MYDB/MYTBLSPCE01.DBF

SQL> show parameter db_block_size
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_block_size                        integer     8192
```

Lancer dbv :

```shell
$ dbv file='MYTBLSPCE01.DBF' blocksize='8192'
DBVERIFY: Release 11.2.0.4.0 - Production on Fri Apr 26 16:35:54 2019
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
DBVERIFY - Verification starting : FILE = /u01/ORADATA/MYDB/MYTBLSPCE01.DBF

DBVERIFY - Verification complete

Total Pages Examined         : 3440640
Total Pages Processed (Data) : 3102612
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 320
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 139223
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 198485
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 2784020020 (1.2784020020)
```

Ici analyse sans erreur, ce n'est donc pas une erreur physique, mais bien une désynchronisation d'un index ou un lob corrompu. On reconstruit donc les indexes.

Si **dbv renvoie des erreurs**, le fichier lui-même est physiquement corrompu sur disque ! Il n'y a alors pas d'autre moyen que de re-créer la base proprement sur un disque valide et réimporter la dernière sauvegarde viable.

### Reconstruction des indexes

```SQL
SQL> ALTER INDEX "MYSCHEMA"."MYTABLE_IND1" REBUILD;
Index altered.
```

Cependant, il peut arriver que le REBUILD ne suffise pas; où qu'on décide de recréer les index dans un autre tablespace par sécurité.

### Re-création des indexes

```SQL
SQL> set long 200000 pagesize 0
SQL> select dbms_metadata.get_ddl('INDEX','MYTABLE_IND1','MYSCHEMA') from dual;

DBMS_METADATA.GET_DDL('INDEX','MYTABLE_IND1','MYSCHEMA')
-------------------------------------------------------------------------------

  CREATE INDEX "MYSCHEMA"."MYTABLE_IND1" ON "MYSCHEMA"."MYTABLE" ("COL1")
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "MYTBLSPCE"

SQL> ALTER INDEX "MYSCHEMA"."MYTABLE_IND1" UNUSABLE;
Index altered.

SQL> DROP INDEX "MYSCHEMA"."MYTABLE_IND1";
Index dropped.

SQL> CREATE INDEX "MYSCHEMA"."MYTABLE_IND1" ON "MYSCHEMA"."MYTABLE" ("COL1")
Index created.
```

Si l'index ne peut pas être recréé; exemple :

```SQL
SQL> drop index "MYSCHEMA"."MYTABLE_IND1";
drop index "MYSCHEMA"."MYTABLE_IND1"
                *
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key
```

alors il faut le reconstruire avec REBUILD, en espérant que ça suffira; ou le déplacer (il sera reconstruit par Oracle à la destination).

#### LOB : déplacer le LOB dans un autre tablespace

Si les indexes sont reconstruits sans erreurs mais que la table continue à générer des erreurs, et qu'elle contient des colonnes LOB, il peut s'agir d'un LOB corrompu. Dans ce cas, déplacer le LOB pour le reconstruire (voir [Déplacer un LOB dans un autre tablespace](https://kb.systea.fr/books/oracle/page/deplacer-un-lob-dans-un-autre-tablespace))

### Dernier recours : dupliquer la table en excluant les blocs corrompus

Le code suivant permet de dupliquer la table "tab1" vers une table "tab1\_new" sans les lignes illisibles :

```SQL
REM Create a new table based on the table that is producing errors with no rows:
create table MYSCHEMA.MYTABLE_NEW
as
select * 
from MYSCHEMA.MYTABLE -- eventually, can add : "partition(<PARTITION_NUM>)"
where  1=2;

REM Create the table "bad_rows" to keep track of ROWIDs pointing to affected rows:
create table bad_rows (row_id rowid
                      ,oracle_error_code number);
set serveroutput on
DECLARE
  TYPE RowIDTab IS TABLE OF ROWID INDEX BY BINARY_INTEGER;
  CURSOR c1 IS select /*+ index(tab1) */ rowid
  from MYSCHEMA.MYTABLE tab1 -- eventually, can add : "partition(<PARTITION_NUM>)"
  -- eventually add a filter to exclude others not necessary rows. Ex : where IMEI is NOT NULL;
  ;
  r RowIDTab;
  rows NATURAL := 20000;
  bad_rows number := 0 ;
  errors number;
  error_code number;
  myrowid rowid;
BEGIN
  OPEN c1;
  LOOP
   FETCH c1 BULK COLLECT INTO r LIMIT rows;
   EXIT WHEN r.count=0;
   BEGIN
    FORALL i IN r.FIRST..r.LAST SAVE EXCEPTIONS
     insert into MYSCHEMA.MYTABLE_NEW 
     select /*+ ROWID(A) */ *
     from MYSCHEMA.MYTABLE A -- eventually, can add : "partition(<PARTITION_NUM>)"
     where rowid = r(i);
   EXCEPTION
   when OTHERS then
    BEGIN
     errors := SQL%BULK_EXCEPTIONS.COUNT;
     FOR err1 IN 1..errors LOOP
      error_code := SQL%BULK_EXCEPTIONS(err1).ERROR_CODE;
      if error_code in (1410, 8103, 1578) then
       myrowid := r(SQL%BULK_EXCEPTIONS(err1).ERROR_INDEX);
       bad_rows := bad_rows + 1;
       insert into bad_rows values(myrowid, error_code);
      else
       raise;
      end if;
     END LOOP;
    END;
   END;
   commit;
  END LOOP;
  commit;
  CLOSE c1;
  dbms_output.put_line('Total Bad Rows: '||bad_rows);
END;
/
```

### Ressources

FIX ORA-0600 (1) : [http://dmbadi.blogspot.com/2019/06/how-to-fix-ora-600-12700-internal-error.html](http://dmbadi.blogspot.com/2019/06/how-to-fix-ora-600-12700-internal-error.html)  
FIX ORA-0600 (2) : [https://oracle-base.com/articles/misc/detect-and-correct-corruption](https://oracle-base.com/articles/misc/detect-and-correct-corruption)  
DBMS\_REPAIR : [https://www.jobacle.nl/?p=746](https://www.jobacle.nl/?p=746)  
DUMP\_ORPHAN\_KEYS, SKIP\_CORRUPT\_BLOCKS : [http://www.dba-oracle.com/concepts/dump\_orphan\_keys.htm](http://www.dba-oracle.com/concepts/dump_orphan_keys.htm)  
DBMS\_REPAIR.FIX\_CORRUPT\_BLOCK : [http://www.dba-oracle.com/t\_fix\_corrupt\_blocks.htm](http://www.dba-oracle.com/t_fix_corrupt_blocks.htm)  
DB\_ULTRA\_SAFE, DB\_BLOCK\_CHECK\* : [http://www.dba-oracle.com/t\_db\_block\_checking.htm](http://www.dba-oracle.com/t_db_block_checking.htm)  
TOUT + DBV : [http://www.datadisk.co.uk/html\_docs/oracle/db\_corruption.htm](http://www.datadisk.co.uk/html_docs/oracle/db_corruption.htm)