1 package body AD_ZD_TABLE as
2 /* $Header: ADZDTMB.pls 120.78.12020000.27 2013/06/21 13:24:42 rputchak ship $ */
3
4 C_PACKAGE constant varchar2(80) := 'ad.plsql.ad_zd_table.';
5
6 /*
7 ** --------------------------------------------------------------------
8 ** Internal
9 ** --------------------------------------------------------------------
10 */
11
12 -- log shortcut
13 procedure LOG(X_MODULE varchar2, X_LOG_TYPE varchar2, X_MESSAGE varchar2) is
14 begin
15 ad_zd.log(x_module, x_log_type, x_message);
16 end;
17
18 -- error shortcut
19 procedure ERROR(X_MODULE varchar2, X_MESSAGE varchar2) is
20 begin
21 ad_zd.error(x_module, x_message);
22 end;
23
24 -- exec shortcut (varchar2)
25 procedure EXEC(
26 X_SQL in varchar2,
27 X_LOG_MOD in varchar2,
28 X_IGNORE in boolean default false,
29 X_PHASE in varchar2 default null) is
30 begin
31 if x_phase is null then
32 -- immediate execution
33 ad_zd.exec(x_sql, x_log_mod, x_ignore);
34 else
35 -- deferred execution: < Control won't come here as of 22-Aug-2012 >
36 ad_zd_parallel_exec.load(
37 x_phase => x_phase,
38 x_sql => x_sql,
39 x_unique => true );
40 end if;
41 end;
42
43 -- exec shortcut (clob)
44 procedure EXEC(
45 X_SQL in clob,
46 X_LOG_MOD in varchar2,
47 X_IGNORE in boolean default false,
48 X_PHASE in varchar2 default null) is
49 begin
50 if x_phase is null then
51 -- immediate execution
52 ad_zd.exec(x_sql, x_log_mod, x_ignore);
53 else
54 -- deferred execution: <Control won't come here as of 22-Aug-2012 >
55 ad_zd_parallel_exec.load(
56 x_phase => x_phase,
57 x_sql => x_sql,
58 x_unique => true);
59 end if;
60 end;
61
62 -- Util function to get Newline
63 --
64 function local_chr(ascii_chr in number) return varchar2 is
65 lang varchar2(255);
66 begin
67 lang := userenv('LANGUAGE');
68 return(convert(chr(ascii_chr),
69 substr(lang, instr(lang,'.') + 1), 'US7ASCII'));
70 end local_chr;
71
72 /*
73 ** Stores patched table name for finalize and cutover processing
74 ** AD_PATCHED_TABLES.STATUS:
75 ** 'N' - New
76 ** 'C' - Completed
77 ** 'U' - Updated
78 */
79 procedure STORE(X_TABLE_OWNER in varchar2, X_TABLE_NAME in varchar2) is
80 L_STATUS varchar2(1);
81 begin
82
83 -- Get existing table status
84 select status
85 into l_status
86 from ad_patched_tables
87 where owner = x_table_owner
88 and name = x_table_name;
89
90 -- Set "Completed" table back "Updated" status
91 if (l_status = 'C') then
92 update ad_patched_tables
93 set status='U'
94 where owner = x_table_owner
95 and name = x_table_name;
96 commit;
97 end if;
98
99 exception when no_data_found then
100 -- Add missing table with "New" status
101 insert into ad_patched_tables(owner, name, status)
102 values (x_table_owner, x_table_name, 'N');
103 commit;
104
105 end STORE;
106
107
108
109 /* ==========================================================================================
110 **
111 ** Editioning View Tools
112 **
113 ** =========================================================================================*/
114
115 --
116 -- Check if Editioning View exists for this table
117 -- return: 'Y' or 'N'
118 --
119 function EV_EXISTS(
120 X_TABLE_OWNER in varchar2,
121 X_TABLE_NAME in varchar2) return varchar2
122 is
123 L_EV_NAME varchar2(30);
124 begin
125 select ev.view_name
126 into l_ev_name
127 from dba_editioning_views ev
128 where ev.owner = x_table_owner
129 and ev.view_name = ad_zd_table.ev_view(x_table_name);
130
131 return 'Y';
132
133 exception
134 when no_data_found then
135 return 'N';
136 end EV_EXISTS;
137
138 --
139 -- Return Editioning View name for given Table
140 --
141 function EV_VIEW(
142 X_TABLE_NAME in varchar2) return varchar2
143 is
144 begin
145 return substrb(x_table_name,1,29)||'#';
146 end EV_VIEW;
147
148 --
149 -- Return Table name for given Editioning View
150 -- Note: EV must exist
151 --
152 function EV_TABLE( X_EV_OWNER in varchar2,
153 X_EV_NAME in varchar2) return varchar2
154 is
155 L_TABLE_NAME varchar2(30);
156 begin
157 select ev.table_name
158 into l_table_name
159 from dba_editioning_views ev
160 where ev.owner = x_ev_owner
161 and ev.view_name = replace(x_ev_name, '$', '#');
162
163 return l_table_name;
164 end EV_TABLE;
165
166 --
167 -- Return EV View Column name for given Table Column
168 --
169 function EV_VIEW_COLUMN(
170 X_COLUMN_NAME in varchar2) return varchar2
171 is
172 begin
173 if x_column_name like '%#_%' then
174 -- this is a versioned column, strip the version
175 return substrb(x_column_name, 1, instrb(x_column_name,'#',-1)-1);
176 end if;
177 return x_column_name;
178 end EV_VIEW_COLUMN;
179
180 --
181 -- Return Table Column for given EV View Column
182 -- In other words, translate logical table.column to actual table.column
183 -- Note: EV must exist
184 --
185 function EV_TABLE_COLUMN(
186 X_EV_OWNER in varchar2,
187 X_EV_NAME in varchar2,
188 X_COLUMN_NAME in varchar2) return varchar2
189 is
190 L_COLUMN_NAME varchar2(30);
191 begin
192 select evc.table_column_name
193 into l_column_name
194 from dba_editioning_view_cols evc
195 where evc.owner = replace(x_ev_owner, '$', '#')
196 and evc.view_name = x_ev_name
197 and evc.view_column_name = x_column_name;
198
199 return l_column_name;
200 end EV_TABLE_COLUMN;
201
202 --
203 -- Returns the Table Column Revision Tag for
204 -- a given column name. Returns '0' if the
205 -- column name does not have revision tag.
206 --
207 function EV_TABLE_COLUMN_REVISION(
208 X_COLUMN_NAME in varchar2) return varchar2
209 is
210 l_col_revision varchar2(10);
211 begin
212 if x_column_name like '%#_%' then
213 return substrb(x_column_name, instrb(x_column_name,'#', -1) + 1, length(x_column_name));
214 end if;
215 return '0';
216 end;
217
218
219 /*
220 ** Is the table a Seed Data Table? Returns'Y'/'N'
221 */
222 function IS_SEED(
223 X_TABLE_OWNER in varchar2,
224 X_TABLE_NAME in varchar2) return varchar2
225 is
226 L_IS_SEED varchar2(1);
227 begin
228 begin
229 select 'Y'
230 into l_is_seed
231 from dba_tab_columns c
232 where c.owner = x_table_owner
233 and c.table_name = x_table_name
234 and c.column_name = 'ZD_EDITION_NAME';
235 exception
236 when no_data_found then
237 return 'N';
238 end;
239
240 return 'Y';
241 end;
242
243
244 /*
245 ** Fetch Multiple DDLs
246 ** x_object_type - object type (TRIGGER)
247 ** x_object_owner - Trigger owner
248 ** x_object_name - trigger name
249 */
250 function FETCH_MULTIPLE_DDLS (
251 X_OBJECT_OWNER in varchar2,
252 X_OBJECT_NAME in varchar2,
253 X_OBJECT_TYPE in varchar2) return SYS.KU$_DDLS
254 is
255 C_MODULE varchar2(80) := c_package||'fetch_multiple_ddls';
256 L_OPEN_HANDLE number;
257 L_TRANSFORM_HANDLE number;
258 L_DDL_STMTS sys.ku$_ddls;
259 begin
260 log(c_module, 'STATEMENT',
261 'Getting DDLs for '||x_object_owner||'.'||x_object_name||', '|| x_object_type);
262
263 l_open_handle := dbms_metadata.open(x_object_type);
264 dbms_metadata.set_filter(l_open_handle, 'SCHEMA', x_object_owner);
265 dbms_metadata.set_filter(l_open_handle, 'NAME', x_object_name);
266 l_transform_handle := dbms_metadata.add_transform(l_open_handle, 'DDL');
267 dbms_metadata.set_transform_param(l_transform_handle, 'SQLTERMINATOR', false);
268
269 l_ddl_stmts := dbms_metadata.fetch_ddl(l_open_handle);
270 dbms_metadata.close(l_open_handle);
271
272 return l_ddl_stmts;
273 end FETCH_MULTIPLE_DDLS;
274
275
276 /*
277 ** Alter triggers for a given table
278 **
279 ** X_MODE - COMPILE or 'ENABLE' or 'DISABLE' the trigger
280 **
281 */
282 procedure ALTER_TRIGGER( X_TRIGGER_OWNER in varchar2,
283 X_TRIGGER_NAME in varchar2,
284 X_MODE in varchar2)
285 is
286 C_MODULE varchar2(80) := c_package||'alter_trigger';
287 L_STMT varchar2(256);
288 begin
289 l_stmt := 'alter trigger "'||x_trigger_owner||'"."'|| x_trigger_name ||'" '||x_mode;
290 exec (l_stmt, c_module, false); -- ignore errors
291 end;
292
293 --
294 -- This procedure copies the Object grants from Table to EV.
295 --
296 -- NOTE: All grants can NOT be copied because some grants may not
297 -- be applicable on a VIEW while same may be applicable on
298 -- a table.
299 --
300 -- View PRIVILEGES
301 -- ==============
302 -- DEBUG, DELETE, INSERT, MERGE, REFERENCES, SELECT, UNDER, UPDATE
303 --
304 -- TABLE PRIVILEGES :
305 -- ==================
306 -- ALTER, DELETE, DEBUG, INDEX, INSERT, REFERENCES, SELECT, UPDATE
307 --
308 procedure COPY_GRANTS(
309 X_TABLE_OWNER in varchar2,
310 X_TABLE_NAME in varchar2,
311 X_EV_NAME in varchar2)
312 is
313
314 C_MODULE varchar2(127) := c_package||'COPY_GRANTS';
315 L_STR varchar2(1026);
316 L_EV_STR_PRIVILEGE varchar2(1024);
317 cursor C_GRANTS(P_TABLE_OWNER varchar2,
318 P_TABLE_NAME varchar2,
319 P_VIEW_NAME varchar2) is
320 select distinct
321 tpt.grantee
322 , tpt.privilege
323 , tpt.grantable
324 , tpt.hierarchy
325 from
326 dba_tab_privs tpt
327 where tpt.owner = p_table_owner
328 and tpt.table_name = p_table_name
329 and tpt.privilege in ('SELECT', 'UPDATE', 'INSERT', 'DELETE', 'DEBUG')
330 and tpt.grantee <> 'SYSTEM'
331 and not exists
332 ( select 'x'
333 from dba_tab_privs tpv
334 where tpv.owner = tpt.owner
335 and tpv.table_name = p_view_name
336 and tpv.grantee = tpt.grantee
337 and tpv.privilege = tpt.privilege
338 )
339 order by grantee;
340
341 begin
342
343 l_ev_str_privilege := 'GRANT ';
344
345 begin
346 for grant_rec in c_grants (x_table_owner, x_table_name, x_ev_name)
347 loop
348
349 l_ev_str_privilege := l_ev_str_privilege || grant_rec.privilege || ' ON "' ||
350 x_table_owner || '"."'||x_ev_name || '" TO "' ||
351 grant_rec.grantee || '" ' ;
352
353 if (nvl(grant_rec.grantable, 'NO') = 'YES' ) then
354 l_ev_str_privilege := l_ev_str_privilege || ' WITH GRANT OPTION ';
355 end if;
356
357 if (nvl(grant_rec.hierarchy, 'NO') = 'YES' and grant_rec.privilege='SELECT' ) then
358 l_ev_str_privilege := l_ev_str_privilege || ' WITH HIERARCHY OPTION ';
359 end if;
360
361 -- Ignore if any error
362 exec(l_ev_str_privilege, c_module, true);
363 l_ev_str_privilege := 'GRANT ';
364
365 end loop;
366 exception
367 when others then
368 raise;
369 end;
370
371 end COPY_GRANTS;
372
373 --
374 -- Moves a trigger defined on a table / EV to a table synonym
375 -- This signature becomes non-public, after the changes for Bug 13597311
376 -- GB TODO: use EV instead of synonym
377 procedure MOVE_TRIGGER(
378 X_TRIGGER_OWNER varchar2,
379 X_TRIGGER_NAME Varchar2,
380 X_TRIGGER_STATUS varchar2,
381 X_VALID_STATUS varchar2,
382 X_TABLE_OWNER varchar2,
383 X_TABLE_NAME varchar2,
384 X_SYNONYM_OWNER varchar2,
385 X_SYNONYM_NAME varchar2 )
386 is
387 C_MODULE varchar2(127) := c_package ||'move_trigger';
388 L_DDL_LOB clob;
389 L_INDEX pls_integer :=0;
390 L_SYN_OWNER varchar2(30);
391 L_SYNONYM_NAME varchar2(30);
392 L_DDL_STMTS sys.ku$_ddls;
393
394 -- Match#1 : exact owner.table
395 L_REG_EXP_PATTERN_1 varchar2(127):= '[[:space:]]ON[[:space:]]*("?' || X_TABLE_OWNER ||
396 '"?)[.]?"?'|| X_TABLE_NAME ||'"?[[:space:]]';
397
398 -- Match#2 : owner.EV
399 L_REG_EXP_PATTERN_2 varchar2(127) := '[[:space:]]ON[[:space:]]*("?' || X_TABLE_OWNER ||
400 '"?)[.]?"?'|| ev_view(X_TABLE_NAME) ||'"?[[:space:]]';
401
402 -- Match#2_1 : trg-owner.EV
403 -- SSTOMAR:
404 -- It has been found if synonym points to EV then DBMS_METADATA returns
405 -- [... ON "trg-owner"."EV-name"] , NOT [ ... ON "ev-owner"."ev-name" ]
406 L_REG_EXP_PATTERN_2_1 varchar2(127) := '[[:space:]]ON[[:space:]]*("?' || X_TRIGGER_OWNER ||
407 '"?)[.]?"?'|| ev_view(X_TABLE_NAME) ||'"?[[:space:]]';
408
409 -- Match#3 : Generic and
410 -- For SYNONYM: will be modified for each synonym before match.
411 L_REG_EXP_PATTERN_3 varchar2(127):= '[[:space:]]ON[[:space:]]*("?[A-Z_0-9]*"?)[.]?"?' ||
412 X_TABLE_NAME ||'"?[[:space:]]';
413
414 -- This is used to replace ALTER TRIGGER ... ENABLE/DISABLE statement
415 -- Bug 12820852 ( some triggers has $ in name)
416 L_ALTER_REG_PATTERN varchar2(127):= 'ALTER[[:space:]]*TRIGGER[[:space:]]*("?'
417 || X_TRIGGER_OWNER || '"?)[.]?"?' ||
418 replace(replace(X_TRIGGER_NAME, '$', '\$'), '+', '\+') ||
419 '"?[[:space:]]*[A-Z]*(;?)';
420
421 -- Match regular pattern will be replaced with following string [ ON APPS.<TABLE-SYNONYM> ]
422 -- Added new-line for bug-12800936
423 L_REG_EXP_REPLACE_STR varchar2(127):= local_chr(10) || ' ON "' || X_SYNONYM_OWNER || '"."'|| X_SYNONYM_NAME ||'" ';
424
425 -- NOTE : if there is any issue with below SQL
426 -- revert back to old one because this has to use only for
427 -- 'MTL_SYSTEM_ITEMS_B' table is not being replaced as
428 -- synonym is defined on APPS synonym.
429 --
430 -- Non-Apps synonyms
431 --
432 cursor C_TAB_SYN (P_TABLE_OWNER varchar2, P_TABLE_NAME varchar2) is
433 with p( owner, synonym_name) as
434 (
435 select owner, synonym_name
436 from dba_synonyms
437 where table_name= p_table_name
438 and table_owner = p_table_owner
439 union all
440 select s.owner , s.synonym_name
441 from dba_synonyms s,
442 p
443 where s.table_name = p.synonym_name
444 and s.table_owner = p.owner
445 and s.table_owner in
446 ( select oracle_username
447 from system.fnd_oracle_userid
448 where read_only_flag in ('A','B', 'E', /*'U',*/ 'C')
449 )
450 )
451 cycle owner, synonym_name set cyclemarker to 'Y' default 'N'
452 select distinct synonym_name from p where cyclemarker = 'N' ;
453
454 SUCEESS_WITH_COMPILE_ERR exception;
455 pragma exception_init(suceess_with_compile_err, -24344);
456 begin
457
458 log(c_module, 'PROCEDURE', 'begin: ' ||x_trigger_owner||'.'||x_trigger_name);
459 --
460 -- Bug 13597311. If trigger is in invalid state, then attempt to compile it
461 -- If compilation fails with hard ORA error like
462 -- ORA-25006: cannot specify this column in UPDATE OF clause
463 -- then abort moving the table name to synonym.
464 --
465 if (upper(x_valid_status) <> 'VALID') THEN
466 begin
467 alter_trigger(x_trigger_owner, x_trigger_name, 'COMPILE');
468 exception
469 when suceess_with_compile_err then
470 -- progress, assuming the compilation error will go away subsequently
471 null;
472 when others then
473 -- ORA-04045: errors during recompilation/revalidation of ...
474 -- ORA-25006: cannot specify this column in UPDATE OF clause
475 if((sqlcode = -25006) or
476 (sqlcode = -4045 and instr (sqlerrm, 'ORA-25006') > 0 ) ) then
477 -- disable the trigger and abandon the movement
478 log(c_module, 'ERROR', 'The trigger ' ||x_trigger_owner||'.'||x_trigger_name ||
479 ' is invalid and it could not be compiled. Autopatch will disable the trigger.' ||
480 ' You must not attempt to use the system before resolving the error with Oracle Support.' ||
481 ' Data consistency may be compromised if you start using the system before resolving the error.' ||
482 ' Error message follows.');
483 log(c_module, 'ERROR', sqlerrm);
484 -- Can NOT DISABLE a trigger which has ORA-25006 issue.
485 --alter_trigger(x_trigger_owner, x_trigger_name, 'DISABLE');
486 return;
487 end if;
488 -- In ALL other cases raise error, so AD_ZD_TABLE.UPGRADE call will be marked as FAILED,
489 -- and worker would consider in next attempt.
490 log(c_module, 'ERROR', x_trigger_owner||'.'||x_trigger_name || ': '|| sqlerrm);
491 raise;
492 end;
493 end if;
494
495 -- Get DDLs of this trigger.
496 l_ddl_stmts := fetch_multiple_ddls(x_trigger_owner,x_trigger_name, 'TRIGGER');
497 if(l_ddl_stmts is not null and l_ddl_stmts.count > 0 ) then
498 for i in 1 .. l_ddl_stmts.count loop
499 l_ddl_lob := l_ddl_stmts(i).ddltext;
500 -- Actual trigger definition will have "ON" keyword
501 if(i = 1) then
502 -- Match <tabe owner>.<table name>
503 l_index := regexp_instr(l_ddl_lob, l_reg_exp_pattern_1, 1,1,0, 'i' );
504 if(l_index > 0 ) then -- MATCH#1:
505 log(c_module, 'STATEMENT','Index->' || l_index || ' Found match of-> '|| l_reg_exp_pattern_1 );
506 -- Start search from that point, replace the owner.table-name with syn-owner.sysn-name
507 l_ddl_lob := regexp_replace(l_ddl_lob,
508 l_reg_exp_pattern_1,
509 l_reg_exp_replace_str,
510 l_index, 1, 'i' ); -- first occurrence only
511 else
512 -- MATCH#2: match <tabl owner>.<EV name>
513 l_index := regexp_instr(l_ddl_lob, l_reg_exp_pattern_2, 1,1,0, 'i' );
514 if(l_index > 0) then
515 log(c_module, 'STATEMENT',
516 'Found match of '||ev_view(X_TABLE_NAME)||' in trigger definition');
517 l_ddl_lob := regexp_replace(l_ddl_lob,
518 l_reg_exp_pattern_2,
519 l_reg_exp_replace_str, l_index, 1, 'i' );
520 else
521 -- MATCH#2_1: Match <Trigger owner>.<EV name>
522 l_index := regexp_instr(l_ddl_lob, l_reg_exp_pattern_2_1, 1,1,0, 'i' );
523 if(l_index > 0) then
524 log(c_module, 'STATEMENT', 'Found match of <trigger owner>.'
525 || ev_view(X_TABLE_NAME) ||' in trigger definition');
526 l_ddl_lob := regexp_replace(l_ddl_lob,
527 l_reg_exp_pattern_2_1,
528 l_reg_exp_replace_str, l_index, 1, 'i' );
529 else
530 log(c_module, 'STATEMENT',
531 'No match found so far and in last step of matching: ->' );
532 -- MATCH#3: Match with <generic owner name>.<table name>
533 l_index := regexp_instr(l_ddl_lob, l_reg_exp_pattern_3, 1,1,0, 'i' );
534 if( l_index > 0 ) then
535 log(c_module, 'STATEMENT', 'Match found at index : ->' || l_index );
536 l_ddl_lob := regexp_replace(l_ddl_lob,
537 l_reg_exp_pattern_3,
538 l_reg_exp_replace_str, l_index, 1, 'i' );
539 else
540 -- Check if trigger has been defined on any other SYNONYMs ( other than APPS synonym)
541 l_index := 0;
542 log(c_module, 'STATEMENT','Checking if it has been defined on a table-synonym' );
543 for syn_rec in c_tab_syn (x_table_owner, x_table_name) loop
544 l_synonym_name := syn_rec.synonym_name;
545 l_reg_exp_pattern_3 := '[[:space:]]ON[[:space:]]*("?[A-Z_0-9]*"?)[.]?"?' || l_synonym_name ||'"?[[:space:]]';
546 l_index := regexp_instr(l_ddl_lob, l_reg_exp_pattern_3, 1,1,0, 'i' );
547 exit when l_index > 0;
548 end loop;
549
550 if(l_index > 0 ) then
551 log(c_module, 'STATEMENT',
552 'Found match, Trigger has been defined on table-synonym : ->' ||
553 l_index || ' Pattern->' || l_reg_exp_pattern_3 );
554 l_ddl_lob := regexp_replace(l_ddl_lob,
555 l_reg_exp_pattern_3,
556 l_reg_exp_replace_str,
557 l_index, /* from that position */
558 1, /* first occurrence */
559 'i' /* case-insensitive */
560 );
561 end if;
562 end if; --- end of MATCH#3
563 end if; --- end of MATCH#2_1
564 end if; -- end of MATCH#2
565 end if; -- end of MATCH#1
566
567 -- Drop trigger
568 exec(x_sql => 'DROP TRIGGER "' || x_trigger_owner || '"."' || x_trigger_name || '"',
569 x_log_mod => c_module,
570 x_ignore => false);
571
572 end if; -- end of if [ i =1 ]
573
574 exec(x_sql => l_ddl_lob, x_log_mod => c_module, x_ignore => false);
575 end loop;
576 end if;
577
578 log(c_module, 'PROCEDURE', 'end');
579 exception
580 when others then
581 raise;
582 end MOVE_TRIGGER;
583
584
585 --
586 -- Move triggers from Table to EV.
587 --
588 -- So: [ TRIGGERS ] --> [ SYNONYM ] --> [ EV ]---> [ TABLE ]
589 --
590 -- Steps
591 -- 1- Drop Trigger
592 -- 2- Recreate Trigger with same definition on top of synonym (EV)
593 --
594 procedure MOVE_TRIGGERS(
595 X_TABLE_OWNER varchar2,
596 X_TABLE_NAME varchar2,
597 X_EV_NAME varchar2)
598 is
599 C_MODULE varchar2(127) := c_package||'move_triggers';
600 L_VALID_STATUS varchar2(10);
601 L_EV_NAME varchar2(30);
602 L_SYNONYM_NAME varchar2(30);
603 L_SYNONYM_OWNER varchar2(30);
604
605 -- triggers defined on a table
606 CURSOR c_trg is
607 select owner, trigger_name, trigger_type, status
608 from dba_triggers
609 where table_owner in
610 ( select oracle_username from system.fnd_oracle_userid
611 where read_only_flag in ('A','B', 'E', 'U', 'C') )
612 -- EXCLUDE: trigger generated by the Oracle Text Indexing
613 and trigger_name not like 'DR$%'
614 -- EXCLUDE: Editioned Data Storage Maintenance Trigger Name
615 and trigger_name <> ad_zd_seed.eds_trigger(x_table_name)
616 and trigger_name <> ad_zd_seed.eds_fcet(x_table_name)
617 -- EXCLUDE: cross edition triggers.
618 and crossedition = 'NO'
619 and table_owner = x_table_owner
620 and (
621 (table_name = x_table_name and base_object_type='TABLE')
622 or
623 (table_name=ev_view(x_table_name) and base_object_type='VIEW')
624 )
625 and owner in
626 (select oracle_username from system.fnd_oracle_userid
627 where read_only_flag in ('A','B', 'E', 'U', 'C') );
628
629 begin
630 log(C_MODULE, 'PROCEDURE', 'begin: '||x_table_owner||'.'||x_table_name);
631
632 l_ev_name := nvl(X_EV_NAME, ev_view(X_TABLE_NAME));
633
634 -- Get APPS synonym of that table or synonym pointing to table EV
635 begin
636 select syn.owner, syn.synonym_name
637 into l_synonym_owner, l_synonym_name
638 from dba_synonyms syn
639 where syn.owner = ad_zd.apps_schema
640 and syn.table_owner = x_table_owner
641 and syn.table_name = l_ev_name
642 and rownum < 2;
643 exception
644 when no_data_found then
645 -- internal error, should probably fail here
646 log(c_module, 'ERROR', 'APPS synonym not found for table '||x_table_owner||'.'||x_table_name);
647 l_synonym_owner := x_table_owner;
648 l_synonym_name := x_table_name;
649 end;
650
651 begin
652 for trg_rec in c_trg loop
653
654 -- Bug 13597311. Adding valid state to decide
655 -- whether to force recompile a trigger or not.
656 -- This can be also done in CURSOR c_trg by joining
657 -- dba_objects. At present there is a huge performance
658 -- drop in the resultant query. Taking suggestion from
659 -- perf team.
660 -- TODO: If perf team can suggest a single query, it would
661 -- help optimize further.
662
663 select status
664 into l_valid_status
665 from dba_objects
666 where owner = trg_rec.owner
667 and object_name = trg_rec.trigger_name
668 and object_type = 'TRIGGER';
669
670 move_trigger(
671 trg_rec.owner,
672 trg_rec.trigger_name,
673 trg_rec.status,
674 l_valid_status,
675 x_table_owner,
676 x_table_name,
677 l_synonym_owner,
678 l_synonym_name);
679 end loop;
680 exception
681 when others then
682 raise;
683 end;
684 log(C_MODULE, 'PROCEDURE', 'end');
685 END MOVE_TRIGGERS;
686
687 --
688 -- Moves VPD policies from table to EV
689 --
690 procedure MOVE_VPD_POLICIES(
691 X_TABLE_OWNER in varchar2,
692 X_TABLE_NAME in varchar2,
693 X_EV_NAME in varchar2 )
694 is
695 c_module varchar2(127) := c_package||'move_vpd_policies';
696
697 l_object_owner varchar2(30);
698 l_object_name varchar2(30);
699 l_policy_group varchar2(30);
700 l_policy_name varchar2(30);
701 l_pf_owner varchar2(30);
702 l_package varchar2(30);
703 l_function varchar2(65);
704 l_sel varchar2(3);
705 l_ins varchar2(3);
706 l_upd varchar2(3);
707 l_del varchar2(3);
708 l_idx varchar2(3);
709
710 l_policy_type varchar2(24);
711 l_chk_option varchar2(5) ;
712 l_enable varchar2(5);
713 l_static_policy varchar2(5);
714 l_long_predicate varchar2(5);
715
716
717 -- variables to store 'true' or 'false' boolean value as string
718 l_b_chk_option varchar2(5) ;
719 l_b_enable varchar2(5);
720 l_b_static_policy varchar2(5);
721 l_b_long_predicate varchar2(5);
722
723
724 l_stmt_types varchar2(64);
725 l_first BOOLEAN := true;
726 l_sql varchar2(32767) ;
727 l_ev_name varchar2(30);
728
729 cursor C_VPD (X_OWNER varchar2, X_NAME varchar2)
730 is
731 select object_owner ,
732 object_name ,
733 policy_group ,
734 policy_name ,
735 pf_owner ,
736 package ,
737 function ,
738 sel ,
739 ins ,
740 upd ,
741 del ,
742 idx ,
743 -- chk_option ,
744 decode(chk_option, 'YES', 'true', 'false'),
745 --enable ,
746 decode(enable, 'YES', 'true', 'false'),
747 --static_policy ,
748 decode(static_policy ,'YES', 'true', 'false'),
749 policy_type ,
750 --long_predicate
751 decode(long_predicate, 'YES', 'true', 'false')
752 from dba_policies
753 where object_owner = x_owner
754 and object_name = x_name
755 and policy_name = UPPER(policy_name) -- EXCLUDE: internal polciy, if any.
756 and lower(policy_name) <> 'ad_zd_seed'; -- EXCLUDE: AD_ZD_SEED policies .
757
758 begin
759 log(c_module, 'PROCEDURE', 'begin: '||x_table_owner||'.'||x_table_name);
760 begin
761 l_ev_name := nvl(X_EV_NAME, ev_view(X_TABLE_NAME));
762 open c_vpd(x_table_owner, x_table_name);
763 fetch c_vpd INTO
764 l_object_owner ,
765 l_object_name ,
766 l_policy_group ,
767 l_policy_name ,
768 l_pf_owner ,
769 l_package ,
770 l_function ,
771 l_sel ,
772 l_ins ,
773 l_upd ,
774 l_del ,
775 l_idx ,
776 l_chk_option ,
777 l_enable ,
778 l_static_policy ,
779 l_policy_type ,
780 l_long_predicate ;
781
782 while (c_vpd%found) loop
783 log(c_module, 'EVENT', 'Drop VPD policy: ' ||l_policy_name||' from '||x_table_owner||'.'||x_table_name );
784 -- If here, policy exist for this table
785 -- Drop from table
786 l_sql := 'BEGIN DBMS_RLS.DROP_POLICY ( ' ||
787 ' object_schema =>''' || X_TABLE_OWNER || ''', ' ||
788 ' object_name => ''' || X_TABLE_NAME || ''', ' ||
789 ' policy_name => ''' || l_policy_name || '''); END; ' ;
790 -- Drop policy from table
791 exec(l_sql, c_module);
792
793 -- SELECT
794 if( l_sel = 'YES' ) then
795 l_stmt_types := 'SELECT ';
796 l_first := false;
797 end if;
798
799 -- INSERT
800 if( l_ins = 'YES' ) then
801 if l_first then
802 l_stmt_types := 'INSERT';
803 l_first := false;
804 else
805 l_stmt_types := l_stmt_types || ', INSERT';
806 END if;
807 end if;
808
809 -- UPDATE
810 if( l_upd = 'YES' ) then
811 if l_first then
812 l_stmt_types := 'UPDATE';
813 l_first := false;
814 else
815 l_stmt_types := l_stmt_types || ', UPDATE';
816 end if;
817 end if;
818
819 -- DELETE
820 if( l_del = 'YES' ) then
821 if l_first then
822 l_stmt_types := 'DELETE';
823 l_first := false;
824 else
825 l_stmt_types := l_stmt_types || ', DELETE';
826 end if;
827 end if;
828
829 -- INDEX
830 if( l_idx = 'YES' ) then
831 if l_first then
832 l_stmt_types := 'INDEX';
833 l_first := false;
834 else
835 l_stmt_types := l_stmt_types || ', INDEX';
836 end if;
837 end if;
838
839 if(l_package is not null ) then
840 -- "pkg_name"."fun_name"
841 l_function := l_package || '.' || l_function ;
842 end if;
843
844 if(l_policy_type is not null ) then
845 l_policy_type := 'DBMS_RLS.' || l_policy_type;
846 end if;
847
848 -- ReAssign VPD to EV
849 l_sql := 'BEGIN DBMS_RLS.ADD_GROUPED_POLICY( ' ||
850 ' object_schema=>''' || x_table_owner || ''', ' ||
851 ' object_name=>''' || l_ev_name || ''', ' ||
852 ' policy_group=>''' || l_policy_group || ''', ' ||
853 ' policy_name=>''' || l_policy_name || ''', ' ||
854 ' function_schema=>''' || l_pf_owner || ''', ' ||
855 ' policy_function=>''' || l_function || ''', ' ||
856 ' statement_types=>''' || l_stmt_types || ''', ' || /* statement_types =>'SELECT,INDEX, INSERT, UPDATE, DELETE.*/
857 ' update_check=>' || l_chk_option || ', ' || /* BOOLEAN value, so extra single quote required otherwise that would become
858 string */
859 ' enable=>' || l_enable || ', ' || /* BOOLEAN */
860 ' static_policy=>' || l_static_policy || ', ' || /* BOOLEAN */
861 ' policy_type=>' || l_policy_type || ', ' || /* integer VALUE */
862 ' long_predicate=>' || l_long_predicate || ', ' || /* BOOLEAN */
863 ' sec_relevant_cols=>NULL, ' ||
864 ' sec_relevant_cols_opt=>NULL); END; ' ;
865
866 log(c_module, 'EVENT', 'Add VPD policy: ' ||l_policy_name||' to '||x_table_owner||'.'||l_ev_name );
867 exec(l_sql, c_module);
868
869 fetch c_vpd INTO
870 l_object_owner ,
871 l_object_name ,
872 l_policy_group ,
873 l_policy_name ,
874 l_pf_owner ,
875 l_package ,
876 l_function ,
877 l_sel ,
878 l_ins ,
879 l_upd ,
880 l_del ,
881 l_idx ,
882 l_chk_option ,
883 l_enable ,
884 l_static_policy ,
885 l_policy_type ,
886 l_long_predicate ;
887
888 end loop;
889
890 if c_vpd%isopen then -- cursor was not already closed
891 close c_vpd;
892 end if;
893
894 exception
895 when others then
896 if c_vpd%isopen then -- cursor was not already closed
897 close c_vpd;
898 end if;
899
900 log(c_module, 'ERROR', substr(sqlerrm, 1, 2000));
901 raise;
902 end;
903 log(c_module, 'PROCEDURE', 'end');
904 END MOVE_VPD_POLICIES ;
905
906 --
907 -- Drops public synonyms and re-create corresponding synonyms in dependent
908 -- schema.
909 --
910 procedure FIX_PUBLIC_SYNONYM (x_table_owner varchar2,
911 x_table_name varchar2,
912 x_synonym_name varchar2,
913 x_ev_name varchar2)
914 is
915 c_module varchar2(127) := c_package||'fix_public_synonym';
916 l_sql varchar2(1024);
917
918 -- MV tables already will be excluded by AD_ZD_TABLE.UPGRADE API
919 -- Check recursive dependency as well:
920 -- e.g: View --> synonym --> public-synonym
921 --
922 cursor c_dependents (p_synonym_name varchar2) is
923 select distinct d.owner --, d1.name, d1.type
924 from dba_dependencies d
925 where d.owner in ( select oracle_username
926 from system.fnd_oracle_userid
927 where read_only_flag in ('A','B', 'E', 'U', 'C' )
928 )
929 and d.referenced_type = 'SYNONYM'
930 and d.referenced_owner = 'PUBLIC'
931 and d.referenced_name = p_synonym_name
932 and not exists ( select 1
933 from dba_synonyms
934 where owner = d.owner
935 and synonym_name= p_synonym_name
936 and table_owner = x_table_owner
937 and table_name in (x_table_name, x_ev_name)
938 );
939
940 begin
941
942 log(c_module, 'PROCEDURE',
943 'begin: '||x_table_owner||'.'||x_table_name||', '||x_synonym_name);
944
945 for dependent in c_dependents ( x_synonym_name) loop
946
947 l_sql := 'CREATE SYNONYM "' || dependent.owner|| '"."' || x_synonym_name ||
948 '" FOR "'|| x_table_owner || '"."' || nvl(x_ev_name, ev_view(x_table_name)) || '"' ;
949
950 log(c_module, 'EVENT', 'Create Synonym: "' || dependent.owner || '"."' || x_synonym_name ||'"') ;
951 exec(l_sql, c_module, false);
952 end loop;
953
954 -- drop public synonym
955 l_sql := 'DROP PUBLIC SYNONYM "' || x_synonym_name || '" FORCE' ;
956 log(c_module, 'EVENT', 'Drop Public Synonym: "' || x_synonym_name ||'"');
957 exec (l_sql, c_module, true);
958
959 log(c_module, 'PROCEDURE', 'end');
960 end FIX_PUBLIC_SYNONYM;
961
962 --
963 -- Drops public synonyms and re-create corresponding synonyms in dependent
964 -- schema.
965 --
966 procedure FIX_PUBLIC_SYNONYMS(x_table_owner varchar2,
967 x_table_name varchar2,
968 x_ev_name varchar2)
969 is
970 c_module varchar2(127) := c_package||'fix_public_synonyms';
971
972 cursor c_pub_syn is
973 select syn.synonym_name
974 from dba_synonyms syn,
975 DBA_TABLES tab
976 where syn.owner='PUBLIC'
977 and syn.table_owner in ( select oracle_username
978 from system.fnd_oracle_userid
979 where read_only_flag in ('E', 'A', 'B', 'C')
980 )
981 and syn.table_owner = x_table_owner
982 and syn.table_name = x_table_name
983 and tab.owner = syn.table_owner
984 and tab.table_name = syn.table_name;
985
986
987 begin
988 log(c_module, 'PROCEDURE', 'begin: '||x_table_owner||'.'||x_table_name);
989
990 for pub_syn in c_pub_syn loop
991 fix_public_synonym(x_table_owner, x_table_name, pub_syn.synonym_name, x_ev_name);
992 end loop;
993
994 log(c_module, 'PROCEDURE', 'end');
995 end FIX_PUBLIC_SYNONYMS;
996
997
998 --
999 -- Generate Editioning View
1000 --
1001 -- Generates an Editioning View for the specified table.
1002 -- Table columns have names with the following structure
1003 --
1004 -- <logical_name>[#<version>]
1005 --
1006 -- The generated editioning view will map each logical column name
1007 -- to the latest version table column for that logical name.
1008 --
1009 procedure GENERATE_EV(
1010 X_TABLE_OWNER varchar2,
1011 X_TABLE_NAME varchar2 )
1012 is
1013 C_MODULE varchar2(127) := c_package||'generate_ev';
1014 L_EV_NAME varchar2(30);
1015 L_EV_STMT varchar2(32676);
1016 L_EV_LOB_STMT clob;
1017 L_EV_FIRST boolean;
1018
1019 cursor C_EV_COLUMNS(X_TABLE_OWNER varchar2, X_TABLE_NAME varchar2) is
1020 select
1021 ad_zd_table.ev_view_column(col.column_name) view_column_name
1022 , max(col.column_name) table_column_name
1023 , min(nvl(evc.view_column_id, 1000+col.column_id)) view_column_id
1024 from dba_tab_columns col,
1025 dba_editioning_view_cols evc
1026 where col.owner = x_table_owner
1027 and col.table_name = x_table_name
1028 and evc.owner(+) = col.owner
1029 and evc.view_name(+) = ad_zd_table.ev_view(col.table_name)
1030 and evc.view_column_name(+) = ad_zd_table.ev_view_column(col.column_name)
1031 group by ad_zd_table.ev_view_column(col.column_name)
1032 order by view_column_id;
1033
1034 begin
1035
1036 -- set up EV creation statement
1037 l_ev_name := ev_view(x_table_name);
1038 l_ev_stmt := 'create or replace editioning view "'||
1039 x_table_owner||'"."'||l_ev_name||'" as select ';
1040
1041 log(c_module, 'EVENT', 'Generate EV '||x_table_owner||'.'||l_ev_name);
1042
1043 begin
1044 -- Loop thru each EV column
1045 l_ev_first := true;
1046 for evcrec in c_ev_columns(x_table_owner, x_table_name) loop
1047
1048 -- add initial statement or separater as needed
1049 if l_ev_first then
1050 l_ev_first := false;
1051 dbms_lob.createtemporary (l_ev_lob_stmt, false, DBMS_LOB.CALL);
1052 dbms_lob.writeappend(lob_loc => l_ev_lob_stmt,
1053 amount => length(l_ev_stmt),
1054 buffer => l_ev_stmt);
1055 else
1056 l_ev_stmt := ', ';
1057 dbms_lob.writeappend(lob_loc => l_ev_lob_stmt,
1058 amount => length(l_ev_stmt),
1059 buffer => l_ev_stmt);
1060 end if;
1061
1062 -- add column mapping to EV creation statement
1063 l_ev_stmt := evcrec.table_column_name||' '||evcrec.view_column_name;
1064 dbms_lob.writeappend(lob_loc => l_ev_lob_stmt,
1065 amount => length(l_ev_stmt),
1066 buffer => l_ev_stmt);
1067
1068 end loop;
1069
1070 -- complete the EV creation statement and execute
1071 if (l_ev_lob_stmt is not null and dbms_lob.getlength(l_ev_lob_stmt) > 0 ) then
1072
1073 l_ev_stmt := ' from "'||x_table_owner||'"."'||x_table_name||'"';
1074 dbms_lob.writeappend(lob_loc => l_ev_lob_stmt,
1075 amount => length(l_ev_stmt),
1076 buffer => l_ev_stmt);
1077
1078 -- Immediate execute. [patching case]
1079 exec(l_ev_lob_stmt, c_module);
1080 end if; -- END : If l_ev_lob_stmt is not null and dbms_lob.getlength(l_ev_lob_stmt) > 0
1081
1082 if (dbms_lob.isTemporary(l_ev_lob_stmt)=1) then
1083 dbms_lob.freeTemporary(l_ev_lob_stmt);
1084 end if;
1085
1086 exception
1087 when others then
1088 if (dbms_lob.isTemporary(l_ev_lob_stmt)=1) then
1089 dbms_lob.freeTemporary(l_ev_lob_stmt);
1090 end if;
1091 log(c_module, 'ERROR',
1092 x_table_owner||'.'||x_table_name || ': ' || substr(sqlerrm, 1, 2000));
1093 raise;
1094 end;
1095
1096 end GENERATE_EV;
1097
1098
1099 --
1100 -- Install Editioning View
1101 --
1102 -- Only needed for first time installation of an EV.
1103 -- - moves table-level VPD policies to editioning view
1104 -- - copies table-level grants to editioning view
1105 -- - Points table synonyms to editioning view
1106 --
1107 -- Note: assumes EV is already generated
1108 --
1109 procedure INSTALL_EV(
1110 X_TABLE_OWNER varchar2,
1111 X_TABLE_NAME varchar2 )
1112 is
1113 C_MODULE varchar2(80) := c_package||'install_ev';
1114 C_APPS_SCHEMA varchar2(30) := ad_zd.apps_schema;
1115 L_EV_NAME varchar2(30);
1116 L_SYN_OWNER varchar2(30);
1117 L_SYN_NAME varchar2(30);
1118 L_STMT varchar2(32000);
1119 L_SYN_EXISTS boolean;
1120
1121 cursor C_SYNONYMS(x_table_owner varchar2, x_table_name varchar2, x_ev_name varchar2) is
1122 select
1123 syn.owner owner
1124 , syn.synonym_name synonym_name
1125 , syn.table_name table_name
1126 from dba_synonyms syn
1127 where syn.table_owner = x_table_owner
1128 and syn.table_name in (x_table_name, x_ev_name)
1129 and syn.owner in
1130 ( select oracle_username
1131 from system.fnd_oracle_userid
1132 where read_only_flag in ('A','B', 'E', 'U', 'C') );
1133
1134 begin
1135 log(c_module, 'PROCEDURE', 'begin: '||X_TABLE_OWNER||'.'||X_TABLE_NAME);
1136
1137 l_ev_name := ev_view(x_table_name);
1138
1139 -- loop through synonyms for table/EV
1140 l_syn_exists := false;
1141 for synrec in c_synonyms(x_table_owner, x_table_name, l_ev_name) loop
1142 l_syn_exists := true;
1143
1144 -- Point table synonyms to EV
1145 if synrec.table_name <> l_ev_name then
1146 l_stmt := 'create or replace synonym "'||synrec.owner||'"."'||
1147 synrec.synonym_name||'" for "'||x_table_owner||'"."' || l_ev_name ||'"' ;
1148
1149 log(c_module, 'EVENT',
1150 'Point Synonym "'||synrec.owner||'"."'||synrec.synonym_name||'" to EV');
1151 exec(l_stmt, c_module);
1152 end if;
1153 end loop;
1154
1155 -- Move VPD policies to EV
1156 move_vpd_policies(x_table_owner, x_table_name, l_ev_name);
1157 -- Copy Table Grants to EV
1158 copy_grants(x_table_owner, x_table_name, l_ev_name);
1159
1160 -- Create a synonym in APPS schema if there are none.
1161 -- This is for new table installation.
1162 if not l_syn_exists then
1163 l_stmt := 'create synonym "'||c_apps_schema|| '"."' ||x_table_name||
1164 '" for "'||x_table_owner||'"."'||l_ev_name||'"';
1165 log(c_module, 'EVENT',
1166 'Create Synonym '||c_apps_schema||'.'||x_table_name||' to EV');
1167 exec(l_stmt, c_module, true);
1168 end if;
1169
1170 log(c_module, 'PROCEDURE', 'end');
1171 end INSTALL_EV;
1172
1173
1174
1175
1176 --
1177 -- Upgrade Effectively Editioned Table
1178 -- - Generate and Install Editioning View
1179 -- - Fix table synonyms to point to EV
1180 -- - Move triggers to EV
1181 --
1182 procedure UPGRADE(
1183 X_TABLE_OWNER in varchar2,
1184 X_TABLE_NAME in varchar2 )
1185 is
1186 C_MODULE varchar2(80) := c_package||'upgrade';
1187 L_TABLE_OWNER varchar2(30);
1188 L_TABLE_NAME varchar2(30);
1189
1190 begin
1191 log(c_module, 'PROCEDURE', 'begin: '||x_table_owner||'.'||x_table_name);
1192
1193 -- Verify table exists
1194 begin
1195 select tab.owner, tab.table_name
1196 into l_table_owner, l_table_name
1197 from dba_tables tab
1198 where tab.owner = x_table_owner
1199 and tab.table_name = x_table_name;
1200 exception
1201 when no_data_found then
1202 error(c_module, 'Table '||nvl(x_table_owner,'<null>')||'.'||nvl(x_table_name,'<null>')||' does not exist.');
1203 end;
1204
1205 -- Generate Editioning View
1206 generate_ev(x_table_owner, x_table_name);
1207
1208 -- Install Editioning View (handles synonyms, VPD policies)
1209 install_ev(x_table_owner, x_table_name);
1210
1211 -- Move triggers to SYNONYM from table
1212 move_triggers(x_table_owner, x_table_name, ad_zd_table.ev_view(x_table_name));
1213
1214 -- cleanup PUBLIC synonyms
1215 fix_public_synonyms(x_table_owner, x_table_name, ad_zd_table.ev_view(x_table_name));
1216
1217 -- upgrade seed data tables
1218 if is_seed(x_table_owner, x_table_name) = 'Y' then
1219 ad_zd_seed.upgrade(x_table_name);
1220 end if;
1221
1222 commit;
1223 log(c_module, 'PROCEDURE', 'end '||x_table_owner ||'.'|| x_table_name);
1224 end UPGRADE;
1225
1226
1227
1228 --
1229 -- Upgrade all developer-managed EBS tables with an editioning view
1230 --
1231 -- Note: we maintain an explicit list of known application-managed
1232 -- tables that are excluded from upgrade (the regexp_like section).
1233 -- This application-managed table name patterns must stay in synch
1234 -- with what is documented in the Database Object Development .
1235 --
1236 procedure UPGRADE_DB
1237 is
1238
1239 C_MODULE varchar2(127) := c_package||'upgrade_db';
1240
1241 -- EBS Tables that need EVs
1242 -- - owned by EBS product schema
1243 -- - not a known DB internal table pattern
1244 -- - not a known application managed table pattern
1245 -- - not an AD internal table
1246 -- - not a Queue Table
1247 -- - not a Materialized View Container Table
1248 -- - has an APPS synonym
1249 cursor C_UPGRADE_TABLES is
1250 select
1251 tab.owner table_owner
1252 , tab.table_name table_name
1253 from dba_tables tab
1254 where tab.owner in
1255 ( select oracle_username from system.fnd_oracle_userid
1256 where read_only_flag in ('A','E','B') )
1257 and tab.temporary = 'N'
1258 and tab.secondary = 'N'
1259 /* not an application-managed dynamic table */
1260 and not regexp_like(tab.table_name, '^AQ\$', 'c')
1261 and not regexp_like(tab.table_name, '^AW\$', 'c')
1262 and not regexp_like(tab.table_name, '^MLOG\$', 'c')
1263 and not regexp_like(tab.table_name, '^BSC_DI_[0-9_]+$', 'c')
1264 and not regexp_like(tab.table_name, '^BSC_D_.+$', 'c')
1265 and not regexp_like(tab.table_name, '^FA_ARCHIVE_ADJUSTMENT_.+$', 'c')
1266 and not regexp_like(tab.table_name, '^FA_ARCHIVE_DETAIL_.+$', 'c')
1267 and not regexp_like(tab.table_name, '^FA_ARCHIVE_SUMMARY_.+$', 'c')
1268 and not regexp_like(tab.table_name, '^GL_DAILY_POST_INT_.+$', 'c')
1269 and not regexp_like(tab.table_name, '^GL_INTERCO_BSV_INT_[0-9]+$', 'c')
1270 and not regexp_like(tab.table_name, '^GL_MOVEMERGE_BAL_[0-9]+$', 'c')
1271 and not regexp_like(tab.table_name, '^GL_MOVEMERGE_INTERIM_[0-9]+$', 'c')
1272 and not regexp_like(tab.table_name, '^XLA_GLT_[0-9]+$', 'c')
1273 and not regexp_like(tab.table_name, '^ICX_POR_C[0-9]+.*$', 'c')
1274 and not regexp_like(tab.table_name, '^ICX_POR_UPLOAD_[0-9]+.*$', 'c')
1275 and not regexp_like(tab.table_name, '^IGI_SLS_[0-9]+$', 'c')
1276 and not regexp_like(tab.table_name, '^JTF_TAE_[0-9]+.*$', 'c')
1277 and not regexp_like(tab.table_name, '^JTY_[0-9]+_.*$', 'c')
1278 and not regexp_like(tab.table_name, '^ZPBDATA[0-9]+_EXCPT_T$', 'c')
1279 and not regexp_like(tab.table_name, '^ZX_DATA_UPLOAD_.*$', 'c')
1280 /* not an AD infrastructure table table */
1281 and tab.table_name not in
1282 ( 'AD_DEFERRED_JOBS',
1283 'AD_TABLE_INDEX_INFO',
1284 'FND_INSTALL_PROCESSES' )
1285 and not exists /* not a queue table */
1286 ( select qt.owner, qt.queue_table
1287 from dba_queue_tables qt
1288 where qt.owner = tab.owner
1289 and qt.queue_table = tab.table_name )
1290 and not exists /* not an MV container table */
1291 ( select mv.owner, mv.container_name
1292 from dba_mviews mv
1293 where mv.owner = tab.owner
1294 and mv.container_name = tab.table_name )
1295 and exists /* has apps synonym to base table */
1296 ( select syn.table_owner, syn.table_name
1297 from dba_synonyms syn
1298 where syn.table_owner = tab.owner
1299 and syn.table_name = tab.table_name
1300 and syn.owner = ad_zd.apps_schema )
1301 and not exists /* not an obsolete table */
1302 ( select
1303 fou.oracle_username owner
1304 , aoo.object_name object_name
1305 from
1306 system.fnd_oracle_userid fou
1307 , fnd_product_installations fpi
1308 , ad_obsolete_objects aoo
1309 where fpi.application_id = aoo.application_id
1310 and fou.oracle_id = fpi.oracle_id
1311 and fou.oracle_username = tab.owner
1312 and aoo.object_name = tab.table_name
1313 and aoo.object_type = 'TABLE' )
1314 order by tab.owner, tab.table_name;
1315
1316 -- EV Tables that are not fully upgraded
1317 -- - synonyms that point to base table
1318 -- - triggers on base table
1319 -- - TODO: vpd policies on base table
1320 cursor C_REUPGRADE_TABLES is
1321 select ev.owner table_owner, ev.table_name
1322 from dba_editioning_views ev
1323 where ev.owner in
1324 ( select oracle_username from system.fnd_oracle_userid
1325 where read_only_flag in ('A','E','B') )
1326 and ( exists /* uncoverted synonyms */
1327 ( select syn.synonym_name
1328 from dba_synonyms syn
1329 where syn.owner in
1330 ( select oracle_username from system.fnd_oracle_userid
1331 where read_only_flag in ('A', 'B', 'C', 'E', 'U') )
1332 and syn.table_owner = ev.owner
1333 and syn.table_name = ev.table_name ) or
1334 exists /* unmoved triggers */
1335 ( select trg.trigger_name
1336 from dba_triggers trg
1337 where trg.owner in
1338 ( select oracle_username from system.fnd_oracle_userid
1339 where read_only_flag in ('A', 'B', 'C', 'E', 'U') )
1340 and trg.trigger_name not like '%$%' /* system trigger */
1341 and trg.crossedition = 'NO'
1342 and trg.table_owner = ev.owner
1343 and trg.table_name = ev.table_name )
1344 )
1345 order by table_owner, table_name;
1346
1347 begin
1348 log(c_module, 'PROCEDURE', 'begin: no parameter(s)' );
1349
1350 -- Process tables that need upgrade
1351 for tab_rec in c_upgrade_tables loop
1352 log(c_module, 'STATEMENT', 'Store Upgrade action for table '||tab_rec.table_name );
1353 ad_zd_parallel_exec.load(
1354 x_phase => ad_zd_parallel_exec.c_phase_upgrade_table,
1355 x_sql => 'begin ad_zd_table.upgrade('''||tab_rec.table_owner ||''', '''|| tab_rec.table_name || '''); end;' ,
1356 x_unique => false );
1357 end loop;
1358
1359 -- Process tables that need re-upgrade.
1360 for tab_rec in c_reupgrade_tables loop
1361 log(c_module, 'STATEMENT', 'Store Re-Upgrade action for table '||tab_rec.table_name );
1362 ad_zd_parallel_exec.load(
1363 x_phase => ad_zd_parallel_exec.c_phase_upgrade_table,
1364 x_sql => 'begin ad_zd_table.upgrade('''||tab_rec.table_owner ||''', '''|| tab_rec.table_name || '''); end;' ,
1365 x_unique => false);
1366
1367 end loop;
1368
1369 log(c_module, 'PROCEDURE', 'end' );
1370
1371 end UPGRADE_DB;
1372
1373
1374 /*
1375 ** Downgrade Table (remove EV layer)
1376 **
1377 ** X_TABLE_OWNER / X_TABLE_NAME - the table
1378 ** TODO: move triggers and VPD policies back to Table
1379 */
1380 procedure DOWNGRADE(
1381 X_TABLE_OWNER in varchar2,
1382 X_TABLE_NAME in varchar2)
1383 is
1384 C_MODULE varchar2(80) := c_package||'downgrade';
1385 L_EV_NAME varchar2(30);
1386 L_TABLE_OWNER varchar2(30);
1387 L_TABLE_NAME varchar2(30);
1388
1389 cursor C_SYNONYMS(x_table_owner varchar2, x_table_name varchar2) is
1390 select syn.owner owner, syn.synonym_name name
1391 from dba_synonyms syn
1392 where syn.table_owner = x_table_owner
1393 and syn.table_name = ad_zd_table.ev_view(x_table_name)
1394 and syn.owner <> 'PUBLIC';
1395
1396 begin
1397 log( c_module, 'PROCEDURE', 'begin: '||x_table_owner||'.'||x_table_name);
1398
1399 -- Verify table exists
1400 begin
1401 select tab.owner, tab.table_name
1402 into l_table_owner, l_table_name
1403 from dba_tables tab
1404 where tab.owner = x_table_owner
1405 and tab.table_name = x_table_name;
1406 exception
1407 when no_data_found then
1408 error(c_module, 'Table '||nvl(x_table_owner,'<null>')||'.'||nvl(x_table_name,'<null>')||' does not exist.');
1409 end;
1410
1411 -- Change synonyms
1412 for synrec in c_synonyms(x_table_owner, x_table_name) loop
1413 log(c_module, 'EVENT',
1414 'Point Synonym back to Table: '||synrec.owner||'.'||synrec.name);
1415 exec('create or replace synonym "'||
1416 synrec.owner||'"."'||synrec.name||'" for "'||
1417 x_table_owner||'"."'||x_table_name||'"', c_module);
1418 end loop;
1419
1420 -- Drop EV
1421 l_ev_name := ad_zd_table.ev_view(x_table_name);
1422 log(c_module, 'EVENT', 'Drop EV '||x_table_owner||'.'||l_ev_name);
1423 exec('drop view "'||x_table_owner||'"."'||l_ev_name ||'"', c_module, true);
1424
1425 log( c_module, 'PROCEDURE', 'end');
1426 end DOWNGRADE;
1427
1428
1429 /*
1430 ** --------------------------------------------------------------------
1431 ** Forward Crossedition Trigger Tools
1432 ** --------------------------------------------------------------------
1433 */
1434
1435
1436 --
1437 -- Update table (fake update) in order to apply Crossedition Trigger.
1438 -- If X_EV_NAME is supplied, the update is executed on the EV instead of the table.
1439 -- This is used for seed data synchronization.
1440 --
1441 procedure UPDATE_4FCET(
1442 X_TABLE_OWNER varchar2,
1443 X_TABLE_NAME varchar2,
1444 X_TRIGGER_NAME varchar2,
1445 X_COLUMN_NAME varchar2,
1446 X_EV_NAME varchar2)
1447 is
1448 C_MODULE varchar2(80) := c_package || 'update_4fcet';
1449 L_STMT varchar2(1024);
1450 L_STATUS number;
1451 L_TASK_NAME varchar2(64);
1452 L_ERROR varchar2(32000);
1453
1454 DUPLICATE_TASK_ERROR exception;
1455 pragma exception_init(duplicate_task_error, -29497);
1456
1457 -- Conflict Triggers will block the apply of a forward crossedition trigger
1458 -- - directly on the table
1459 -- - owned by other than the current user
1460 cursor C_CONFLICT_TRIGGERS(X_TABLE_OWNER varchar2, X_TABLE_NAME varchar2) is
1461 select
1462 trg.owner owner
1463 , trg.trigger_name trigger_name
1464 , trg.status status
1465 from
1466 dba_triggers trg
1467 where trg.owner <> user
1468 and trg.table_owner = x_table_owner
1469 and trg.table_name = x_table_name
1470 order by 1, 2;
1471 begin
1472 -- disable any conflict triggers (workaround for DB Bug 13951889)
1473 -- TODO: remember explicit list of disabled triggers, only reenable those
1474 for crec in c_conflict_triggers(x_table_owner, x_table_name) loop
1475 if (crec.status = 'ENABLED') then
1476 log(c_module, 'STATEMENT', 'Disable conflict trigger '||crec.owner||'.'||crec.trigger_name);
1477 exec('alter trigger "'||crec.owner||'"."'||crec.trigger_name||'" disable', c_module);
1478 end if;
1479 end loop;
1480
1481 -- For large table, do parallel update
1482 l_stmt := 'update /*+ rowid (tbl) */ '||
1483 x_table_owner||'.'||nvl(x_ev_name, x_table_name)||' tbl set '||
1484 x_column_name||'='||x_column_name||' where rowid between :start_id and :end_id ';
1485
1486 log(c_module, 'STATEMENT', 'Parallel Update for '||x_trigger_name);
1487 log(c_module, 'STATEMENT', 'SQL: '||l_stmt);
1488
1489 -- create task, handle possible conflicting task
1490 l_task_name := x_trigger_name;
1491 begin
1492 log(c_module, 'STATEMENT', 'Create task: '||l_task_name);
1493 dbms_parallel_execute.create_task(l_task_name);
1494 exception
1495 when duplicate_task_error then
1496 log(c_module, 'STATEMENT', 'Drop duplicate task: '||l_task_name);
1497 dbms_parallel_execute.drop_task(l_task_name);
1498 log(c_module, 'STATEMENT', 'Create task (retry): '||l_task_name);
1499 dbms_parallel_execute.create_task(l_task_name);
1500 end;
1501
1502 dbms_parallel_execute.create_chunks_by_rowid(
1503 l_task_name, x_table_owner, x_table_name, true, 10000);
1504
1505 -- 'apply_crossedition_trigger' parameter is required to make cross edition trigger fire
1506 -- 'parallel_level' of null uses default parallelism.
1507 dbms_parallel_execute.run_task(
1508 l_task_name, l_stmt, dbms_sql.native,
1509 apply_crossedition_trigger=>'"'||x_trigger_name ||'"', parallel_level=>null);
1510
1511 -- If any error, Try again
1512 l_status := dbms_parallel_execute.task_status(l_task_name);
1513 if (l_status = dbms_parallel_execute.processed_with_error or
1514 l_status = dbms_parallel_execute.crashed ) then
1515 log(c_module, 'WARNING', 'Update failed, status='||to_char(l_status)||', Retrying...');
1516 dbms_parallel_execute.resume_task(l_task_name);
1517 end if;
1518
1519 -- If still error, report problem
1520 l_status := dbms_parallel_execute.task_status(l_task_name);
1521 if (l_status = dbms_parallel_execute.processed_with_error or
1522 l_status = dbms_parallel_execute.crashed ) then
1523 error(c_module, 'Update failed, status = '||to_char(l_status));
1524 end if;
1525
1526 log(c_module, 'STATEMENT', 'Successful Parallel Update, dropping task: '||l_task_name);
1527 dbms_parallel_execute.drop_task(l_task_name);
1528
1529 -- re-enable conflict triggers
1530 -- TODO: Enable only those triggers which were DISABLED by this API.
1531 for crec in c_conflict_triggers(x_table_owner, x_table_name) loop
1532 if (crec.status = 'DISABLED') then
1533 log(c_module, 'STATEMENT', 'Re-enable conflict trigger '||crec.owner||'.'||crec.trigger_name);
1534 exec('alter trigger "'||crec.owner||'"."'||crec.trigger_name||'" enable', c_module, true);
1535 end if;
1536 end loop;
1537
1538 exception
1539 when others then
1540 l_error := sqlerrm;
1541 begin
1542 dbms_parallel_execute.drop_task(l_task_name);
1543 exception
1544 when others then null;
1545 end;
1546 error(c_module, l_error);
1547 end update_4fcet;
1548
1549 --
1550 -- Apply Crossedition Trigger
1551 --
1552 -- Enables crossedition trigger, then applies to table
1553 -- If the trigger "follows" other triggers or non-existant
1554 -- triggers, this procedure will recursively enable or create
1555 -- the referenced triggers first, as needed.
1556 --
1557 -- Note: we expect the trigger to be created already.
1558 --
1559 -- Multilple crossedition triggers may be applied on the same table. For
1560 -- performance purposes, only the APPLY call for the last trigger will
1561 -- will be executed, which will fire all triggers on the table in a single
1562 -- pass update.
1563 --
1564 procedure APPLY(X_CET_NAME in varchar2)
1565 is
1566 C_MODULE varchar2(80) := c_package||'apply';
1567 C_CET_OWNER varchar2(30) := ad_zd.apps_schema;
1568 L_STATUS varchar2(8);
1569 L_CROSSEDITION varchar2(8);
1570 L_TABLE_OWNER varchar2(30);
1571 L_TABLE_NAME varchar2(30);
1572 L_UPD_COLUMN varchar2(30);
1573 L_UPD_EV_NAME varchar2(30);
1574 L_UPD_CET_NAME varchar2(30);
1575 L_STMT varchar2(1000);
1576 L_ERROR varchar2(2000);
1577 L_EXISTS varchar2(1);
1578 L_SCN number := null;
1579
1580 -- predecessor triggers must fire before the trigger to be applied
1581 cursor C_PREDECESSORS(X_OWNER varchar2, X_NAME varchar2) is
1582 select
1583 tord.referenced_trigger_owner ref_trg_owner
1584 , tord.referenced_trigger_name ref_trg_name
1585 , rtrg.status trg_status
1586 from
1587 dba_trigger_ordering tord
1588 , dba_triggers rtrg
1589 where tord.trigger_owner = x_owner
1590 and tord.trigger_name = x_name
1591 and tord.ordering_type = 'FOLLOWS'
1592 and rtrg.owner(+) = tord.referenced_trigger_owner
1593 and rtrg.trigger_name(+) = tord.referenced_trigger_name
1594 order by 1, 2;
1595
1596 cursor C_PREDECESSOR_CHAIN(X_OWNER varchar2, X_NAME varchar2) is
1597 select
1598 tord.referenced_trigger_owner ref_trg_owner
1599 , tord.referenced_trigger_name ref_trg_name
1600 from
1601 dba_trigger_ordering tord
1602 start with
1603 tord.trigger_owner = x_owner
1604 and tord.trigger_name = x_name
1605 and tord.ordering_type = 'FOLLOWS'
1606 connect by
1607 tord.trigger_owner = prior tord.referenced_trigger_owner
1608 and tord.trigger_name = prior tord.referenced_trigger_name
1609 and tord.ordering_type = 'FOLLOWS';
1610
1611 begin
1612 log(c_module, 'PROCEDURE', 'begin: '||x_cet_name);
1613
1614 -- Get CET info
1615 begin
1616 select trg.status, trg.crossedition, trg.table_owner, trg.table_name
1617 into l_status, l_crossedition, l_table_owner, l_table_name
1618 from dba_triggers trg
1619 where trg.crossedition in ('FORWARD', 'REVERSE')
1620 and trg.owner = c_cet_owner
1621 and trg.trigger_name = x_cet_name;
1622 exception
1623 when no_data_found then
1624 error(c_module, 'Crossedition Trigger '||c_cet_owner||'.'||x_cet_name||' does not exist');
1625 end;
1626
1627 -- If CET is already enabled, we are done
1628 if l_status = 'ENABLED' then
1629 log(c_module, 'PROCEDURE', 'end - noop');
1630 return;
1631 end if;
1632
1633 log(c_module, 'EVENT', 'Apply Crossedition Trigger: '||c_cet_owner||'.'||x_cet_name);
1634
1635 -- check for missing/disabled predecessor triggers
1636 -- TODO: what about RCET "PRECEDES" ordering_type?
1637 for prerec in c_predecessors(c_cet_owner, x_cet_name) loop
1638 if prerec.trg_status is NULL then
1639
1640 -- missing predecessor trigger, create stub trigger to satisfy dependency
1641 log(c_module, 'STATEMENT', 'Creating Stub CET: '|| c_cet_owner||'.'||prerec.ref_trg_name);
1642
1643 -- use first column of base table as triggering column for stub trigger
1644 -- this is to minimize uncessary firing of the stub
1645 select col.column_name into l_upd_column
1646 from dba_tab_columns col
1647 where col.owner = l_table_owner
1648 and col.table_name = l_table_name
1649 and col.column_id = 1;
1650
1651 -- create stub trigger, enabled
1652 l_stmt :=
1653 'create trigger "'||prerec.ref_trg_owner||'"."'||prerec.ref_trg_name||
1654 '" before insert or update of '||l_upd_column||' on "'||
1655 l_table_owner||'"."'||l_table_name||
1656 '" for each row '||l_crossedition||' crossedition begin null; end; ';
1657
1658 exec(l_stmt, c_module);
1659
1660 elsif prerec.trg_status = 'DISABLED' then
1661
1662 -- disabled predecessor, apply it (recurses to first trigger)
1663 apply(prerec.ref_trg_name);
1664
1665 end if;
1666 end loop;
1667
1668 -- TODO: check for triggering columns, these are NOT supported
1669 -- because column assignments from preceding triggers do not count as
1670 -- triggering column updates for following triggers.
1671
1672 -- Compile/Enable CET
1673 -- TODO: slight risk this is done already by a parallel thread, should check
1674 exec('alter trigger "'||c_cet_owner||'"."'||x_cet_name||'" compile', c_module);
1675 exec('alter trigger "'||c_cet_owner||'"."'||x_cet_name||'" enable', c_module);
1676
1677 -- If this is a reverse crossedition trigger, we are done.
1678 if l_crossedition = 'REVERSE' then
1679 log(c_module, 'PROCEDURE', 'end - reverse');
1680 return;
1681 end if;
1682
1683 -- If this is not the final trigger in FCET firing order, we are done.
1684 begin
1685 select 'Y' into l_exists from dual where exists
1686 ( select tord.trigger_name
1687 from dba_trigger_ordering tord
1688 where tord.referenced_trigger_owner = c_cet_owner
1689 and tord.referenced_trigger_name = x_cet_name
1690 and tord.ordering_type = 'FOLLOWS' );
1691
1692 log(c_module, 'PROCEDURE', 'end: '|| c_cet_owner || '.' || x_cet_name || ' - update deferred');
1693 return;
1694 exception
1695 when no_data_found then null;
1696 end;
1697
1698 -- At this point we are applying the final trigger of the FCET firing chain
1699 -- Execute the table update to apply all FCET triggers in the firing chain
1700
1701 -- Wait on pending DML so that update will not be overwritten by stale data
1702 log(c_module, 'STATEMENT', 'Waiting on pending DML for '||l_table_owner||'.'||l_table_name);
1703 if not dbms_utility.wait_on_pending_dml(l_table_owner||'.'||l_table_name, null, l_scn) then
1704 exec('alter trigger "'||c_cet_owner||'"."'||x_cet_name||'" disable', c_module);
1705 error(c_module, 'Pending transactions block apply of '||x_cet_name);
1706 end if;
1707
1708 -- Get Update Column
1709 -- Any column will do, but should be least-indexed and smallest column for best performance
1710 if (x_cet_name = ad_zd_seed.eds_fcet(l_table_name)) then
1711
1712 -- for seed data sync FCET, update least indexed smallest column in EV
1713 l_upd_ev_name := ad_zd_table.ev_view(l_table_name);
1714 select x.view_column_name into l_upd_column from
1715 ( select evc.view_column_name, count(idc.index_name), col.data_length
1716 from dba_tab_columns col, dba_ind_columns idc, dba_editioning_view_cols evc
1717 where col.owner = l_table_owner
1718 and col.table_name = l_table_name
1719 and evc.owner = col.owner
1720 and evc.view_name = l_upd_ev_name
1721 and evc.table_column_name = col.column_name
1722 and idc.table_owner(+) = col.owner
1723 and idc.table_name(+) = col.table_name
1724 and idc.column_name(+) = col.column_name
1725 group by evc.view_column_name, col.data_length
1726 order by count(idc.index_name), col.data_length ) x
1727 where rownum = 1;
1728
1729 else
1730
1731 -- for ordinary FCET, update least indexed smallest column in base table
1732 l_upd_ev_name := NULL;
1733 select x.column_name into l_upd_column from
1734 ( select col.column_name, count(idc.index_name), col.data_length
1735 from dba_tab_columns col, dba_ind_columns idc
1736 where col.owner = l_table_owner
1737 and col.table_name = l_table_name
1738 and idc.table_owner(+) = col.owner
1739 and idc.table_name(+) = col.table_name
1740 and idc.column_name(+) = col.column_name
1741 group by col.column_name, col.data_length
1742 order by count(idc.index_name), col.data_length ) x
1743 where rownum = 1;
1744
1745 end if;
1746
1747 -- Get Update FCET
1748 -- First in FCET in trigger firing order, will be last returned from this query
1749 l_upd_cet_name := x_cet_name;
1750 for cetrec in c_predecessor_chain(c_cet_owner, x_cet_name) loop
1751 l_upd_cet_name := cetrec.ref_trg_name;
1752 end loop;
1753
1754 -- update table
1755 begin
1756 ad_zd_table.update_4fcet(l_table_owner, l_table_name, l_upd_cet_name, l_upd_column, l_upd_ev_name);
1757 exception
1758 when others then
1759 l_error := substrb(sqlerrm, 1, 2000);
1760 -- disable the trigger
1761 exec('alter trigger "'||c_cet_owner||'"."'||x_cet_name||'" disable', c_module);
1762 error(c_module, 'Could not apply Crossedition Trigger "'||
1763 c_cet_owner||'"."'||x_cet_name||'": '||l_error);
1764 end;
1765
1766 log(c_module, 'PROCEDURE', 'end - update');
1767 end APPLY;
1768
1769
1770
1771
1772 /*===================================================================================
1773 **
1774 ** Index Tools
1775 **
1776 ** ===================================================================================
1777 */
1778
1779 -- Revised Index Name from Original Index Name
1780 function REVISED_INDEX_NAME(X_ORIGINAL_INDEX_NAME in varchar2) return varchar2 is
1781 begin
1782 return translate(x_original_index_name, '_', '~');
1783 end REVISED_INDEX_NAME;
1784
1785 -- Regular expression for Revised Index Names
1786 function REVISED_INDEX_REGEXP return varchar2 is
1787 begin
1788 return '^[A-Z][0-9A-Z~$]*~[0-9A-Z$]*$';
1789 end REVISED_INDEX_REGEXP;
1790
1791 -- Original Index Name from Revised Index Name
1792 function ORIGINAL_INDEX_NAME(X_REVISED_INDEX_NAME in varchar2) return varchar2 is
1793 begin
1794 return translate(x_revised_index_name, '~', '_');
1795 end ORIGINAL_INDEX_NAME;
1796
1797 -- Regular expression for Original Index Names
1798 function ORIGINAL_INDEX_REGEXP return varchar2 is
1799 begin
1800 return '^[A-Z][0-9A-Z_$]*_[0-9A-Z$]*$';
1801 end ORIGINAL_INDEX_REGEXP;
1802
1803
1804 /*
1805 ** Revise Index
1806 **
1807 ** Generates a revised index using the latest revised columns:
1808 ** 1) query the columns for the original index
1809 ** 2) use editioning view to map logical columns to new columns
1810 ** 3) create new index using new columns
1811 **
1812 ** TODO: Constraints
1813 */
1814 procedure REVISE_INDEX(
1815 X_INDEX_OWNER in varchar2,
1816 X_INDEX_NAME in varchar2)
1817 is
1818 C_MODULE varchar2(80) := c_package||'revise_index';
1819 L_REVISED_NAME varchar2(30);
1820 L_TABLE_OWNER varchar2(30);
1821 L_TABLE_NAME varchar2(30);
1822 L_PRE_STMT varchar2(1000);
1823 L_COL_STMT varchar2(30000);
1824 L_POST_STMT varchar2(1000);
1825 L_NEW_COLUMN varchar2(4000);
1826 L_FIRST boolean;
1827
1828 cursor C_INDEX_COLUMNS(x_owner varchar2, x_name varchar2) is
1829 select idc.column_name
1830 from dba_ind_columns idc
1831 where idc.index_owner = x_owner
1832 and idc.index_name = x_name
1833 order by column_position;
1834 begin
1835 log(c_module, 'PROCEDURE', 'begin: '||x_index_owner||'.'||x_index_name);
1836
1837 -- drop existing revised index, if any
1838 begin
1839 select idx.index_name
1840 into l_revised_name
1841 from dba_indexes idx
1842 where idx.owner = x_index_owner
1843 and idx.index_name = ad_zd_table.revised_index_name(x_index_name);
1844
1845 exec('drop index "'||x_index_owner||'"."'||l_revised_name||'"', c_module);
1846 exception
1847 when no_data_found then
1848 null;
1849 end;
1850
1851 -- generate pre/post statement
1852 -- TODO: use DBMS_METADATA
1853 select table_owner, table_name,
1854 'create '||
1855 decode(index_type,
1856 'NORMAL', decode(uniqueness, 'UNIQUE', 'UNIQUE', ''),
1857 index_type)||
1858 ' index "'||owner||'"."'||ad_zd_table.revised_index_name(index_name)||'"'||
1859 ' on "'||table_owner||'"."'||table_name||'"' I_HEADER,
1860 decode(nvl(tablespace_name,'???'),'???','','tablespace '||tablespace_name)||
1861 ' storage (initial '||nvl(initial_extent, 128*1024)/1024||'K '||
1862 'next '||nvl(next_extent, 128*1024)/1024||'K)'
1863 into l_table_owner, l_table_name, l_pre_stmt, l_post_stmt
1864 from dba_indexes
1865 where owner = x_index_owner
1866 and index_name = x_index_name;
1867
1868 -- prepare column clause
1869 l_col_stmt := ' (';
1870 l_first := TRUE;
1871 -- for each old index column...
1872 for icolrec in c_index_columns(x_index_owner, x_index_name) loop
1873 -- get new revised column name
1874 if icolrec.column_name = 'ZD_EDITION_NAME' then
1875 l_new_column := icolrec.column_name;
1876 else
1877 l_new_column :=
1878 ev_table_column(l_table_owner, ev_view(l_table_name), ev_view_column(icolrec.column_name));
1879 end if;
1880
1881 -- add new column to index creation statement
1882 if l_first then
1883 l_first := FALSE;
1884 else
1885 l_col_stmt := l_col_stmt||', ';
1886 end if;
1887 l_col_stmt := l_col_stmt||l_new_column;
1888 end loop;
1889 l_col_stmt := l_col_stmt||') ';
1890
1891 -- Create Revised Index
1892 -- try ONLINE first, then regular mode.
1893 begin
1894 log(c_module, 'EVENT', 'Revise Index: '||x_index_name);
1895
1896 exec(l_pre_stmt||l_col_stmt||l_post_stmt||' online', c_module);
1897 exception
1898 when others then
1899 -- maximum key length exceeded error?
1900 -- try to build the index without the ONLINE keyword
1901 if sqlcode = -1450 then
1902 exec(l_pre_stmt || l_col_stmt || l_post_stmt, c_module);
1903 else
1904 raise;
1905 end if;
1906 end;
1907
1908 log(c_module, 'PROCEDURE', 'end');
1909
1910
1911 end REVISE_INDEX;
1912
1913
1914 /*
1915 ** Revise Indexes for Table
1916 **
1917 ** Locates each out-of-date index for a table, and revises that index
1918 **
1919 ** TODO: Index Orgainized Talbes
1920 */
1921 procedure REVISE_INDEXES(
1922 X_TABLE_OWNER in varchar2,
1923 X_TABLE_NAME in varchar2)
1924 is
1925 C_MODULE varchar2(80) := c_package||'revise_indexes';
1926 L_INDEX_OWNER varchar2(30);
1927 L_INDEX_NAME varchar2(30);
1928
1929 -- Out Of Date indexes for a table
1930 -- A) Original Index with out of date columns and no Revised Index
1931 -- B) Revised Index with out of date columns
1932 cursor c_ood_indexes(x_table_owner varchar2, x_table_name varchar2) is
1933 select idx.owner, idx.index_name, idx.index_type, idx.partitioned
1934 from dba_indexes idx
1935 where idx.owner in
1936 ( select oracle_username from system.fnd_oracle_userid
1937 where read_only_flag in ('A', 'B', 'E', 'U') )
1938 and idx.owner = idx.table_owner
1939 and idx.table_owner = x_table_owner
1940 and idx.table_name = x_table_name
1941 and regexp_like(idx.index_name, ad_zd_table.original_index_regexp, 'c')
1942 and (
1943 (
1944 /* Revised Index does not exist and Original Index is out of date */
1945 not exists
1946 ( select idt.index_name
1947 from dba_indexes idt
1948 where idt.owner = idx.owner
1949 and idt.index_name = ad_zd_table.revised_index_name(idx.index_name) )
1950 and exists
1951 ( select col.column_name
1952 from dba_tab_columns col, dba_ind_columns idc
1953 where col.owner = idx.table_owner
1954 and col.table_name = idx.table_name
1955 and idc.index_owner = idx.owner
1956 and idc.index_name = idx.index_name
1957 and ad_zd_table.ev_view_column(idc.column_name)=ad_zd_table.ev_view_column(col.column_name)
1958 and col.column_name > idc.column_name )
1959 )
1960 or
1961 (
1962 /* Revised Index exists, but is out of date */
1963 exists
1964 ( select idt.index_name
1965 from dba_indexes idt
1966 where idt.owner = idx.owner
1967 and idt.index_name = ad_zd_table.revised_index_name(idx.index_name) )
1968 and exists
1969 ( select col.column_name
1970 from dba_tab_columns col, dba_ind_columns idc
1971 where col.owner = idx.table_owner
1972 and col.table_name = idx.table_name
1973 and idc.index_owner = idx.owner
1974 and idc.index_name = ad_zd_table.revised_index_name(idx.index_name)
1975 and ad_zd_table.ev_view_column(idc.column_name)=ad_zd_table.ev_view_column(col.column_name)
1976 and col.column_name > idc.column_name )
1977 )
1978 )
1979 order by 1, 2;
1980
1981 begin
1982 log(c_module, 'PROCEDURE',
1983 'begin: '||nvl(x_table_owner, 'NULL')||'.'||nvl(x_table_name, 'NULL'));
1984
1985 -- revise each out-of-date index for the table
1986 for idxrec in c_ood_indexes(x_table_owner, x_table_name) loop
1987 if idxrec.index_type = 'IOT - TOP' then
1988 error(c_module, 'Automatic Revision of Index Organized Table not yet implemented: '||idxrec.owner||'.'||idxrec.index_name);
1989 elsif idxrec.index_type = 'FUNCTION-BASED NORMAL' then
1990 error(c_module, 'Automatic Revision of Function-based Index not yet implemented: '||idxrec.owner||'.'||idxrec.index_name);
1991 elsif idxrec.index_type = 'DOMAIN' then
1992 error(c_module, 'Automatic Revision of Domain Index not yet implemented: '||idxrec.owner||'.'||idxrec.index_name);
1993 elsif idxrec.partitioned = 'YES' then
1994 error(c_module, 'Automatic Revision of Partitioned Index not yet implemented: '||idxrec.owner||'.'||idxrec.index_name);
1995 else
1996 revise_index(idxrec.owner, idxrec.index_name);
1997 end if;
1998 end loop;
1999
2000 log(c_module, 'PROCEDURE', 'end');
2001 end REVISE_INDEXES;
2002
2003
2004 /*
2005 ** Cutover to revised Indexes for given table
2006 **
2007 ** X_TABLE_OWNER - table owner filter (match any owner if NULL)
2008 ** X_TABLE_NAME - table name filter (match any table if NULL)
2009 **
2010 ** TODO: Constraints
2011 */
2012 procedure CUTOVER_INDEXES(
2013 X_TABLE_OWNER in varchar2,
2014 X_TABLE_NAME in varchar2)
2015 is
2016 C_MODULE varchar2(80) := c_package||'cutover_indexes';
2017 -- revised indexes
2018 cursor C_REVISED_INDEXES(X_TABLE_OWNER varchar2, X_TABLE_NAME varchar2) is
2019 select
2020 ridx.owner owner
2021 , ridx.index_name revised_index
2022 , ridx.table_owner table_owner
2023 , ridx.table_name table_name
2024 , oidx.index_name original_index
2025 , con.constraint_name constraint_name
2026 from
2027 dba_indexes ridx
2028 , dba_indexes oidx
2029 , dba_constraints con
2030 where ridx.owner in
2031 ( select oracle_username from system.fnd_oracle_userid
2032 where read_only_flag in ('A', 'B', 'E', 'U') )
2033 and ridx.table_owner = x_table_owner
2034 and ridx.table_name = x_table_name
2035 and regexp_like(ridx.index_name, ad_zd_table.revised_index_regexp, 'c')
2036 and oidx.owner(+) = ridx.owner
2037 and oidx.index_name(+) = ad_zd_table.original_index_name(ridx.index_name)
2038 and con.owner(+) = ridx.table_owner
2039 and con.table_name(+) = ridx.table_name
2040 and con.index_owner(+) = ridx.owner
2041 and con.index_name(+) = ad_zd_table.original_index_name(ridx.index_name)
2042 order by 1, 2;
2043
2044 begin
2045 log(c_module, 'PROCEDURE',
2046 'begin: '||nvl(x_table_owner,'NULL')||'.'||nvl(x_table_name,'NULL'));
2047
2048 -- for each new/revised index
2049 for idxrec in c_revised_indexes(x_table_owner, x_table_name) loop
2050
2051 log( c_module,'EVENT', 'Cutover index: '||idxrec.owner||'.'||idxrec.revised_index);
2052
2053 -- drop unique constraint or index, if it exists
2054 if idxrec.constraint_name is not null then
2055 exec('alter table "'||idxrec.table_owner||'"."'||idxrec.table_name||
2056 '" drop constraint "'||idxrec.constraint_name||'" cascade keep index', c_module);
2057 end if;
2058 if idxrec.original_index is not null then
2059 exec('drop index "'||idxrec.owner||'"."'||idxrec.original_index||'"', c_module);
2060 end if;
2061
2062 -- rename the revised index to the original name
2063 exec('alter index "'||idxrec.owner||'"."'||idxrec.revised_index||'" '||
2064 'rename to "'||original_index_name(idxrec.revised_index)||'"', c_module);
2065
2066 end loop;
2067
2068 log( c_module, 'PROCEDURE', 'end');
2069 end CUTOVER_INDEXES;
2070
2071
2072
2073
2074 /*
2075 ** --------------------------------------------------------------------
2076 ** Event Interfaces
2077 ** --------------------------------------------------------------------
2078 */
2079
2080
2081 /*
2082 ** Patch Table
2083 **
2084 ** X_TABLE_OWNER - table owner
2085 ** X_TABLE_NAME - table name
2086 **
2087 ** This procedure must be called after an existing table is patched with
2088 ** - Add column
2089 ** - Create index
2090 */
2091 procedure PATCH(
2092 X_TABLE_OWNER in varchar2,
2093 X_TABLE_NAME in varchar2)
2094 is
2095 C_MODULE varchar2(80) := c_package||'patch';
2096
2097 begin
2098 log(c_module,'EVENT', 'Patch Table: '||x_table_owner||'.'||x_table_name);
2099
2100 -- If there is an EV then do related processing
2101 if ad_zd_table.ev_exists(x_table_owner, x_table_name) = 'Y' then
2102
2103 -- Cannot regenerate EV from RUN edition if PATCH edition exists
2104 if ad_zd.get_edition_type = 'RUN' and ad_zd.get_edition('PATCH') is not null then
2105 error(c_module, 'Cannot PATCH table from Run Edition while Patch Edition exists');
2106 end if;
2107
2108 -- Generate revised editioning view
2109 generate_ev(x_table_owner, x_table_name);
2110
2111 -- For seed data tables, call patch processing in seed data manager
2112 if is_seed(x_table_owner, x_table_name) = 'Y' then
2113 ad_zd_seed.patch(x_table_owner, x_table_name);
2114 end if;
2115
2116 end if;
2117
2118 -- Store table for Finalize/Cutover processing
2119 store(x_table_owner, x_table_name);
2120
2121 end PATCH;
2122
2123
2124 /*
2125 ** Finalize Tables (get ready for cutover)
2126 **
2127 ** Process tables that have been marked as Patched
2128 ** Check for un-applied Forward Crossedition Triggers
2129 ** Revise Out-of-date Indexes
2130 */
2131 procedure FINALIZE is
2132 C_MODULE varchar2(80) := c_package||'finalize';
2133
2134 cursor C_PATCHED_TABLES is
2135 select owner, name, status
2136 from ad_patched_tables
2137 where status in ('N', 'U');
2138 begin
2139 log(c_module,'PROCEDURE', 'begin: no parameter(s)');
2140
2141 -- TODO: check for unapplied (disabled) CETS
2142
2143 for tablerec in c_patched_tables loop
2144 log(c_module, 'STATEMENT', 'Process table '|| tablerec.owner||'.'||tablerec.name);
2145
2146 -- Revise Out-of-date Indexs
2147 revise_indexes(tablerec.owner, tablerec.name);
2148
2149 -- Store cutover action: Check for uniqueness also:22-Aug-2012
2150 if (tablerec.status = 'N') then
2151 ad_zd_parallel_exec.load(
2152 x_phase => ad_zd_parallel_exec.c_phase_cutover,
2153 x_sql => 'begin ad_zd_table.cutover('||
2154 ''''||tablerec.owner||''', '||
2155 ''''||tablerec.name||'''); end;',
2156 x_unique => true);
2157 end if;
2158
2159 -- Mark as Complete
2160 update ad_patched_tables
2161 set status = 'C'
2162 where owner = tablerec.owner
2163 and name = tablerec.name;
2164 commit;
2165
2166 end loop;
2167
2168 log(c_module,'PROCEDURE', 'end');
2169 end FINALIZE;
2170
2171
2172
2173 /*
2174 ** Cutover Table
2175 **
2176 ** X_TABLE_OWNER - table owner filter (match any owner if NULL)
2177 ** X_TABLE_NAME - table name filter (match any table if NULL)
2178 **
2179 ** Cutover revised indexes
2180 ** Set obsolete columns to nullable
2181 ** remove obsolete column constraints
2182 **
2183 ** Note: X_EXECUTE is unused and should be eliminated. Storage of cutover DDL
2184 ** is done in the FINALIZE call.
2185 */
2186 procedure CUTOVER(
2187 X_TABLE_OWNER varchar2,
2188 X_TABLE_NAME varchar2,
2189 X_EXECUTE boolean default true)
2190 is
2191 C_MODULE varchar2(80) := c_package||'cutover';
2192
2193 -- Obsolete columns
2194 cursor C_OBSOLETE_COLUMNS(X_OWNER varchar2, X_TABLE_NAME varchar2) is
2195 select col.owner, col.table_name, col.column_name, col.nullable, col.data_default
2196 from dba_tables tab
2197 , dba_tab_columns col
2198 , dba_editioning_views ev
2199 where tab.owner in
2200 ( select oracle_username
2201 from system.fnd_oracle_userid
2202 where read_only_flag in ('A','E','B') )
2203 and col.owner = tab.owner
2204 and col.table_name = tab.table_name
2205 and col.owner = x_owner
2206 and col.table_name = x_table_name
2207 and col.column_name <> 'ZD_EDITION_NAME'
2208 and ev.owner = col.owner
2209 and ev.view_name = substrb(col.table_name, 1, 29)||'#'
2210 and not exists
2211 ( select evc.table_column_name
2212 from dba_editioning_view_cols evc
2213 where evc.owner = ev.owner
2214 and evc.view_name = ev.view_name
2215 and evc.table_column_name = col.column_name )
2216 order by col.owner, col.table_name, col.column_name;
2217
2218 -- Multi-Column constraints on obsolete column
2219 cursor C_COLUMN_CONSTRAINTS(X_OWNER varchar2, X_TABLE_NAME varchar2, X_COLUMN_NAME varchar2) is
2220 select cc.constraint_name
2221 from dba_cons_columns cc
2222 where cc.owner = x_owner
2223 and cc.table_name = x_table_name
2224 and cc.column_name = x_column_name;
2225 begin
2226 log(c_module, 'PROCEDURE',
2227 'begin: '||nvl(x_table_owner,'ALL')||'.'||nvl(x_table_name,'ALL'));
2228
2229 -- Cutover Indexes
2230 cutover_indexes(x_table_owner, x_table_name);
2231
2232 -- Remove constraints from obsolete columns
2233 -- - Not Null Constraints
2234 -- - Multi-column constraints
2235 for colrec in c_obsolete_columns(x_table_owner, x_table_name) loop
2236
2237 -- remove Not Null constraint if column has no default value
2238 if (colrec.nullable = 'N' and
2239 (colrec.data_default is null or upper(colrec.data_default) = 'NULL')) then
2240 log(c_module, 'STATEMENT',
2241 'Alter obsolete column to be nullable: '||
2242 colrec.owner||'.'||colrec.table_name||'.'||colrec.column_name);
2243
2244 exec('alter table "'||colrec.owner||'"."'||colrec.table_name||'"'||
2245 ' modify ("'||colrec.column_name||'" null)', c_module);
2246 end if;
2247
2248 -- remove multi-column constraints involving this column, if any
2249 for conrec in c_column_constraints(colrec.owner, colrec.table_name, colrec.column_name) loop
2250 log(c_module, 'STATEMENT',
2251 'Drop Obsolete column constraint: '||
2252 colrec.owner||'.'||colrec.table_name||'.'||conrec.constraint_name);
2253
2254 exec('alter table "'||colrec.owner||'"."'||colrec.table_name||'"'||
2255 ' drop constraint "'||conrec.constraint_name||'"', c_module);
2256 end loop;
2257
2258 end loop;
2259
2260 begin
2261 delete from ad_patched_tables
2262 where owner = x_table_owner
2263 and name = x_table_name;
2264 exception
2265 when no_data_found then
2266 null;
2267 end;
2268
2269 log(c_module, 'PROCEDURE', 'end');
2270 end CUTOVER;
2271
2272
2273 /*
2274 ** Cleanup Table
2275 **
2276 ** X_TABLE_OWNER - filter for tables to clean (match any owner if NULL)
2277 ** X_TABLE_NAME - filter for tables to clean (match any table if NULL)
2278 ** X_CLEAN_MODE - how to clean
2279 ** 'QUICK' - standard post-patch cleanup, drops indexes, triggers
2280 ** 'FULL' - include obsolete columns (danger, should be fully actualized)
2281 **
2282 ** Drop CETS
2283 ** Mark obsolete columns as unused (FULL)
2284 **
2285 ** TODO: use parallel workers to process cleanup actions more quickly.
2286 ** But this cleanup API must not return control until all cleanup
2287 ** processing is complete.
2288 */
2289 procedure CLEANUP(
2290 X_TABLE_OWNER in varchar2 default NULL,
2291 X_TABLE_NAME in varchar2 default NULL,
2292 X_CLEAN_MODE in varchar2 default 'QUICK')
2293 is
2294 C_MODULE varchar2(80) := c_package||'cleanup';
2295 L_INDEX_OWNER varchar2(30);
2296 L_INDEX_NAME varchar2(30);
2297 L_TABLE_OWNER varchar2(30);
2298 L_TABLE_NAME varchar2(30);
2299 L_COLUMN_NAME varchar2(30);
2300
2301 -- Crossedition Triggers
2302 cursor C_CETS(X_TABLE_OWNER varchar2, X_TABLE_NAME varchar2) is
2303 select trg.owner, trg.trigger_name
2304 from dba_triggers trg
2305 where trg.crossedition in ('FORWARD', 'REVERSE')
2306 and trg.table_owner = nvl(x_table_owner, trg.table_owner)
2307 and trg.table_name = nvl(x_table_name, trg.table_name)
2308 order by trg.owner, trg.trigger_name;
2309
2310 -- Obsolete Columns
2311 cursor C_OBSOLETE_COLUMNS(X_TABLE_OWNER varchar2, X_TABLE_NAME varchar2) is
2312 select col.owner, col.table_name, col.column_name
2313 from dba_tab_columns col
2314 , dba_editioning_views ev
2315 where ev.owner in
2316 ( select oracle_username
2317 from system.fnd_oracle_userid
2318 where read_only_flag in ('A','E','B')
2319 )
2320 and ev.owner = nvl(x_table_owner, col.owner)
2321 and ev.table_name = nvl(x_table_name, col.table_name)
2322 and col.column_name <> 'ZD_EDITION_NAME'
2323 and ev.owner = col.owner
2324 and ev.table_name = col.table_name
2325 and not exists
2326 ( select evc.table_column_name
2327 from dba_editioning_view_cols evc
2328 where evc.owner = ev.owner
2329 and evc.view_name = ev.view_name
2330 and evc.table_column_name = col.column_name
2331 )
2332 order by col.owner, col.table_name, col.column_name;
2333
2334
2335 -- EV Tables with unused columns
2336 -- Currently not used, but in future, use this to find tables that need online redef
2337 cursor C_UNUSED_COL_TABS(X_TABLE_OWNER varchar2, X_TABLE_NAME varchar2) is
2338 select ev.owner, ev.table_name
2339 from dba_unused_col_tabs uct, dba_editioning_views ev
2340 where uct.owner in
2341 ( select oracle_username
2342 from system.fnd_oracle_userid
2343 where read_only_flag in ('A','E','B') )
2344 and uct.owner = nvl(x_table_owner, uct.owner)
2345 and uct.table_name = nvl(x_table_name, uct.table_name)
2346 and ev.owner = uct.owner
2347 and ev.table_name = uct.table_name
2348 order by 1, 2;
2349
2350 begin
2351 log(c_module, 'PROCEDURE',
2352 'begin: '||nvl(x_table_owner,'ALL')||'.'||nvl(x_table_name,'ALL')||', '||x_clean_mode);
2353
2354 -- Disable CETS
2355 log(c_module, 'EVENT', 'Disable Crossedition Triggers');
2356 for cetrec in c_cets(x_table_owner, x_table_name) loop
2357 exec('alter trigger "'||cetrec.owner||'"."'||cetrec.trigger_name||'" DISABLE', c_module, true);
2358 end loop;
2359
2360 -- Cleanup CETS
2361 log(c_module, 'EVENT', 'Drop Crossedition Triggers');
2362 for cetrec in c_cets(x_table_owner, x_table_name) loop
2363 exec('drop trigger "'||cetrec.owner||'"."'||cetrec.trigger_name||'"', c_module, true);
2364 end loop;
2365
2366 -- Additional actions for FULL cleanup
2367 if x_clean_mode = 'FULL' then
2368
2369 -- Mark unused columns
2370 log(c_module, 'EVENT', 'Mark unused columns');
2371 for colrec in c_obsolete_columns(x_table_owner, x_table_name) loop
2372 exec('alter table "'||colrec.owner||'"."'||colrec.table_name||'"'||
2373 ' set unused ('||colrec.column_name||')', c_module, true);
2374 end loop;
2375
2376 end if;
2377
2378 log(c_module, 'PROCEDURE', 'end');
2379 end CLEANUP;
2380
2381
2382 /*
2383 ** Abort patch edition
2384 **
2385 ** Drop new or revised indexes
2386 ** Remove unused column not null constratints
2387 ** Note: CETS are dropped when the Patch Edition is dropped
2388 ** Note: Unused columns are handled during FULL cleanup
2389 */
2390 procedure ABORT is
2391 C_MODULE varchar2(80) := c_package||'abort';
2392
2393 -- Patched Indexes: created since the patch edition
2394 cursor C_PATCHED_INDEXES is
2395 select idx.owner, idx.index_name,
2396 con.owner table_owner, con.table_name, con.constraint_name
2397 from
2398 dba_indexes idx
2399 , dba_constraints con
2400 where idx.owner in
2401 ( select oracle_username from system.fnd_oracle_userid
2402 where read_only_flag in ('A', 'B', 'E', 'U') )
2403 and regexp_like(idx.index_name, ad_zd_table.revised_index_regexp, 'c')
2404 and con.owner(+) = idx.table_owner
2405 and con.table_name(+) = idx.table_name
2406 and con.index_name(+) = idx.index_name
2407 order by 1, 2;
2408
2409 -- Unused Not-Null columns with no default
2410 cursor C_UNUSED_NN_COLUMNS is
2411 select col.owner, col.table_name, col.column_name, col.data_default
2412 from dba_tab_columns col
2413 , dba_editioning_views ev
2414 where col.owner in
2415 ( select oracle_username
2416 from system.fnd_oracle_userid
2417 where read_only_flag in ('A','E','B') )
2418 and col.nullable = 'N'
2419 and col.table_name not like '%#'
2420 and (col.default_length is null or col.default_length = 4)
2421 and ev.owner = col.owner
2422 and ev.view_name = substrb(col.table_name, 1, 29)||'#'
2423 and not exists
2424 ( select evc.table_column_name
2425 from dba_editioning_view_cols evc
2426 where evc.owner = ev.owner
2427 and evc.view_name = ev.view_name
2428 and evc.table_column_name = col.column_name )
2429 order by col.owner, col.table_name, col.column_name;
2430
2431 begin
2432 log(c_module, 'PROCEDURE', 'begin: no parameter(s)');
2433
2434 -- Drop patched indexes
2435 for idxrec in c_patched_indexes loop
2436 if idxrec.constraint_name is not null then
2437 log(c_module, 'EVENT', 'Drop patched constraint: '||
2438 idxrec.table_owner||'.'||idxrec.table_name||'/'||idxrec.constraint_name);
2439 exec('alter table '||idxrec.table_owner||'."'||idxrec.table_name||'" '||
2440 'drop constraint "'||idxrec.constraint_name||'" cascade', c_module, true);
2441 end if;
2442 log(c_module, 'EVENT', 'Drop patched index: '||idxrec.owner||'.'||idxrec.index_name);
2443 exec('drop index "'||idxrec.owner||'"."'||idxrec.index_name||'"', c_module, true);
2444 end loop;
2445
2446 -- Alter unused not-null columns with no default value to be nullable
2447 for colrec in c_unused_nn_columns loop
2448 if colrec.data_default is null or upper(colrec.data_default) = 'NULL' then
2449 log(c_module, 'EVENT', 'Alter unused column to be nullable: '||
2450 colrec.owner||'.'||colrec.table_name || '.' || colrec.column_name );
2451 exec('alter table "'||colrec.owner||'"."'||colrec.table_name||'"'||
2452 ' modify ('||colrec.column_name||' null)', c_module, true);
2453 end if;
2454 end loop;
2455
2456 -- Clear patched table list
2457 delete from ad_patched_tables;
2458 commit;
2459
2460 log(c_module, 'PROCEDURE', 'end');
2461 end ABORT;
2462
2463 /*
2464 * Drop an editioned table
2465 *
2466 * TODO: Drop Synonyms pointing to Editioning View
2467 * Drop VPD policies on Editioning View
2468 * Drop Editioning View
2469 * If Running in PATCH edition
2470 * Store deferred DDL to drop table in CLEANUP
2471 * If running in RUN edition
2472 * Drop table immediately
2473 *
2474 */
2475 procedure DROP_TABLE (
2476 X_TABLE_OWNER in varchar2,
2477 X_TABLE_NAME in varchar2,
2478 X_DROP_STMT in varchar2,
2479 X_UPD_STMT in varchar2,
2480 X_DROPPED out nocopy varchar2 )
2481 is
2482 C_MODULE varchar2(80) := c_package || 'DROP';
2483 l_table_owner varchar2(30);
2484 l_ev_name varchar2(30);
2485 l_count varchar2(30);
2486 l_appsname varchar2(30);
2487 l_defer_stmt varchar2(2000);
2488
2489 cursor C_SYNONYMS(x_table_owner varchar2, x_ev_name varchar2) is
2490 select syn.owner owner, syn.synonym_name name
2491 from dba_synonyms syn
2492 where syn.table_owner = x_table_owner
2493 and syn.table_name = x_ev_name
2494 and syn.owner <> 'PUBLIC';
2495
2496 begin
2497 l_appsname := ad_zd.apps_schema;
2498 l_table_owner := trim(nvl(x_table_owner, l_appsname));
2499 log(C_MODULE, 'PROCEDURE', 'begin: '|| l_table_owner ||'.'||x_table_name);
2500
2501 -- Verify table exists
2502 select count(1)
2503 into l_count
2504 from dba_tables tab
2505 where tab.owner = l_table_owner
2506 and tab.table_name = x_table_name;
2507
2508 if (l_count = 0) then
2509 -- Table doesn't exist.
2510 if (x_upd_stmt is not null) then
2511 exec(x_upd_stmt, c_module, true);
2512 end if;
2513 X_DROPPED := 'Y';
2514 else
2515 l_ev_name := ad_zd_table.ev_view(x_table_name);
2516
2517 -- Drop synonyms pointing to EV
2518 for synrec in c_synonyms(l_table_owner, l_ev_name) loop
2519 log(c_module, 'EVENT', 'Dropping synonym '||synrec.owner||'.'||synrec.name);
2520 exec ('drop synonym "' || synrec.owner||'"."'||synrec.name||'"', c_module, true);
2521 end loop;
2522
2523 -- Drop Edition view
2524 log(c_module, 'EVENT', 'Drop EV '||l_table_owner||'.'||l_ev_name);
2525 exec('drop view "'||l_table_owner||'"."'||l_ev_name ||'"', c_module, true);
2526
2527 -- (Deferred) drop table logic
2528 if ad_zd.get_edition_type = 'RUN' then
2529 -- Drop table
2530 log(c_module, 'EVENT', 'Drop table '||l_table_owner||'.'||x_table_name);
2531 begin
2532 exec(x_drop_stmt, c_module);
2533
2534 -- Update dropped status
2535 if (x_upd_stmt is not null) then
2536 exec(x_upd_stmt, c_module, true);
2537 end if;
2538
2539 X_DROPPED := 'Y';
2540
2541 exception
2542 when others then
2543 if (sqlcode <> -942) then
2544 log(c_module, 'ERROR', 'Error while dropping table '
2545 ||l_table_owner||'.'||x_table_name || ': ' || substr(sqlerrm, 1, 2000));
2546 X_DROPPED := 'N';
2547 else
2548 -- Update dropped status
2549 if (x_upd_stmt is not null) then
2550 exec(x_upd_stmt, c_module, true);
2551 end if;
2552
2553 X_DROPPED := 'Y';
2554 end if;
2555 end;
2556
2557 else -- If running from PATCH edition
2558
2559 -- Defer drop table to next cleanup
2560 log (c_module, 'EVENT', 'Defer drop table ' ||
2561 l_table_owner||'.'||x_table_name|| ' to next cleanup');
2562
2563 if (x_upd_stmt is not null) then
2564 l_defer_stmt := 'begin execute immediate '''|| regexp_replace(x_drop_stmt, '''', '''''') || '''; ' ||
2565 'execute immediate ''' || regexp_replace(x_upd_stmt, '''', '''''') ||
2566 '''; exception when others then null; end;';
2567
2568 ad_zd.load_ddl ('CLEANUP', l_defer_stmt);
2569 else
2570 ad_zd.load_ddl ('CLEANUP', x_drop_stmt);
2571 end if;
2572
2573 -- Return 'N' as the table object is not deleted yet.
2574 X_DROPPED := 'N';
2575 end if;
2576 end if;
2577
2578 log( c_module, 'PROCEDURE', 'end');
2579 commit;
2580 end DROP_TABLE;
2581
2582 end AD_ZD_TABLE;