One of the easiest ways is to use diagnostic events:
alter session set events 'sql_trace {callstack: fname xctend} errorstack(1)';
One of the easiest ways is to use diagnostic events:
alter session set events 'sql_trace {callstack: fname xctend} errorstack(1)';
Oracle diagnostic events is a great feature, but unfortunately poorly documented and nonintuitive, so it’s difficult to remember all events/actions/parameters and even read its internal documentation using oradebug. So I decided to compile its internal doc as a more convenient html-version (https://orasql.org/files/events/) and make a cheat sheet of some unknown or little-known use cases.
alter system set events
'kg_event[1476]
{occurence: start_after 1, end_after 3}
trace("stack is: %\n", shortstack())
errorstack(2)
';
alter system set events
'trace[SQL_Compiler.* | SQL_Execution.*]
[SQL: ...]
{process: ospid = ...}
{occurence:end_after 3}
controlc_signal()';
Today I wanted to give a link to the description of v$sql_hint.target_level to show that no_parallel can be specified for statement or object, and though it’s pretty obvious, but surprisingly I haven’t found any articles or posts about it, so this short post describes it.
v$sql_hint.target_level is a bitset, where
1st bit set to 1 means that the hint can be specified on statement level,
2nd – on query block level,
3rd – on object level,
4th – on join level(for multiple objects).
Short example:
select name,sql_feature ,class,inverse ,version,version_outline ,target_level ,decode(bitand(target_level,1),0,'no','yes') Statement_level ,decode(bitand(target_level,2),0,'no','yes') Query_block_level ,decode(bitand(target_level,4),0,'no','yes') Object_level ,decode(bitand(target_level,8),0,'no','yes') Join_level from v$sql_hint h;
with hints as ( select name,sql_feature ,class,inverse ,version,version_outline ,target_level ,decode(bitand(target_level,1),0,'no','yes') Statement_level ,decode(bitand(target_level,2),0,'no','yes') Query_block_level ,decode(bitand(target_level,4),0,'no','yes') Object_level ,decode(bitand(target_level,8),0,'no','yes') Join_level from v$sql_hint h ) select * from hints where statement_level='yes' and to_number(regexp_substr(version,'^\d+')) >= 18 order by version;
Result:
NAME SQL_FEATURE CLASS VERSION TARGET_LEVEL STATEMENT_LEVEL QUERY_BLOCK_LEVEL OBJECT_LEVEL JOIN_LEVEL ----------------- --------------- -------------------- -------- ------------ --------------- ----------------- ------------ ---------- PDB_LOCAL_ONLY QKSFM_DML PDB_LOCAL_ONLY 18.1.0 1 yes no no no SUPPRESS_LOAD QKSFM_DDL SUPPRESS_LOAD 18.1.0 1 yes no no no SYSTEM_STATS QKSFM_ALL SYSTEM_STATS 18.1.0 1 yes no no no MEMOPTIMIZE_WRITE QKSFM_EXECUTION MEMOPTIMIZE_WRITE 18.1.0 1 yes no no no SKIP_PROXY QKSFM_ALL SKIP_PROXY 18.1.0 1 yes no no no CURRENT_INSTANCE QKSFM_ALL CURRENT_INSTANCE 18.1.0 1 yes no no no JSON_LENGTH QKSFM_EXECUTION JSON_LENGTH 19.1.0 1 yes no no no QUARANTINE QKSFM_EXECUTION QUARANTINE 19.1.0 1 yes no no no
A friend contacted me to ask why they were having problems using the table_stats hint to influence optimizer decision making and also to influence the decision to use direct read or buffer cache scan so this is just a quick blog post to clarify the syntax as it is not well documented.
table_stats(<table_name> <method> {<keyword>=<value>} )
Method is one of: DEFAULT, SET, SCALE, SAMPLE
Keyword is one of: BLOCKS, ROWS, ROW_LENGTH
This interesting question was posted on our russian forum yesterday:
We have a huge PL/SQL package and this simple function returns wrong result when it’s located at the end of package body:
create or replace package body PKGXXX as ... function ffff return number is nRes number; begin nRes := 268435456; return nRes; end; end; /But it works fine in any of the following cases:
* replace 268435456 with power(2, 28), or
* replace 268435456 with small literal like 268, or
* move this function to the beginning of package body
The one of the interesting findings was that the returned value is equal to the one of literals in another function.
We can reproduce this bug even with an anonymous pl/sql block. The following test case uses 32768 integer literals from 1000001 to 1032768 and prints 5 other integers:
declare n number; begin n:=1000001; -- this part n:=1000002; -- creates n:=1000003; -- 32768 ... -- integer n:=1032768; -- literals dbms_output.put_line('100000='||100000); -- it should print: 100000=100000 dbms_output.put_line('32766 ='||32766); dbms_output.put_line('32767 ='||32767); dbms_output.put_line('32768 ='||32768); dbms_output.put_line('32769 ='||32769); end;
declare c clob:='declare n number;begin'||chr(10); f varchar2(100):='n:=%s;'||chr(10); v varchar2(32767); n number:=32768; begin for i in 1..n loop v:=v||utl_lms.format_message(f,to_char(1e7+i)); if length(v)>30000 then c:=c||v; v:=''; end if; end loop; v:=v||q'[ dbms_output.put_line('100000='||100000); dbms_output.put_line('32766 ='||32766); dbms_output.put_line('32767 ='||32767); dbms_output.put_line('32768 ='||32768); dbms_output.put_line('32769 ='||32769); end; ]'; c:=c||v; execute immediate c; end; /
100000=10000001 32766 =32766 32767 =32767 32768 =10000002 32769 =10000003
This test case well demonstrates wrong results:
* instead of 100000 we get 10000001, which is the value from first line after “begin”, ie 1st integer literal in the code,
* for 32766 and 32767 oracle returns right values
* instead of 32768 (==32767+1) it returns 10000002, which is the integer from 2nd line, ie 2nd integer literal in the code,
* instead of 32769 (==32767+2) it returns 10000003, which is the integer from 3rd line, ie 3rd integer literal in the code
After several tests I can make a conclusion:
So we can describe this behaviour using first test case:
declare n number; begin n:=1000001; -- this part n:=1000002; -- creates n:=1000003; -- 32768 ... -- integer n:=1032768; -- literals dbms_output.put_line('100000='||100000); -- it should print 100000, ie 32768th element of array, but prints 10000001 -- where 10000001 is the 1st element of array (1==mod(32768,32767)) dbms_output.put_line('32766 ='||32766); -- these 2 lines print right values, dbms_output.put_line('32767 ='||32767); -- because their values are in the range of -32768..32767 dbms_output.put_line('32768 ='||32768); -- this line contains 32769th element and prints 2nd element of array (2==mod(32769,32767)) dbms_output.put_line('32769 ='||32769); -- this line contains 32770th element and prints 3nd element of array (3==mod(32770,32767)) end;
The following query can help you to find objects which can potentially have this problem:
select s.owner,s.name,s.type ,sum(regexp_count(text,'(\W|^)3\d{4,}([^.0-9]|$)')) nums_count -- this regexp counts integer literals >= 30000 from dba_source s where owner='&owner' and type in ('FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY') group by s.owner,s.name,s.type having sum(regexp_count(text,'(\W|^)3\d{4,}([^.0-9]|$)'))>32767 -- filter only objects which have >=32767 integer literal
Workaround:
You may noticed that I wrote about INTEGER literals only, so the easiest workaround is to make them FLOAT – just add “.” to the end of each literal:
declare n number; begin n:=1000001.; n:=1000002.; n:=1000003.; ... n:=1032768.; dbms_output.put_line('100000='||100000.); dbms_output.put_line('32766 ='||32766.); dbms_output.put_line('32767 ='||32767.); dbms_output.put_line('32768 ='||32768.); dbms_output.put_line('32769 ='||32769.); end;
declare c clob:='declare n number;begin'||chr(10); f varchar2(100):='n:=%s.;'||chr(10); -- I've added here "." v varchar2(32767); n number:=32768; begin for i in 1..n loop v:=v||utl_lms.format_message(f,to_char(1e7+i)); if length(v)>30000 then c:=c||v; v:=''; end if; end loop; v:=v||q'[ dbms_output.put_line('100000='||100000.); -- . dbms_output.put_line('32766 ='||32766.); dbms_output.put_line('32767 ='||32767.); dbms_output.put_line('32768 ='||32768.); dbms_output.put_line('32769 ='||32769.); end; ]'; c:=c||v; execute immediate c; end; /
I’ve troubleshooted one query today and I was very surprised that bind variables in this query were specified with &ersand instead of :colon! I have never seen this before and I couldn’t find anything about this in documentation…
Unfortunately SQL*Plus doesn’t support ampersand yet, even if you disable define (“set define off”),
so I’ve tested such behaviour with this code:
set def off serverout on exec declare s varchar2(1); begin execute immediate 'select 1 from dual where dummy=&var' into s using 'X'; dbms_output.put_line(s); end;
And it really works! //at least on 11.2.0.2 and 12.2.0.1
SQL> set def off serverout on SQL> exec declare s varchar2(1); begin execute immediate 'select 1 from dual where dummy=&var' into s using 'X'; dbms_output.put_line(s); end; 1 PL/SQL procedure successfully completed. SQL> select substr(sql_text,1,40) stext,sql_id,executions,rows_processed from v$sqlarea a where sql_text like '%dual%&var'; STEXT SQL_ID EXECUTIONS ROWS_PROCESSED ------------------------------------- ------------- ---------- -------------- select 1 from dual where dummy=&var ckkw4u3atxz02 3 3 SQL> select * from table(dbms_xplan.display_cursor('ckkw4u3atxz02')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------- SQL_ID ckkw4u3atxz02, child number 0 ------------------------------------- select 1 from dual where dummy=&var Plan hash value: 272002086 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | |* 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("DUMMY"=:VAR) 18 rows selected.
Update: Btw, it works for SQL only, not for PL/SQL:
SQL> var v varchar2(1); SQL> begin &v = 'Z'; end; 2 / begin &v = 'Z'; end; * ERROR at line 1: ORA-06550: line 1, column 7: PLS-00103: Encountered the symbol "&" when expecting one of the following: SQL> exec &v := 'X'; BEGIN &v := 'X'; END; * ERROR at line 1: ORA-06550: line 1, column 7: PLS-00103: Encountered the symbol "&" when expecting one of the following: The symbol "&" was ignored.
SQL> exec :v := 'X'; PL/SQL procedure successfully completed. SQL> select * from dual where dummy=&v 2 ; D - X
And we can can use mixed placeholders:
SQL> select * from dual where dummy=&v and &v=:v; D - X
I’ve wrote in previous post
4. Next row piece can be stored in the same block only with inserts. When you run update, oracle will place new row piece into another block.
But it’s not valid anymore ๐ Since 12.2 Oracle optimizes updates too.
You can check it on 12.2 and previous version using example 4 from previous post:
drop table test purge; set serverout on alter session set tracefile_identifier='test4'; declare cols varchar2(32000):='c_1 char(3)'; procedure print_and_exec(c varchar2) as begin dbms_output.put_line(c); execute immediate c; end; begin for i in 2..355 loop cols:=cols||',c_'||i||' char(3)'; end loop; print_and_exec ('create table test('||cols||')'); print_and_exec ('insert into test(c_1) values(null)'); commit; for i in 256..355 loop execute immediate 'update test set c_'||i||'='||i; end loop; commit; execute immediate 'alter system flush buffer_cache'; for r in (select 'alter system dump datafile '||file#||' block '||block# cmd from ( select distinct file#,block# from v$bh where class#=1 and objd in (select o.data_object_id from user_objects o where object_name='TEST') order by 1,2 ) ) loop execute immediate r.cmd; end loop; end; / disc;
PS. My presentation about Intra-block row chaining from RuOUG seminar: Intra-block row chaining(RuOUG)
I’ve seen some blogs recommending thatย _partition_large_extents be set to FALSE for a variety of space conserving reasons without the authors thinking about the negative impact this is going to have on Smart Scan. Large Extents cause an INITIAL allocation of 8 MB and a NEXT allocation of 1 MB and they have been the default for table spaces on Exadata since 11.2.0.2. You can verify that large extents are in use by a given table or partition by:
Select segment_flags
From sys_dba_segs
where segment_name = <table_name>
and owner = <schema_name>;
The segment flag bit for large extents is 0x40000000.
This pattern of allocation is design to work optimally with Smart Scan because Smart Scan intrinsically works in 1 MB chunks. Reads of ASM allocation units are split into maximum 1 MB chunks to be passed to the filter processing library to have their blocks sliced and diced to create the synthetic blocks that contain only the rows and columns of interest to return to the table scan driver. When less than 1 MB gets allocated at a time to a segment and then the next contiguous blocks gets allocated to a different segment, each separate run of blocks will be read by a different MBR. Each run will be passed separately to Smart Scan and we get sub-optimal chunks to work on increasing both the overhead of processing and the number of round trips needed to process the table scan. The design of Smart Scan is predicated on scooping up contiguous runs of data from disk for efficient processing.
This matters particularly for HCC data and for chained rows.
Continue reading→You might be familiar with intra-block row chaining, which can occur when a table has more than 255 columns. However, did you know that intra-block chaining only works with inserts, not updates? (Upd: This is not valid since version 12.2).
Documentation says:
When a table has more than 255 columns, rows that have data after the 255th column are likely to be chained within the same block. This is called intra-block chaining. A chained row’s pieces are chained together using the rowids of the pieces. With intra-block chaining, users receive all the data in the same block. If the row fits in the block, users do not see an effect in I/O performance, because no extra I/O operation is required to retrieve the rest of the row.
A bit more details:
1. A single row piece can store up to 255 columns.
2. Oracle splits fields into row pieces in reverse order.
3. Oracle doesn’t store trailing NULLs in a row (not in a row piece)
4. The next row piece can be stored in the same block only with inserts. When you run an update, oracle will place the new row piece into a different block. (not valid since 12.2)
I’ll show in examples with dumps:
I’ve just found out that we can specify query block for PRECOMPUTE_SUBQUERY: /*+ precompute_subquery(@sel$2) */
So we can use it now with SQL profiles, SPM baselines and patches.
SQL> select/*+ precompute_subquery(@sel$2) */ * from dual where dummy in (select chr(level) from dual connect by level<=100); D - X SQL> @last PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ SQL_ID c437vsqj7c4jy, child number 0 ------------------------------------- select/*+ precompute_subquery(@sel$2) */ * from dual where dummy in (select chr(level) from dual connect by level<=100) Plan hash value: 272002086 --------------------------------------------------------------------------- | Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 2 (100)| | |* 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 | --------------------------------------------------------------------------- Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 / DUAL@SEL$1 Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("DUMMY"='' OR "DUMMY"='' OR "DUMMY"='โฅ' OR "DUMMY"='โฆ' OR "DUMMY"='โฃ' OR "DUMMY"='โ ' OR "DUMMY"='' OR "DUMMY"=' OR "DUMMY"=' ' OR "DUMMY"=' ' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"=' ' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='โบ' OR "DUMMY"='โ' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='โ' OR "DUMMY"='โ' OR "DUMMY"='' OR "DUMMY"=' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"='' OR "DUMMY"=' ' OR "DUMMY"='!' OR "DUMMY"='"' OR "DUMMY"='#' OR "DUMMY"='$' OR "DUMMY"='%' OR "DUMMY"='&' OR "DUMMY"='''' OR "DUMMY"='(' OR "DUMMY"=')' OR "DUMMY"='*' OR "DUMMY"='+' OR "DUMMY"=',' OR "DUMMY"='-' OR "DUMMY"='.' OR "DUMMY"='/' OR "DUMMY"='0' OR "DUMMY"='1' OR "DUMMY"='2' OR "DUMMY"='3' OR "DUMMY"='4' OR "DUMMY"='5' OR "DUMMY"='6' OR "DUMMY"='7' OR "DUMMY"='8' OR "DUMMY"='9' OR "DUMMY"=':' OR "DUMMY"=';' OR "DUMMY"='<' OR "DUMMY"='=' OR "DUMMY"='>' OR "DUMMY"='?' OR "DUMMY"='@' OR "DUMMY"='A' OR "DUMMY"='B' OR "DUMMY"='C' OR "DUMMY"='D' OR "DUMMY"='E' OR "DUMMY"='F' OR "DUMMY"='G' OR "DUMMY"='H' OR "DUMMY"='I' OR "DUMMY"='J' OR "DUMMY"='K' OR "DUMMY"='L' OR "DUMMY"='M' OR "DUMMY"='N' OR "DUMMY"='O' OR "DUMMY"='P' OR "DUMMY"='Q' OR "DUMMY"='R' OR "DUMMY"='S' OR "DUMMY"='T' OR "DUMMY"='U' OR "DUMMY"='V' OR "DUMMY"='W' OR "DUMMY"='X' OR "DUMMY"='Y' OR "DUMMY"='Z' OR "DUMMY"='[' OR "DUMMY"='\' OR "DUMMY"=']' OR "DUMMY"='^' OR "DUMMY"='_' OR "DUMMY"='`' OR "DUMMY"='a' OR "DUMMY"='b' OR "DUMMY"='c' OR "DUMMY"='d'))
PS. I’m not sure, but as far as i remember, when I tested it on 10.2, it didn’t work with specifying a query block.
And I have never seen such usage.