Peeking under the Data Dictionary Hood July 7, 2009
Posted by mwidlake in internals.Tags: data dictionary, SQL
9 comments
Have you ever wondered how some people seem to be able to find out how the Oracle database does things internally? How they work out where some of the interesting stuff is contained within the data dictionary and can pull it out?
Well, one of the simpler ways to peek under the data dictionary hood is to just look at the objects in the data dictionary itself. I’ve found some interesting things by looking at the data dictionary views. A lot of the dictionary objects areviews, like DBA_TABLES and DBA_COLUMNS. I’ll look at DBA_TABLESPACES {as it’s a bit smaller!}
You can get my script here – vw_txt.sql . As you will see, it is a terribly complex script… The only trick is to make sure that “set long 32767” as the view text is held in column of the ancient LONG datatype.
> @vw_txt Enter value for vw_name: dba_tablespaces old 6: where view_name like upper (nvl('&vw_name','WHOOPS')||'%') new 6: where view_name like upper (nvl('dba_tablespaces','WHOOPS')||'%') Any Key...> OWNER VIEW_NAME TEXT_LENGTH ------------------------------ ------------------------------ ----------- TEXT -------------------------------------------------------------------------------- SYS DBA_TABLESPACES 1724 select ts.name, ts.blocksize, ts.blocksize * ts.dflinit, decode(bitand(ts.flags, 3), 1, to_number(NULL), ts.blocksize * ts.dflincr), ts.dflminext, decode(ts.contents$, 1, to_number(NULL), ts.dflmaxext), decode(bitand(ts.flags, 4096), 4096, ts.affstrength, NULL), decode(bitand(ts.flags, 3), 1, to_number(NULL), ts.dflextpct), ts.blocksize * ts.dflminlen, decode(ts.online$, 1, 'ONLINE', 2, 'OFFLINE', 4, 'READ ONLY', 'UNDEFINED'), decode(ts.contents$, 0, (decode(bitand(ts.flags, 16), 16, 'UNDO', 'PERMANENT')), 1, 'TEMPORARY'), decode(bitand(ts.dflogging, 1), 0, 'NOLOGGING', 1, 'LOGGING'), decode(bitand(ts.dflogging, 2), 0, 'NO', 2, 'YES'), decode(ts.bitmapped, 0, 'DICTIONARY', 'LOCAL'), decode(bitand(ts.flags, 3), 0, 'USER', 1, 'SYSTEM', 2, 'UNIFORM', 'UNDEFINED'), decode(ts.plugged, 0, 'NO', 'YES'), decode(bitand(ts.flags,32), 32,'AUTO', 'MANUAL'), decode(bitand(ts.flags,64), 64,'ENABLED', 'DISABLED'), decode(bitand(ts.flags,16), 16, (decode(bitand(ts.flags, 512), 512, 'GUARANTEE', 'NOGUARANTEE')), 'NOT APPLY'), decode(bitand(ts.flags,256), 256, 'YES', 'NO'), decode(tsattr.storattr, 1, 'STORAGE', 'HOST'), decode(bitand(ts.flags,16384), 16384, 'YES', 'NO'), decode(bitand(ts.flags,64), 0, null, decode(bitand(ts.flags,65536), 65536,'FOR ALL OPERATIONS', 'DIRECT LOAD ONLY')) from sys.ts$ ts, sys.x$kcfistsa tsattr where ts.online$ != 3 and bitand(flags,2048) != 2048 and ts.ts# = tsattr.tsid 1 row selected.
You see a lot of decodes of bitand functions in these internal views. What is more interesting is to see what they are decoded into as it confirms what possible options there are.
Are you curious as to what all the possible object types in the database are? Whether your database happens to have examples of them or not? Peek inside the DBA_OBJECTS view {the following is part of that view}:-
OWNER VIEW_NAME TEXT_LENGTH ------------------------------ ------------------------------ ----------- TEXT ------------------------------------------------------------------------------- SYS DBA_OBJECTS 2992 select u.name, o.name, o.subname, o.obj#, o.dataobj#, decode(o.type#, 0, 'NEXT OBJECT', 1, 'INDEX', 2, 'TABLE', 3, 'CLUSTER', 4, 'VIEW', 5, 'SYNONYM', 6, 'SEQUENCE', 7, 'PROCEDURE', 8, 'FUNCTION', 9, 'PACKAGE', 11, 'PACKAGE BODY', 12, 'TRIGGER', 13, 'TYPE', 14, 'TYPE BODY', 19, 'TABLE PARTITION', 20, 'INDEX PARTITION', 21, 'LOB', 22, 'LIBRARY', 23, 'DIRECTORY', 24, 'QUEUE', 28, 'JAVA SOURCE', 29, 'JAVA CLASS', 30, 'JAVA RESOURCE', 32, 'INDEXTYPE', 33, 'OPERATOR', 34, 'TABLE SUBPARTITION', 35, 'INDEX SUBPARTITION', 40, 'LOB PARTITION', 41, 'LOB SUBPARTITION', 42, NVL((SELECT 'REWRITE EQUIVALENCE' FROM sum$ s WHERE s.obj#=o.obj# and bitand(s.xpflags, 8388608) = 8388608), 'MATERIALIZED VIEW'), 43, 'DIMENSION', 44, 'CONTEXT', 46, 'RULE SET', 47, 'RESOURCE PLAN', 48, 'CONSUMER GROUP', 51, 'SUBSCRIPTION', 52, 'LOCATION', 55, 'XML SCHEMA', 56, 'JAVA DATA', 57, 'EDITION', 59, 'RULE', 60, 'CAPTURE', 61, 'APPLY', 62, 'EVALUATION CONTEXT', 66, 'JOB', 67, 'PROGRAM', 68, 'JOB CLASS', 69, 'WINDOW', 72, 'WINDOW GROUP', 74, 'SCHEDULE', 79, 'CHAIN', 81, 'FILE GROUP', 82, 'MINING MODEL', 87, 'ASSEMBLY', 90, 'CREDENTIAL', 92, 'CUBE DIMENSION', 93, 'CUBE', 94, 'MEASURE FOLDER', 95, 'CUBE BUILD PROCESS', 'UNDEFINED')
Scanning the view definitions for something you become interested in is a little more tricky as the text is, as I said, stored in a long and you can’t run sql functions against a long. I use a bit of PL/SQL to get around the problem :-
-- find_vw_txt -- martin widlake 13/8/08 -- can't search view text as it is in a long. -- so let's try PL/SQL set serveroutput on size unlimited spool find_vw_txt.lst set trims on lines 120 pages 32 declare v_name varchar2(4000) :='&viewname'; v_search varchar2(100) :='&search_txt'; cursor get_vw_txt is select u.name owner, o.name name, v.textlength textlen, v.text text from sys.obj$ o , sys.view$ v , sys.user$ u where o.obj# = v.obj# and o.owner# = u.user# and o.name like upper(v_name)||'%' and v.textlength <32000; -- cant think how I stop a numeric -- error trying to pull a larger long -- into a varchar. bloody longs v_text varchar2(32000); v_where number; begin v_name :=upper (v_name); v_search := upper(v_search); for vtr in get_vw_txt loop --dbms_output.put_line('looking at '||vtr.name); v_text := vtr.text; v_text := upper(v_text); v_where := instr(v_text,v_search); if v_where !=0 then dbms_output.put_line('view '||vtr.owner||'.'||vtr.name||':** ' ||substr(v_text,greatest(0,v_where),80)||' **' ); end if; end loop; end; / spool off
It can be fun to dig into the internals this way. And useful. A few times I’ve been able to find out one or two things about how oracle is storing information.
It might occur to you that there are lots of internal pl/sql packages and you can look into them too:-
pkg_txt.sql will show you the contents of a package or stored function/procedure.
find_plsql_txt.sql will scan the stored PL/SQL for a text string.
However, a lot of the internal PL/SQL is wrapped, ie converted into a form you can’t peek into easily {well, in my case, at all, but some people know how}.
One last word of warning. You can find what look like interesting undocumented features when you peek under the covers. In V10 I came across the REVERSE function:
select reverse('Martin was here')
from dual
REVERSE(‘MARTIN
—————
ereh saw nitraM
Nice :-).
That could be useful, yes?
I’ve checked in the 11g documentation and it is still not documented. I googled “oracle reverse function” and several people have also found it. A couple ask why such a useful thing is not documented…
Well, I was “lucky” enough to find out why it is undocumented. I tried to use the REVERSE function on a number { this wason v10.1 I think}.
My session core-dumped.
I was on a production system when I tried this {Yes I KNOW!!! It was a momentary lapse}!
It seems to work OK on 10.2.0.4 and 11.1 but don’t. You have no idea if it will always work OK and no come-back to Oracle Corp if it blows up on you. My guess is they wrote the REVERSE function for internal use and did not test it for people using it “not as intended” {as I had done}.
So, my warning is, though it is fun to look under the covers and try things out, never, ever ever, ever do so on a production system. Or a test system used for proper testing. Or a development system those touchy developers are using. Or any system that you can’t afford to trash and recreate on a whim. Try it at home on your own PC, OK?