Showing posts with label bootstrap$. Show all posts
Showing posts with label bootstrap$. Show all posts

April 8, 2016

orphan entries in col$ (due to creating online index failure) make gather stats job fail with ORA-01847

The gather stats job always fail on one of our core table with below error:

EXEC dbms_stats.gather_table_stats(ownname=> 'SYSADM', 
tabname=>'gbprestatiegroep', method_opt=>'For all columns size 1',estimate_percent=> 100, 
cascade=>false, degree=>1, BLOCK_SAMPLE=>false, NO_INVALIDATE=>false ); 
Error report: 
ORA-01847: day of month must be between 1 and last day of month 
ORA-06512: at "SYS.DBMS_STATS", line 23829 
ORA-06512: at "SYS.DBMS_STATS", line 23880


With "cascade=>false" in above command, we define to only gather table data stats.
Then, we treid to gather index only stats for the indexes on this table and they all succeeded:

gather_index_stats(OWNNAME=> 'SYSADM', INDNAME=> 'IX_GBP_PATTRACKING',  GRANUL
gather_index_stats(OWNNAME=> 'SYSADM', INDNAME=> 'DPG17',  GRANULARITY=>'ALL',
gather_index_stats(OWNNAME=> 'SYSADM', INDNAME=> 'DPG2',  GRANULARITY=>'ALL',e
gather_index_stats(OWNNAME=> 'SYSADM', INDNAME=> 'DPG14',  GRANULARITY=>'ALL',
gather_index_stats(OWNNAME=> 'SYSADM', INDNAME=> 'CGBP01',  GRANULARITY=>'ALL'
gather_index_stats(OWNNAME=> 'SYSADM', INDNAME=> 'CIND_GBP_PK_S_KEY2',  GRANUL
............


Above result seems to indicate data (corruption/invalidation/broken) issue on the table while the data in index is fine.
However, below command can return result correctly without any issue:
select /*+full(table)*/ * from table;
select /*+full(table)*/ to_char(column1,'yyyy-mm-dd hh24:mi:ss'), to_char(column2,'yyyy-mm-dd hh24:mi:ss'), ..... from table;


To investigate, I enabled level 3 errorstack, and I captured below issue SQL:

select /*+  no_parallel(t) no_parallel_index(t) dbms_stats cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring no_substrb_pad  */
count(*),
count("SYS_NC00074$"),
count("SYS_NC00075$"),
count("SYS_NC00076$"),
count("SYS_NC00077$"),
count("SYS_NC00078$"),
count("SYS_NC00079$"),
sum(sys_op_opnsize("SLEUTEL")),
sum(sys_op_opnsize("CLIENTAPPLIC")),
sum(sys_op_opnsize("DEMONSTRATION_FU1")),
count("DEMONSTRATION_FU2"),
sum(sys_op_opnsize("DEMONSTRATION_FU2")),
count("CONTACTTEMPLATE"),
sum(sys_op_opnsize("CONTACTTEMPLATE")),
count("MEDICALVALIDATION"),
sum(sys_op_opnsize("AANVRAGER")),
...................
...................
from "SYSADM"."GBPRESTATIEGROEP" t ;


What the hell are these columns? They do not show up if we use "DESC GBPRESTATIEGROEP" command:
count("SYS_NC00074$"),
count("SYS_NC00075$"),
count("SYS_NC00076$"),
count("SYS_NC00077$"),
count("SYS_NC00078$"),
count("SYS_NC00079$"),


After verifying, we confirmed that it is below columns that lead to the issue, and we can reproduce by executing.

SQL> set lines 300
SQL> set pagesize 500
SQL> select
2  count("SYS_NC00074$"),
3  count("SYS_NC00075$"),
4  count("SYS_NC00076$"),
5  count("SYS_NC00077$"),
6  count("SYS_NC00078$"),
7  count("SYS_NC00079$")
8   from "SYSADM"."GBPRESTATIEGROEP" t ;
count("SYS_NC00074$"),
*
ERROR at line 2:
ORA-01847: day of month must be between 1 and last day of month

Above SYS_NC0007* columns does not exist in DBA_INDEXES/DBA_OBJECTS/DBA_COLUMNS.

However, in col$, I find below:

SQL> select OBJ#,COL#,SEGCOL#,SEGCOLLENGTH,NAME,TYPE#,NULL$,DEFLENGTH,DEFAULT$,INTCOL#,PROPERTY  
  2  from col$ where name in ('SYS_NC00074$','SYS_NC00075$','SYS_NC00076$','SYS_NC00077$','SYS_NC00078$','SYS_NC00079$') and obj#=15182;
      OBJ#       COL#    SEGCOL# SEGCOLLENGTH NAME                                TYPE#      NULL$  DEFLENGTH DEFAULT$                                                                            INTCOL#   PROPERTY
---------- ---------- ---------- ------------ ------------------------------ ---------- ---------- ---------- -------------------------------------------------------------------------------- ---------- ----------
     15182          0          0            7 SYS_NC00074$                          180          0         81 TO_TIMESTAMP(TO_CHAR("PLANUITVOERING",'DD-MON-RR'),'yyyy-mm-dd hh24:mi:sssss.ff'         74     327976
     15182          0          0            7 SYS_NC00075$                          180          0         75 TO_TIMESTAMP(TO_CHAR("PLANUITVOERING",'DD-MON-RR'),'yyyy-mm-dd hh24:mi:ss')              75     327976
     15182          0          0            7 SYS_NC00076$                          180          0         78 TO_TIMESTAMP(TO_CHAR("PLANUITVOERING",'DD-MON-RR'),'yyyy-mm-dd hh24:mi:sssss')           76     327976
     15182          0          0            7 SYS_NC00077$                          180          0         75 TO_TIMESTAMP(TO_CHAR("PLANUITVOERING",'DD-MON-RR'),'yyyy-mm-dd hh24:mi:ss')              77     327976
     15182          0          0            7 SYS_NC00078$                          180          0         75 TO_TIMESTAMP(TO_CHAR("PLANUITVOERING",'DD-MON-RR'),'yyyy-mm-dd hh24:mi:ss')              78     327976
     15182          0          0            7 SYS_NC00079$                          180          0         75 TO_TIMESTAMP(TO_CHAR("PLANUITVOERING",'DD-MON-RR'),'yyyy-MM-dd hh24:mi:ss')              79     327976

6 rows selected. 

For last a few years, constant performance turning has always being on-going for this site.
And during the process, many indexes were created based on poor SQL found or suggested by sql advisor.

It might be that when creating above function indexes, the creating function index command failed, and for some reason, it left behind orphan entries in col$.

After testing, I managed to reproduce the bug in 11.2.0.3.10, while it does not exist in 11.2.0.4 though.



SQL> create table SYSADM.TESTORPHAN as select created from DBA_OBJECTS where 1=2;
Table created.

SQL> insert into SYSADM.TESTORPHAN values (to_date('2000-07-27 14:47:30','YYYY-MM-DD HH24:MI:SS'));
1 row created.

SQL> commit;
Commit complete.

SQL> create index SYSADM.INT_ERROR on SYSADM.TESTORPHAN(TO_TIMESTAMP(TO_CHAR("CREATED",'DD-MON-RR'),'yyyy-MM-dd hh24:mi:ss')) online;
create index SYSADM.INT_ERROR on SYSADM.TESTORPHAN(TO_TIMESTAMP(TO_CHAR("CREATED",'DD-MON-RR'),'yyyy-MM-dd hh24:mi:ss')) online
ERROR at line 1:
ORA-01847: day of month must be between 1 and last day of month

---------here it will leaves orphan entries in col$ table-------------


This is an obvious defect in online index (re)build cleanup function.
I checked below tables and I cannot find any entries for above columns there:
IND$
ICOL$
CON$
SEG$
CCOL$
icol$
icoldep$


I believe we can fix the issue by executing below SQLs:

delete from col$ where obj#=15182 and name in ('SYS_NC00074$','SYS_NC00075$','SYS_NC00076$','SYS_NC00077$','SYS_NC00078$','SYS_NC00079$');
update tab$ set INTCOLS=73, AVGRLN=228 where obj#=15182;
update obj$ set SPARE2=1 where obj#=15182;
commit;
shutdown abort
startup


As this is very critical environment and a huge site, I found an outage time to implement the fix by first enabling flashback on. The fix worked as I expected, the gather table stats job then completed successfully for this table.
Cheers.

More......

June 29, 2012

hack the core:bootstrap$, replace bootstrap$ with user's table

bootstrap$ is the most core table for database. It contains defination for oracle most critical dictinary tables such as: obj$, tab$, ts$ etc.

During an startup, oracle will read bootstrap$ to build dictionary for whole DB.
Any modification on bootstrap$ may destroy your database, and oracle support won't help anything on that.

Below is  bootstrap$ structure and one row in it:

SYS @ geded > select count(*) from bootstrap$;
  COUNT(*)
----------
        60

SYS @ geded > select * from bootstrap$ where line#=18;

     LINE#       OBJ#
---------- ----------
SQL_TEXT
-------------------------------------------------------------------------------------
        18         18
CREATE TABLE OBJ$("OBJ#" NUMBER NOT NULL,"DATAOBJ#" NUMBER,"OWNER#" NUMBER NOT NULL,"NAME" VARCHAR2(30) NOT NULL,"NAMESPACE" NUMBER NOT NULL,"SUBNAME" VARCHAR2(30),"TYPE#" NUMBER NOT NULL,"C
TIME" DATE NOT NULL,"MTIME" DATE NOT NULL,"STIME" DATE NOT NULL,"STATUS" NUMBER NOT NULL,"REMOTEOWNER" VARCHAR2(30),"LINKNAME" VARCHAR2(128),"FLAGS" NUMBER,"OID$" RAW(16),"SPARE1" NUMBER,"SP
ARE2" NUMBER,"SPARE3" NUMBER,"SPARE4" VARCHAR2(1000),"SPARE5" VARCHAR2(1000),"SPARE6" DATE) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE (  INITIAL 16K NEXT 104K MINEXTENTS 1 MAXEXT
ENTS 2147483645 PCTINCREASE 0 OBJNO 18 EXTENTS (FILE 1 BLOCK 240))

SYS @ geded > desc bootstrap$;
Name              Null?    Type
-------------- ---------- ---------------
LINE#          NOT NULL   NUMBER
OBJ#           NOT NULL   NUMBER
SQL_TEXT       NOT NULL   VARCHAR2(4000)

Below is one possible error for bootstrap$ failure during startup:
ORA-01092: ORACLE instance terminated. Disconnection forced
ORA-00704: bootstrap process failure.

Today, let's hack the bootstrap$, even replace it with our own table:
Kevin.Zhang >  select count(*) from bootstrap$;
  COUNT(*)
----------
        60

Kevin.Zhang > shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.

Kevin.Zhang > startup upgrade;
ORACLE instance started.
Total System Global Area  730714112 bytes
Fixed Size                  2230080 bytes
Variable Size             318769344 bytes
Database Buffers          322961408 bytes
Redo Buffers               86753280 bytes
Database mounted.
Database opened.

Kevin.Zhang > create table KILLBOOT as select * from bootstrap$;
Table created.

Kevin.Zhang > delete from KILLBOOT where LINE#=59;
1 row deleted.

Kevin.Zhang > commit;
Commit complete.

Kevin.Zhang > delete from bootstrap$;
60 rows deleted.

Kevin.Zhang > commit;
Commit complete.

Here we use an internal package DBMS_DDL_INTERNAL.SWAP_BOOTSTRAP to swap bootstrap$ to our new table KILLBOOT.
Infact package DBMS_DDL_INTERNAL.SWAP_BOOTSTRAP only do one thing, to update kcvfhrdb in super block(file 1 block 1):
Kevin.Zhang > exec DBMS_DDL_INTERNAL.SWAP_BOOTSTRAP('KILLBOOT');
PL/SQL procedure successfully completed.

Kevin.Zhang > update obj$ set name='BOOTSTRAP_DEL' where name='BOOTSTRAP$';
1 row updated.

Kevin.Zhang > commit;
Commit complete.

Kevin.Zhang > shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.

Kevin.Zhang > startup;
ORACLE instance started.
Total System Global Area  730714112 bytes
Fixed Size                  2230080 bytes
Variable Size             318769344 bytes
Database Buffers          322961408 bytes
Redo Buffers               86753280 bytes
Database mounted.
Database opened.

Kevin.Zhang > select count(*) from bootstrap$;
  COUNT(*)
----------
        59

Kevin.Zhang > select count(*) from bootstrap_del;
  COUNT(*)
----------
         0

Kevin.Zhang > select count(*) from KILLBOOT;
  COUNT(*)
----------
        59


We are done, let's prove that we have already succeeded in replacing bootstrap$ with our new table KILLBOOT:
Kevin.Zhang > select dbms_rowid.rowid_relative_fno(rowid) file_id,dbms_rowid.rowid_block_number(rowid) block_id from bootstrap$ where rownum<10;

   FILE_ID   BLOCK_ID
---------- ----------
         1      60817
         1      60817
         1      60817
         1      60817
         1      60817
         1      60817
         1      60817
         1      60817
         1      60817

9 rows selected.

From the block_id we can clearly identify since this block belong to KILLBOOT table.
More......