In an earlier post I had already written about sessions receiving an ORA-08103 error after selecting from a table that had been truncated. At that time, I wrote the following conclusion:
“At this point I have no real explanation as for why the error occurs at this number of records fetched.
I suspect oracle of storing the locations of the blocks it has to scan during a full table in a kind of array with a limited length. After this array has been processed, Oracle would use the dataobj# value (and other keys) to get the next batch of block locations. At that moment the “object no longer exists” error would be thrown as the referenced dataobj# value no longer exists (it has been increased by 1).
But at this moment this is just pure speculation.”
Some weeks ago, I read an answer on the Oracle-L list from Tanel Poder to an ORA-08103 question, in which he stated that oracle would read 10 pointers to extents from the segment header at a time.
A quick test does seem to confirm this:
The TEST tablespace is a LMT with an uniform extent size of 16K
drop table test; Table dropped. fdh@GUNNAR> create table test (veld1 char(2000)) tablespace test pctfree 0; Table created. fdh@GUNNAR> insert into test select 'x' from all_objects where rownum <= 200; 200 rows created. fdh@GUNNAR> commit; Commit complete. fdh@GUNNAR> select bytes, blocks, extents from user_segments where segment_name = 'TEST'; BYTES BLOCKS EXTENTS ---------- ---------- ---------- 425984 52 26 fdh@GUNNAR> set serveroutput on fdh@GUNNAR> -- set the arraysize to 1 to prevent oracle from "prefetching" records fdh@GUNNAR> set arraysize 1 fdh@GUNNAR> var x refcursor fdh@GUNNAR> fdh@GUNNAR> BEGIN 2 3 open :x for select veld1 from test; 4 5 END; 6 / PL/SQL procedure successfully completed. fdh@GUNNAR> fdh@GUNNAR> DECLARE 2 3 l_veld1 char(2000); 4 5 BEGIN 6 7 fetch :x into l_veld1; 8 dbms_output.put_line('veld1: ' || trim(l_veld1)); 9 10 END; 11 / veld1: x PL/SQL procedure successfully completed. -- AT THIS MOMENT ANOTHER SESSION TRUNCATES THE TABLE fdh@GUNNAR> fdh@GUNNAR> DECLARE 2 3 l_veld1 char(2000); 4 l_cnt number(3,0) := 0; 5 6 BEGIN 7 8 BEGIN 9 10 loop 11 12 fetch :x into l_veld1; 13 exit when :x%NOTFOUND; 14 l_cnt := l_cnt + 1; 15 16 end loop; 17 18 EXCEPTION 19 when others then 20 dbms_output.put_line(SQLERRM); 21 22 END; 23 24 dbms_output.put_line('fetched rows: ' || l_cnt); 25 26 END; 27 / ORA-08103: object no longer exists fetched rows: 75 PL/SQL procedure successfully completed. fdh@GUNNAR>
My block size is 8K, so I could store 4 records per db block. Before the truncate I read 1 record and after the truncate I could read 75 records before getting the error. 76 records / 4 records per block / 2 blocks per extent = 9.5 extents. Remember that the first block is used for the segment header, so pointer 1 would only point to an extent with 1 data block ( meaning that my 10 pointers point in total to 19 blocks * 4 = 76 records).
This answers the question on when you would receive the ORA-08103 error after a truncate, but also means that your dbfmrc is limited by your extent size.
In my example the 10 pointers would reference 20 blocks (not taking in account the segment header), so a dbfmrc of more then 20 would not be possible for this table.
New question: Why is a multiblock read not crossing extent bounderies?
