715 lines
30 KiB
SQL
715 lines
30 KiB
SQL
--------------------------------------------------------------------------------
|
|
-- Author: Alberto Dell'Era
|
|
-- Copyright: (c) 2008, 2009, 2013 Alberto Dell'Era http://www.adellera.it
|
|
--------------------------------------------------------------------------------
|
|
|
|
/*
|
|
type ref_object_t is record (
|
|
object_id int,
|
|
object_type varchar2(30 char),
|
|
object_owner varchar2(30 char),
|
|
object_name varchar2(30 char),
|
|
base_table_object_id int,
|
|
base_table_owner varchar2(30 char),
|
|
base_table_name varchar2(30 char)
|
|
); */
|
|
|
|
function calc_base_table_object_id (p_object_id int)
|
|
return int
|
|
is
|
|
begin
|
|
-- N.B. Even for partitioned tables accessed with FROM T PARTITION(P),
|
|
-- gv$sql_plan.object# is always the object_id of the table, not the partition.
|
|
-- Same for partitioned indexes.
|
|
for o in (select /*+ xplan_exec_marker */ owner, object_name, object_type
|
|
from sys.all_objects
|
|
where object_id = p_object_id)
|
|
loop
|
|
if o.object_type = 'TABLE' then
|
|
return p_object_id;
|
|
elsif o.object_type in ('SEQUENCE', 'VIEW') then
|
|
return null;
|
|
elsif o.object_type in ('INDEX') then
|
|
for o2 in (select /*+ xplan_exec_marker */ table_owner, table_name
|
|
from sys.all_indexes
|
|
where owner = o.owner
|
|
and index_name = o.object_name)
|
|
loop
|
|
for o3 in (select /*+ xplan_exec_marker */ object_id
|
|
from sys.all_objects
|
|
where object_type = 'TABLE'
|
|
and owner = o2.table_owner
|
|
and object_name = o2.table_name)
|
|
loop
|
|
return o3.object_id;
|
|
end loop;
|
|
end loop;
|
|
end if;
|
|
end loop;
|
|
|
|
return null;
|
|
end calc_base_table_object_id;
|
|
|
|
function get_cache_base_table_object_id (p_object_id int)
|
|
return int
|
|
is
|
|
begin
|
|
if p_object_id is null then
|
|
return null;
|
|
end if;
|
|
if not m_cache_base_table_object_id.exists(to_char(p_object_id)) then
|
|
m_cache_base_table_object_id(to_char(p_object_id)) := calc_base_table_object_id (to_char(p_object_id));
|
|
end if;
|
|
return to_number(m_cache_base_table_object_id (to_char(p_object_id)));
|
|
end get_cache_base_table_object_id;
|
|
|
|
procedure cache_obj_infos (p_object_id int)
|
|
is
|
|
begin
|
|
if not m_cache_obj_infos.exists (to_char(p_object_id)) then
|
|
declare
|
|
l_obj_infos obj_infos_t;
|
|
begin
|
|
l_obj_infos.object_type := 'obj#'||p_object_id;
|
|
l_obj_infos.owner := 'obj#'||p_object_id;
|
|
l_obj_infos.object_name := 'obj#'||p_object_id;
|
|
for r in (select /*+ xplan_exec_marker */ owner, object_name, object_type
|
|
from sys.all_objects
|
|
where object_id = p_object_id)
|
|
loop
|
|
--if r.object_type <> 'TABLE' then
|
|
-- raise_application_error (-20001, 'object_id="'||p_object_id||'" is of type "'||r.object_type||'", not TABLE.');
|
|
--end if;
|
|
l_obj_infos.object_type := r.object_type;
|
|
l_obj_infos.owner := r.owner;
|
|
l_obj_infos.object_name := r.object_name;
|
|
end loop;
|
|
m_cache_obj_infos(to_char(p_object_id)) := l_obj_infos;
|
|
end;
|
|
end if;
|
|
end cache_obj_infos;
|
|
|
|
function get_cache_obj_name (p_object_id int)
|
|
return varchar2
|
|
is
|
|
begin
|
|
if p_object_id is null then
|
|
return null;
|
|
end if;
|
|
cache_obj_infos (p_object_id);
|
|
return m_cache_obj_infos(to_char(p_object_id)).object_name;
|
|
end get_cache_obj_name;
|
|
|
|
function get_cache_obj_owner (p_object_id int)
|
|
return varchar2
|
|
is
|
|
begin
|
|
if p_object_id is null then
|
|
return null;
|
|
end if;
|
|
cache_obj_infos (p_object_id);
|
|
return m_cache_obj_infos(to_char(p_object_id)).owner;
|
|
end get_cache_obj_owner;
|
|
|
|
function get_virtual_col_expr (
|
|
p_table_owner varchar2,
|
|
p_table_name varchar2,
|
|
p_virt_col_name varchar2
|
|
)
|
|
return varchar2
|
|
is
|
|
l_ret long;
|
|
begin
|
|
-- try FBI expression
|
|
for i in (select /*+ xplan_exec_marker */ index_owner, index_name, column_position
|
|
from sys.all_ind_columns
|
|
where table_owner = p_table_owner and table_name = p_table_name
|
|
and column_name = p_virt_col_name)
|
|
loop
|
|
for e in (select /*+ xplan_exec_marker */ column_expression
|
|
from sys.all_ind_expressions
|
|
where table_owner = p_table_owner and table_name = p_table_name
|
|
and index_owner = i.index_owner and index_name = i.index_name
|
|
and column_position = i.column_position)
|
|
loop
|
|
if l_ret != e.column_expression then
|
|
return 'INTERNAL ERROR conflicting virtual col defs found for'||p_virt_col_name;
|
|
end if;
|
|
if l_ret is null then
|
|
l_ret := e.column_expression;
|
|
end if;
|
|
end loop;
|
|
end loop;
|
|
l_ret := 'I:' || l_ret;
|
|
|
|
-- try multi-column expression
|
|
&COMM_IF_LT_11G if l_ret is null then
|
|
&COMM_IF_LT_11G. for e in (select /*+ xplan_exec_marker */ extension
|
|
&COMM_IF_LT_11G. from sys.all_stat_extensions
|
|
&COMM_IF_LT_11G. where owner = p_table_owner and table_name = p_table_name
|
|
&COMM_IF_LT_11G. and extension_name = p_virt_col_name)
|
|
&COMM_IF_LT_11G. loop
|
|
&COMM_IF_LT_11G. l_ret := 'E:'|| e.extension;
|
|
&COMM_IF_LT_11G. end loop;
|
|
&COMM_IF_LT_11G. end if;
|
|
return l_ret;
|
|
end get_virtual_col_expr;
|
|
|
|
function get_part_key_list (
|
|
p_owner varchar2,
|
|
p_name varchar2,
|
|
p_object_type varchar2
|
|
)
|
|
return varchar2
|
|
is
|
|
l_ret varchar2(500 char);
|
|
begin
|
|
for r in (select /*+ xplan_exec_marker */ column_name
|
|
from sys.all_part_key_columns
|
|
where owner = p_owner
|
|
and name = p_name
|
|
and object_type = p_object_type
|
|
order by column_position)
|
|
loop
|
|
l_ret := l_ret || r.column_name || ', ';
|
|
end loop;
|
|
return rtrim (l_ret, ', ');
|
|
end get_part_key_list;
|
|
|
|
function get_subpart_key_list (
|
|
p_owner varchar2,
|
|
p_name varchar2,
|
|
p_object_type varchar2
|
|
)
|
|
return varchar2
|
|
is
|
|
l_ret varchar2(500 char);
|
|
begin
|
|
for r in (select /*+ xplan_exec_marker */ column_name
|
|
from sys.all_subpart_key_columns
|
|
where owner = p_owner
|
|
and name = p_name
|
|
and object_type = p_object_type
|
|
order by column_position)
|
|
loop
|
|
l_ret := l_ret || r.column_name || ', ';
|
|
end loop;
|
|
return rtrim (l_ret, ', ');
|
|
end get_subpart_key_list;
|
|
|
|
procedure print_table_infos (p_object_id int)
|
|
is
|
|
type t_prog_number is table of binary_integer index by varchar2(61 char);
|
|
l_index_number t_prog_number;
|
|
l_cons_u_number t_prog_number;
|
|
l_cons_r_number t_prog_number;
|
|
l_cons_r2_number t_prog_number;
|
|
type t_virt_expressions is table of varchar2(2000 char) index by varchar2(30 char);
|
|
l_virt_expressions t_virt_expressions;
|
|
l_table_owner varchar2(30) := get_cache_obj_owner (p_object_id);
|
|
l_table_name varchar2(30) := get_cache_obj_name (p_object_id);
|
|
l_scf scf_state_t;
|
|
l_iot_type sys.all_tables.iot_type%type;
|
|
l_partitioned sys.all_tables.partitioned%type;
|
|
l_temporary sys.all_tables.temporary%type;
|
|
l_duration sys.all_tables.duration%type;
|
|
l_tmp long;
|
|
l_tmp2 varchar2(1 char);
|
|
l_data_mod varchar2(100 char);
|
|
l_num_rows number;
|
|
l_columns_names_inited boolean;
|
|
begin
|
|
-- label each index with a progressive number
|
|
for i in (select /*+ xplan_exec_marker */
|
|
owner index_owner, index_name
|
|
from sys.all_indexes
|
|
where table_owner = l_table_owner and table_name = l_table_name
|
|
order by index_name, owner -- keep this row aligned with rows labeled as "block b001"
|
|
)
|
|
loop
|
|
l_index_number(i.index_owner||'.'||i.index_name) := l_index_number.count;
|
|
end loop;
|
|
-- label each unique/reference constraint with a progressive number
|
|
-- UQ and from-this-table-to-others FK
|
|
for co in (select /*+ xplan_exec_marker */ constraint_name, constraint_type
|
|
from sys.all_constraints
|
|
where owner = l_table_owner and table_name = l_table_name
|
|
and constraint_type in ('U','R')
|
|
order by decode (constraint_type,'P',1,'U',2,'R',3), constraint_name -- keep this row aligned with rows labeled as "block b003"
|
|
)
|
|
loop
|
|
if co.constraint_type = 'U' then
|
|
l_cons_u_number (co.constraint_name) := l_cons_u_number.count;
|
|
elsif co.constraint_type = 'R' then
|
|
l_cons_r_number (co.constraint_name) := l_cons_r_number.count;
|
|
end if;
|
|
end loop;
|
|
|
|
--------------- table ---------------
|
|
print ('############################################# table '||l_table_owner||'.'||l_table_name||' ###');
|
|
|
|
begin
|
|
select iot_type, partitioned, temporary, duration, num_rows
|
|
into l_iot_type, l_partitioned, l_temporary, l_duration, l_num_rows
|
|
from sys.all_tables
|
|
where owner = l_table_owner and table_name = l_table_name;
|
|
exception
|
|
when no_data_found then
|
|
print (l_table_owner||'.'||l_table_name||' not found in all_tables.');
|
|
return;
|
|
end;
|
|
|
|
if l_temporary = 'Y' then
|
|
print ('GLOBAL TEMPORARY TABLE on commit '|| case when l_duration = 'SYS$SESSION' then 'PRESERVE' else 'delete' end ||' rows');
|
|
end if;
|
|
|
|
if l_partitioned='YES' then
|
|
for r in (select /*+ xplan_exec_marker */ partitioning_type, subpartitioning_type
|
|
from sys.all_part_tables
|
|
where owner = l_table_owner and table_name = l_table_name)
|
|
loop
|
|
print ('PARTITIONED BY '||r.partitioning_type||' ( '||get_part_key_list(l_table_owner,l_table_name,'TABLE')||' ) ');
|
|
if r.subpartitioning_type<>'NONE' then
|
|
print ( 'SUBPARTITIONED BY '||r.subpartitioning_type||' ( '||get_subpart_key_list(l_table_owner,l_table_name,'TABLE')||' ) ');
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
print (l_iot_type);
|
|
|
|
-- dbms_metadata.get_ddl for table
|
|
&COMM_IF_NO_DBMS_METADATA. print('---- output of dbms_metadata.get_ddl ----');
|
|
&COMM_IF_NO_DBMS_METADATA. print_clob( dbms_metadata.get_ddl( 'TABLE', l_table_name, l_table_owner) );
|
|
|
|
-- columns
|
|
scf_reset (l_scf);
|
|
l_virt_expressions.delete;
|
|
for c in (select /*+ xplan_exec_marker */ column_id, internal_column_id, column_name, nullable,
|
|
data_type, data_length, data_precision, data_scale,
|
|
char_used, char_length, hidden_column, virtual_column
|
|
from sys.all_tab_cols
|
|
where owner = l_table_owner and table_name = l_table_name
|
|
order by column_id, internal_column_id -- keep this row aligned with rows labeled as "block b002"
|
|
)
|
|
loop
|
|
l_data_mod := '';
|
|
if c.data_type in ('NUMBER') then
|
|
if c.data_precision is not null or c.data_scale is not null then
|
|
l_data_mod := ' ('||nvl(c.data_precision,38)||','||c.data_scale||')';
|
|
end if;
|
|
elsif c.data_type in ('FLOAT') then
|
|
l_data_mod := ' ('||c.data_precision||')';
|
|
-- VARCHAR2, NVARCHAR2, CHAR, NCHAR are the only types that can have CHAR length semantics
|
|
elsif c.data_type in ('VARCHAR2', 'VARCHAR', 'NVARCHAR2', 'NVARCHAR', 'CHAR', 'NCHAR') then
|
|
if c.char_used = 'C' then
|
|
l_data_mod := ' ('||c.char_length||' char)';
|
|
else
|
|
l_data_mod := ' ('||c.data_length||' byte)';
|
|
end if;
|
|
elsif c.data_type in ('RAW') then
|
|
l_data_mod := ' ('||c.data_length||')';
|
|
end if;
|
|
scf_add_elem (l_scf, 'Id' , c.column_id);
|
|
scf_add_elem (l_scf, 'IId' , c.internal_column_id);
|
|
scf_add_elem (l_scf, 'V' , case when c.virtual_column = 'NO' then 'N' else 'Y' end);
|
|
scf_add_elem (l_scf, 'ColName', c.column_name);
|
|
scf_add_elem (l_scf, 'Type' , c.data_type||l_data_mod);
|
|
scf_add_elem (l_scf, 'Null' , case when c.nullable = 'N' then 'NOT' else 'yes' end);
|
|
if c.virtual_column ='YES' then
|
|
l_tmp := get_virtual_col_expr (l_table_owner, l_table_name, c.column_name);
|
|
l_virt_expressions (c.column_name) := l_tmp;
|
|
else
|
|
l_tmp := null;
|
|
end if;
|
|
scf_add_elem (l_scf, 'Expression', substr (l_tmp, 1, 10), p_sep_mid => 'trunc' );
|
|
end loop;
|
|
-- add to each indexed column a pointer to indexing index (via index#)
|
|
for i in (select /*+ xplan_exec_marker */
|
|
owner index_owner, index_name, uniqueness
|
|
from sys.all_indexes
|
|
where table_owner = l_table_owner and table_name = l_table_name
|
|
order by index_name, owner -- keep this row aligned with rows labeled as "block b001"
|
|
)
|
|
loop
|
|
l_tmp := to_char(l_index_number(i.index_owner||'.'||i.index_name));
|
|
for r in (select /*+ xplan_exec_marker */ ic.column_position
|
|
from sys.all_tab_cols c, sys.all_ind_columns ic
|
|
where c.owner = l_table_owner and c.table_name = l_table_name
|
|
and c.column_name = ic.column_name(+)
|
|
and ic.table_owner(+) = l_table_owner and ic.table_name(+) = l_table_name
|
|
and ic.index_owner(+) = i.index_owner and ic.index_name(+) = i.index_name
|
|
order by c.column_id, c.internal_column_id -- keep this row aligned with rows labeled as "block b002"
|
|
)
|
|
loop
|
|
scf_add_elem (l_scf, l_tmp, r.column_position, p_sep_mid => case when i.uniqueness='UNIQUE' then 'U' end);
|
|
end loop;
|
|
end loop;
|
|
|
|
-- al new
|
|
scf_print_output (l_scf, 'INTERNAL ERROR : no columns infos found', 'INTERNAL ERROR : no columns infos found(aux)');
|
|
scf_reset (l_scf);
|
|
|
|
l_columns_names_inited := false;
|
|
|
|
-- add to each constrained column a label marking it as constrained
|
|
-- 1) UQ and from-this-table-to-others FK
|
|
for co in (select /*+ xplan_exec_marker */ constraint_name, constraint_type
|
|
from sys.all_constraints
|
|
where owner = l_table_owner and table_name = l_table_name
|
|
and constraint_type in ('P','U','R')
|
|
order by decode (constraint_type,'P',1,'U',2,'R',3), constraint_name -- keep this row aligned with rows labeled as "block b003"
|
|
)
|
|
loop
|
|
if co.constraint_type = 'P' then
|
|
l_tmp := 'P';
|
|
elsif co.constraint_type = 'U' then
|
|
l_tmp := 'U'||to_char(l_cons_u_number (co.constraint_name));
|
|
elsif co.constraint_type = 'R' then
|
|
l_tmp := 'R'||to_char(l_cons_r_number (co.constraint_name));
|
|
end if;
|
|
-- mark every PK/FK constraint with at least a FK from another table to the current one
|
|
l_tmp2 := '';
|
|
if co.constraint_type in ('P','U') then
|
|
for r in (select constraint_name
|
|
from sys.all_constraints
|
|
where constraint_type = 'R'
|
|
and r_owner = l_table_owner and r_constraint_name = co.constraint_name
|
|
and rownum = 1
|
|
)
|
|
loop
|
|
-- dbms_output.put_line ('on '||co.constraint_name||' fk from '||r.constraint_name);
|
|
l_tmp2 := 'R';
|
|
end loop;
|
|
end if;
|
|
for c in (select /*+ xplan_exec_marker */ c.column_id, c.internal_column_id, c.virtual_column, c.column_name, cc.position
|
|
from sys.all_tab_cols c, sys.all_cons_columns cc
|
|
where c.owner = l_table_owner and c.table_name = l_table_name
|
|
and c.column_name = cc.column_name(+)
|
|
and cc.owner(+) = l_table_owner and cc.table_name(+) = l_table_name
|
|
and cc.constraint_name(+) = co.constraint_name
|
|
order by c.column_id, c.internal_column_id -- keep this row aligned with rows labeled as "block b002"
|
|
)
|
|
loop
|
|
if not l_columns_names_inited then
|
|
scf_add_elem (l_scf, 'Id' , c.column_id);
|
|
scf_add_elem (l_scf, 'IId' , c.internal_column_id);
|
|
scf_add_elem (l_scf, 'V' , case when c.virtual_column = 'NO' then 'N' else 'Y' end);
|
|
scf_add_elem (l_scf, 'ColName', c.column_name);
|
|
end if;
|
|
scf_add_elem (l_scf, l_tmp, case when c.position is null then null else l_tmp2||c.position end);
|
|
end loop;
|
|
l_columns_names_inited := true;
|
|
end loop;
|
|
|
|
if l_columns_names_inited then
|
|
scf_print_output (l_scf, 'INTERNAL ERROR : no columns infos found', 'INTERNAL ERROR : no columns infos found(cons)');
|
|
end if;
|
|
|
|
-- virtual column expressions
|
|
if l_virt_expressions.count > 0 then
|
|
scf_reset (l_scf);
|
|
declare
|
|
l_colname varchar2(30);
|
|
begin
|
|
l_colname := l_virt_expressions.first;
|
|
loop
|
|
exit when l_colname is null;
|
|
scf_add_elem (l_scf, 'ColName', l_colname);
|
|
scf_add_elem (l_scf, 'Expression (full)', l_virt_expressions(l_colname) );
|
|
l_colname := l_virt_expressions.next (l_colname);
|
|
end loop;
|
|
end;
|
|
scf_print_output (l_scf, 'INTERNAL ERROR: no virt expression found', 'INTERNAL ERROR: no virt expression found(aux)');
|
|
end if;
|
|
|
|
scf_reset (l_scf);
|
|
|
|
for r in (select /*+ xplan_exec_marker */
|
|
'1' typ, cast(null as number) partition_position, null as partition_name, cast(null as number) subpartition_position, null as subpartition_name,
|
|
num_rows, blocks, empty_blocks, avg_row_len, sample_size, last_analyzed, degree
|
|
from sys.all_tables
|
|
where owner = l_table_owner and table_name = l_table_name
|
|
union all
|
|
select '2' typ, partition_position, partition_name, null as subpartition_position, null as subpartition_name,
|
|
num_rows, blocks, empty_blocks, avg_row_len, sample_size, last_analyzed, null as degree
|
|
from sys.all_tab_partitions
|
|
where table_owner = l_table_owner and table_name = l_table_name
|
|
and :OPT_PARTINFOS = 'Y'
|
|
union all
|
|
select '3' typ, p.partition_position, s.partition_name, s.subpartition_position, s.subpartition_name,
|
|
s.num_rows, s.blocks, s.empty_blocks, s.avg_row_len, s.sample_size, s.last_analyzed, null as degree
|
|
from sys.all_tab_subpartitions s, sys.all_tab_partitions p
|
|
where s.table_owner = l_table_owner and s.table_name = l_table_name
|
|
and p.table_owner = l_table_owner and p.table_name = l_table_name
|
|
and s.partition_name = p.partition_name
|
|
and :OPT_PARTINFOS = 'Y'
|
|
order by typ, partition_position, subpartition_position
|
|
)
|
|
loop
|
|
scf_add_elem (l_scf, 'Pid' , r.partition_position);
|
|
scf_add_elem (l_scf, 'Partition' , r.partition_name);
|
|
scf_add_elem (l_scf, 'SPid' , r.subpartition_position);
|
|
scf_add_elem (l_scf, 'SubPart' , r.subpartition_name);
|
|
scf_add_elem (l_scf, 'num_rows' , r.num_rows);
|
|
scf_add_elem (l_scf, 'blocks' , r.blocks);
|
|
scf_add_elem (l_scf, 'empty_blocks' , r.empty_blocks);
|
|
scf_add_elem (l_scf, 'avg_row_len' , r.avg_row_len);
|
|
scf_add_elem (l_scf, 'sample_size' , r.sample_size);
|
|
scf_add_elem (l_scf, 'last_analyzed', nvl(d2s (r.last_analyzed),'* null *'));
|
|
scf_add_elem (l_scf, 'parallel' , r.degree);
|
|
end loop;
|
|
scf_print_output (l_scf, 'INTERNAL ERROR : no table infos found', 'INTERNAL ERROR : no table infos found(aux)');
|
|
if :OPT_PARTINFOS='N' and l_partitioned='YES' then
|
|
print ( 'WARNING: (sub)partitions infos not printed.');
|
|
end if;
|
|
|
|
-- column statistics
|
|
scf_reset (l_scf);
|
|
for r in (select /*+ xplan_exec_marker */
|
|
'1' typ, column_id, internal_column_id, column_name, cast(null as number) partition_position, null as partition_name, cast(null as number) subpartition_position, null as subpartition_name,
|
|
num_distinct, density, num_nulls, num_buckets, avg_col_len, sample_size, last_analyzed
|
|
&COMM_IF_LT_10G. , histogram
|
|
from sys.all_tab_cols
|
|
where owner = l_table_owner and table_name = l_table_name
|
|
union all
|
|
select '2' typ, c.column_id, c.internal_column_id, c.column_name, p.partition_position, pcs.partition_name, null as subpartition_position, null as subpartition_name,
|
|
pcs.num_distinct, pcs.density, pcs.num_nulls, pcs.num_buckets, pcs.avg_col_len, pcs.sample_size, pcs.last_analyzed
|
|
&COMM_IF_LT_10G. , pcs.histogram
|
|
from sys.all_part_col_statistics pcs, sys.all_tab_cols c, sys.all_tab_partitions p
|
|
where pcs.owner = l_table_owner and pcs.table_name = l_table_name
|
|
and c.owner = l_table_owner and c.table_name = l_table_name
|
|
and p.table_owner = l_table_owner and p.table_name = l_table_name
|
|
and pcs.column_name = c.column_name
|
|
and pcs.partition_name = p.partition_name
|
|
and :OPT_PARTINFOS = 'Y'
|
|
union all
|
|
select '3' typ, c.column_id, c.internal_column_id, c.column_name, p.partition_position, p.partition_name, s.subpartition_position, s.subpartition_name,
|
|
scs.num_distinct, scs.density, scs.num_nulls, scs.num_buckets, scs.avg_col_len, scs.sample_size, scs.last_analyzed
|
|
&COMM_IF_LT_10G. , scs.histogram
|
|
from sys.all_subpart_col_statistics scs, sys.all_tab_cols c, sys.all_tab_subpartitions s, sys.all_tab_partitions p
|
|
where scs.owner = l_table_owner and scs.table_name = l_table_name
|
|
and c.owner = l_table_owner and c.table_name = l_table_name
|
|
and s.table_owner = l_table_owner and s.table_name = l_table_name
|
|
and p.table_owner = l_table_owner and p.table_name = l_table_name
|
|
and scs.column_name = c.column_name
|
|
and scs.subpartition_name = s.subpartition_name -- it seems that subpart names are unique across the whole table
|
|
and s.partition_name = p.partition_name
|
|
and :OPT_PARTINFOS = 'Y'
|
|
order by typ, column_id, internal_column_id, partition_position, subpartition_position
|
|
)
|
|
loop
|
|
scf_add_elem (l_scf, 'ColName' , r.column_name);
|
|
scf_add_elem (l_scf, 'Partition' , r.partition_name);
|
|
scf_add_elem (l_scf, 'SubPart' , r.subpartition_name);
|
|
scf_add_elem (l_scf, 'ndv' , r.num_distinct);
|
|
scf_add_elem (l_scf, 'dens*#rows' , r.density * l_num_rows);
|
|
scf_add_elem (l_scf, 'num_nulls' , r.num_nulls);
|
|
scf_add_elem (l_scf, '#bkts' , r.num_buckets);
|
|
&COMM_IF_LT_10G. scf_add_elem (l_scf, 'hist', case r.histogram
|
|
&COMM_IF_LT_10G. when 'NONE' then null
|
|
&COMM_IF_LT_10G. when 'FREQUENCY' then 'FREQ'
|
|
&COMM_IF_LT_10G. when 'HEIGHT BALANCED' then 'HB'
|
|
&COMM_IF_LT_10G. else r.histogram
|
|
&COMM_IF_LT_10G. end);
|
|
scf_add_elem (l_scf, 'avg_col_len' , r.avg_col_len);
|
|
scf_add_elem (l_scf, 'sample_size' , r.sample_size);
|
|
scf_add_elem (l_scf, 'last_analyzed', nvl(d2s (r.last_analyzed),'* null *'));
|
|
end loop;
|
|
scf_print_output (l_scf, 'INTERNAL ERROR : no column statistics infos found', 'INTERNAL ERROR : no column statistics infos found(aux)');
|
|
if :OPT_PARTINFOS='N' and l_partitioned='YES' then
|
|
print ( 'WARNING: (sub)partitions infos not printed.');
|
|
end if;
|
|
|
|
--------------- indexes ---------------
|
|
for i in (select /*+ xplan_exec_marker */
|
|
owner index_owner, index_name, partitioned, uniqueness, index_type
|
|
from sys.all_indexes
|
|
where table_owner = l_table_owner and table_name = l_table_name
|
|
order by index_name, owner -- keep this row aligned with rows labeled as "block b001"
|
|
)
|
|
loop
|
|
scf_reset (l_scf);
|
|
print ('### index #'||l_index_number(i.index_owner||'.'||i.index_name)||': '||i.index_owner||'.'||i.index_name);
|
|
l_tmp := null;
|
|
for c in (select column_name, descend
|
|
from sys.all_ind_columns
|
|
where table_owner = l_table_owner and table_name = l_table_name
|
|
and index_owner = i.index_owner and index_name = i.index_name
|
|
order by column_position)
|
|
loop
|
|
l_tmp := l_tmp || c.column_name || case when c.descend='DESC' then ' desc' end ||', ';
|
|
end loop;
|
|
print ('on '||l_table_owner||'.'||l_table_name||' ( '||rtrim(l_tmp,', ')||' )');
|
|
|
|
print (i.uniqueness||' '||replace(i.index_type,'NORMAL','B+TREE'));
|
|
|
|
if i.partitioned='YES' then
|
|
for r in (select /*+ xplan_exec_marker */ partitioning_type, subpartitioning_type, locality
|
|
from sys.all_part_indexes
|
|
where owner = i.index_owner and index_name = i.index_name)
|
|
loop
|
|
print (r.locality||' PARTITIONED BY '||r.partitioning_type||' ( '||get_part_key_list(i.index_owner,i.index_name,'INDEX')||' ) ');
|
|
if r.subpartitioning_type<>'NONE' then
|
|
print ( 'SUBPARTITIONED BY '||r.subpartitioning_type||' ( '||get_subpart_key_list(i.index_owner,i.index_name,'INDEX')||' ) ');
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
-- dbms_metadata.get_ddl for index
|
|
&COMM_IF_NO_DBMS_METADATA. print('---- output of dbms_metadata.get_ddl ----');
|
|
&COMM_IF_NO_DBMS_METADATA. print_clob( dbms_metadata.get_ddl( 'INDEX', i.index_name, i.index_owner) );
|
|
|
|
for r in (select /*+ xplan_exec_marker */
|
|
'1' typ, cast(null as number) partition_position, null as partition_name, cast(null as number) subpartition_position, null as subpartition_name,
|
|
distinct_keys, num_rows, blevel, leaf_blocks, clustering_factor as cluf, sample_size, last_analyzed, degree
|
|
from sys.all_indexes
|
|
where owner = i.index_owner and index_name = i.index_name
|
|
union all
|
|
select '2' typ, partition_position, partition_name, null as subpartition_position, null as subpartition_name,
|
|
distinct_keys, num_rows, blevel, leaf_blocks, clustering_factor as cluf, sample_size, last_analyzed, null as degree
|
|
from sys.all_ind_partitions
|
|
where index_owner = i.index_owner and index_name = i.index_name
|
|
and :OPT_PARTINFOS = 'Y'
|
|
union all
|
|
select '3' typ, p.partition_position, s.partition_name, s.subpartition_position, s.subpartition_name,
|
|
s.distinct_keys, s.num_rows, s.blevel, s.leaf_blocks, s.clustering_factor as cluf, s.sample_size, s.last_analyzed, null as degree
|
|
from sys.all_ind_subpartitions s, sys.all_ind_partitions p
|
|
where s.index_owner = i.index_owner and s.index_name = i.index_name
|
|
and p.index_owner = i.index_owner and p.index_name = i.index_name
|
|
and s.partition_name = p.partition_name
|
|
and :OPT_PARTINFOS = 'Y'
|
|
order by typ, partition_position, subpartition_position)
|
|
loop
|
|
scf_add_elem (l_scf, 'Partition' , r.partition_name);
|
|
scf_add_elem (l_scf, 'SubPart' , r.subpartition_name);
|
|
scf_add_elem (l_scf, 'distinct_keys', r.distinct_keys);
|
|
scf_add_elem (l_scf, 'num_rows' , r.num_rows);
|
|
scf_add_elem (l_scf, 'blevel' , r.blevel);
|
|
scf_add_elem (l_scf, 'leaf_blocks' , r.leaf_blocks);
|
|
scf_add_elem (l_scf, 'cluf' , r.cluf);
|
|
scf_add_elem (l_scf, 'sample_size' , r.sample_size);
|
|
scf_add_elem (l_scf, 'last_analyzed', nvl(d2s (r.last_analyzed),'* null *'));
|
|
scf_add_elem (l_scf, 'parallel' , r.degree);
|
|
end loop;
|
|
scf_print_output (l_scf, 'INTERNAL ERROR : no index infos found', 'INTERNAL ERROR : no index infos found(aux)');
|
|
if :OPT_PARTINFOS='N' and i.partitioned='YES' then
|
|
print ( 'WARNING: (sub)partitions infos not printed.');
|
|
end if;
|
|
end loop;
|
|
end print_table_infos;
|
|
|
|
--type cache_table_printed_infos_t is table of print_buffer_t index by varchar2(20); -- binary_integer is too small for object_id
|
|
--m_cache_table_printed_infos cache_table_printed_infos_t;
|
|
procedure cache_table_printed_infos (p_object_id int)
|
|
is
|
|
l_object_id_char varchar2(20) := to_char(p_object_id);
|
|
begin
|
|
if m_cache_table_printed_infos.exists (l_object_id_char) then
|
|
return;
|
|
end if;
|
|
|
|
enable_print_buffer ('ENABLE');
|
|
|
|
print_table_infos (p_object_id);
|
|
|
|
m_cache_table_printed_infos(l_object_id_char) := m_print_buffer;
|
|
|
|
enable_print_buffer ('DISABLE');
|
|
end cache_table_printed_infos;
|
|
|
|
procedure print_cache_table_infos (p_object_id int)
|
|
is
|
|
l_object_id_char varchar2(20) := to_char(p_object_id);
|
|
begin
|
|
if p_object_id is null then
|
|
return;
|
|
end if;
|
|
cache_table_printed_infos (p_object_id);
|
|
for i in 0 .. m_cache_table_printed_infos(l_object_id_char).count-1 loop
|
|
print (m_cache_table_printed_infos(l_object_id_char)(i));
|
|
end loop;
|
|
end print_cache_table_infos;
|
|
|
|
-- following procedures are for gv$sql.program_id
|
|
procedure cache_program_info (p_program_id int)
|
|
is
|
|
begin
|
|
if m_cache_program_info.exists (p_program_id) then
|
|
return;
|
|
end if;
|
|
|
|
m_cache_program_info(p_program_id) := '(not found)'||p_program_id;
|
|
for r in (select /*+ xplan_exec_marker */ owner, object_name, object_type
|
|
from sys.all_objects
|
|
where object_id = p_program_id)
|
|
loop
|
|
m_cache_program_info(p_program_id) := r.object_type ||' '||r.owner||'.'||r.object_name;
|
|
end loop;
|
|
end cache_program_info;
|
|
|
|
function get_cache_program_info (p_program_id int)
|
|
return varchar2
|
|
is
|
|
begin
|
|
if p_program_id is null then
|
|
return null;
|
|
end if;
|
|
cache_program_info (p_program_id);
|
|
return m_cache_program_info(p_program_id);
|
|
end get_cache_program_info;
|
|
|
|
-- following procedures are for main xplan
|
|
procedure cache_username (p_user_id int)
|
|
is
|
|
begin
|
|
if m_cache_username.exists (p_user_id) then
|
|
return;
|
|
end if;
|
|
|
|
m_cache_username(p_user_id) := '(not found)'||p_user_id;
|
|
for r in (select /*+ xplan_exec_marker */ username
|
|
from sys.all_users
|
|
where user_id = p_user_id)
|
|
loop
|
|
m_cache_username(p_user_id) := r.username;
|
|
end loop;
|
|
end cache_username;
|
|
|
|
function get_cache_username (p_user_id int)
|
|
return varchar2
|
|
is
|
|
begin
|
|
if p_user_id is null then
|
|
return null;
|
|
end if;
|
|
cache_username (p_user_id);
|
|
return m_cache_username (p_user_id);
|
|
end get_cache_username;
|
|
|
|
procedure cache_user_id (p_username varchar2)
|
|
is
|
|
begin
|
|
if m_cache_user_id.exists (p_username) then
|
|
return;
|
|
end if;
|
|
|
|
m_cache_user_id(p_username) := -1;
|
|
for r in (select /*+ xplan_exec_marker */ user_id
|
|
from sys.all_users
|
|
where username = p_username)
|
|
loop
|
|
m_cache_user_id(p_username) := r.user_id;
|
|
end loop;
|
|
end cache_user_id;
|
|
|
|
function get_cache_user_id (p_username varchar2)
|
|
return varchar2
|
|
is
|
|
begin
|
|
if p_username is null then
|
|
return null;
|
|
end if;
|
|
cache_user_id (p_username);
|
|
return m_cache_user_id (p_username);
|
|
end get_cache_user_id;
|
|
|