Quantcast
Channel: Doyensys Allappsdba Blog..
Viewing all 1640 articles
Browse latest View live

Checking Socket and Servlet Mode

$
0
0

Using the below methods we can check whether the Oracle Forms server is running in servlet or socket mode.

Oracle Applications Release 12

Note Oracle Application Release 12 is, by default, configured to run in servlet mode.

Use the following command:

[applmgr@webserver:] grep connectMode$FORMS_WEB_CONFIG_FILE
connectMode=servlet
[applmgr@webserver:]

Alternatively, use the following command:
[applmgr@webserver:] grep _frmConnectMode $CONTEXT_FILE
<forms_connect oa_var="s_frmConnectMode">servlet</forms_connect>
[applmgr@webserver:]

Oracle Applications Release 11

Note Oracle Application Release 11 is, by default, configured to run in socket mode.

Use the following command:

$ grep connectMode FORMS60_WEB_CONFIG_FILE
The current connection mode is reported:
connectMode=socket

Alternatively, use the following command:

$ grep socket CONTEXT_FILE
The current connection mode is reported:
<forms_connect oa_var="s_frmConnectMode">socket</forms_conr....


Checking the HTML Source:

Finally, you can also check the HTML source of page used to launch the Oracle Forms application. To do so with Internet Explorer, select View, and then Source. This contains the connection mode

Manual cleanup of extract process from the database for Goldengate

$
0
0
When removing an extract process, it has to be first unregistered from the database.

In this case, it was not unregistered properly, so we need to clear it manually.

Please find the list of steps to do that.

SYS@ORCL>>select CAPTURE_NAME,QUEUE_NAME,QUEUE_OWNER,CAPTURE_USER,START_SCN,STATUS,STATUS_CHANGE_TIME,PURPOSE  from dba_capture;

CAPTURE_NAME      QUEUE_NAME      QUEUE_OWNER CAPTURE_USER            START_SCN STATUS   STATUS_CHANGE_TIME       PURPOSE
----------------- --------------- ----------- ------------ -------------------- -------- ------------------------ -------------------
GG$CAP_REPCPR      GG$Q_REPCPR     GGOWNER     GGOWNER              23987613411 DISABLED 15-SEP-2018 12:12:29     GoldenGate Capture


SYS@ORCL>>exec DBMS_CAPTURE_ADM.DROP_CAPTURE ('GG$CAP_REPCPR');

PL/SQL procedure successfully completed.

SYS@ORCL>>select  CAPTURE_NAME,QUEUE_NAME,QUEUE_OWNER,CAPTURE_USER,START_SCN,STATUS,STATUS_CHANGE_TIME,PURPOSE  from dba_capture;

no rows selected

SYS@ORCL>>execute DBMS_STREAMS_ADM.REMOVE_STREAMS_CONFIGURATION();

PL/SQL procedure successfully completed.

How to Find whether Application is Under Maintenance Mode from SQLPLUS

$
0
0
Query to know the status of maintenance mode:

select fnd_profile.value('APPS_MAINTENANCE_MODE') from dual;

IF THE STATUS
“MAINT” = MAINTENANCE MODE HAS BEEN ENABLED AND THE USERS WILL NOT BE ABLE TO LOGIN.
“NORMAL” = MAINTENANCE MODE HAS BEEN DE-ACTIVATED AND THE USERS WILL BE ABLE TO LOGIN.

ENABLE maintenance mode from SQLPLUS Session:

sqlplus -s apps/apps @$AD_TOP/patch/115/sql/adsetmmd.sql ENABLE

DISABLE  maintenance mode from SQLPLUS Session:

sqlplus -s apps/apps @$AD_TOP/patch/115/sql/adsetmmd.sql DISABLE


SQL> select fnd_profile.value('APPS_MAINTENANCE_MODE') from dual;

FND_PROFILE.VALUE('APPS_MAINTENANCE_MODE')
--------------------------------------------------------------------------------
NORMAL

Does the Explain Plan command really show the execution plan that will be used?

$
0
0
Does the Explain Plan command really show the execution plan that will be used?

When it comes to SQL tuning we often need to look at the execution plan for a SQL statement to determine where the majority of the time is spent. But how we generate that execution plan can have a big impact on whether or not the plan we are looking at is really the plan that is used.

The two most common methods used to generate the execution plan for a SQL statement are:

EXPLAIN PLAN command – This displays an execution plan for a SQL statement without actually executing the statement.

V$SQL_PLAN– A dynamic performance view introduced in Oracle 9i that shows the execution plan for a SQL statement that has been compiled into a cursor and stored in the cursor cache.

My preferred method is always to use V$SQL_PLAN (even though it requires the statement to at least begin executing) because under certain conditions the plan shown by the EXPLAIN PLAN command can be different from the plan that will actually be used when the query is executed.

So, what can cause the plans to differ?


Bind Variables

When a SQL statement contains bind variables, the plan shown using EXPLAIN PLAN is not aware of bind variable values while the plan shown in V$SQL_PLAN takes the bind variable values into account in the plan generation process. Let’s look at a simple example, using the customers tables, which has 1,018 rows and an index on the C_ZIPCODE column.

SELECT COUNT(*) FROM  customers;

  COUNT(*)
----------
      1018

SELECT   c_zipcode, COUNT(*)  FROM  customers GROUP BY c_zipcode;

 C_ZIPCODE   COUNT(*)
---------- ----------
     20001  290
      2111   81
     10018  180
     90034  225
     94102  225
     94065   17


var n NUMBER;
exec :n :=94065;

PL/SQL PROCEDURE successfully completed.

SELECT COUNT(c_email) FROM   customers WHERE  c_zipcode=:n;

COUNT(C_EMAIL)
--------------
    17

SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format=&gt;'typical +peeked_binds'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------
SQL_IDbjj643zga3mfu, child NUMBER 0
-------------------------------------
SELECT COUNT(c_email) FROM customers WHERE c_zipcode=:n

Plan hash VALUE: 4213764942

----------------------------------------------------------------------
| Id  | Operation      | Name    | Rows  |  Bytes|
----------------------------------------------------------------------
|   0 | SELECT STATEMENT      |    |    |       | 
|   1 |  SORT AGGREGATE               |    |  1 |   24  |
|   2 |   TABLE ACCESS BY INDEX ROWID | CUSTOMERS   | 17 |       |
|*  3 |    INDEX RANGE SCAN      | IND_CUST_ZIP| 17 |    | 
----------------------------------------------------------------------

Peeked Binds (identified BY position):
--------------------------------------
   1 - :N (NUMBER): 94065

Predicate Information (identified BY operation id):
---------------------------------------------------
   3 - access("C_ZIPCODE"=:N)

20 rows selected.

EXPLAIN PLAN FOR
SELECT COUNT(c_email) FROM customers WHERE c_zipcode=:n;

Explained.

SQL&gt;
SQL&gt; SELECT * FROM TABLE(DBMS_XPLAN.display());

PLAN_TABLE_OUTPUT
----------------------------------------------------------------
Plan hash VALUE: 296924608
----------------------------------------------------------------
| Id  | Operation   | Name      | Rows  | Bytes | Cost |
----------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |     1 |    24 |  7   |
|   1 |  SORT AGGREGATE    |       |     1 |    24 |      | 
|*  2 |   TABLE ACCESS FULL| CUSTOMERS |   170 |  4080 |  7   |
-----------------------------------------------------------------

Predicate Information (identified BY operation id):
---------------------------------------------------
   2 - filter("C_ZIPCODE"=TO_NUMBER(:N))
When we query the actual plan used at execution from V$SQL_PLAN via the DBMS_XPLAN.DISPLAY_CURSOR command we get an index access plan and the cardinality estimate (estimated number of rows returned) is accurate at 17 rows.

However when we use the EXPLAIN PLAN command for our statement, we get a full table scan plan and a cardinality estimate of 170 rows.

The first indication that the EXPLAIN PLAN command is not bind aware can be seen in the predicate information under the plan. There you will see the addition of a TO_NUMBER function to our bind variable :N, even though we declare the variable as a number.

Since no bind peeking occurs the optimizer can’t used the histogram on the c_zipcode column. Therefore the optimizer has to assume a uniform data distribution in the c_zipcode column and it calculates the cardinality estimate as NUM_ROWS / NDV or 1018/6 = 169.66, which rounded up is 170 rows.

Cursor_Sharing = FORCE

By setting the initialization parameter CURSOR_SHARING to FORCE, you are asking Oracle to replace the literal values in your SQL statements with system generated bind variables (commonly known as literal replacement). The intent of literal replacement is to reduce the number of cursors generated in the shared pool. In the best-case scenario, only one cursor will be built for all statements that only differ in the literal value used.

Let’s take our original example and replace our bind variable :N with the literal value 94065 and see what happens when CURSOR_SHARING is set to FORCE and we use the EXPLAIN PLAN command.

ALTER SYSTEM SET cursor_sharing = force;

System altered.

SELECT COUNT(c_email)
FROM   customers
WHERE  c_zipcode=94065;

COUNT(C_EMAIL)
--------------
    17

SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format=&gt;'typical +peeked_binds'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------
SQL_IDdjn0jckqvy2gk, child NUMBER 0
-------------------------------------
SELECT COUNT(c_email) FROM customers WHERE c_zipcode=:"SYS_B_0"

Plan hash VALUE: 4213764942

---------------------------------------------------------------------
| Id  | Operation     | Name     | Rows  |Bytes |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT     |    |    |    | 
|   1 |  SORT AGGREGATE      |    |  1 | 24 |
|   2 |   TABLE ACCESS BY INDEX ROWID| CUSTOMERS    | 17 |408 | 
|*  3 |    INDEX RANGE SCAN     | IND_CUST_ZIP | 17 |    | 
---------------------------------------------------------------------

Peeked Binds (identified BY position):
--------------------------------------
   1 - :SYS_B_0 (NUMBER): 94065

Predicate Information (identified BY operation id):
---------------------------------------------------
   3 - access("C_ZIPCODE"=:SYS_B_0)

25 rows selected.

EXPLAIN PLAN FOR
SELECT COUNT(c_email) FROM customers WHERE c_zipcode=94065;

Explained.

SELECT * FROM TABLE(DBMS_XPLAN.display());

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------
Plan hash VALUE: 4213764942
--------------------------------------------------------------------
| Id  | Operation     | Name     | Rows  |Bytes|
--------------------------------------------------------------------
|   0 | SELECT STATEMENT     |    |    |   | 
|   1 |  SORT AGGREGATE              |    |  1 | 24|
|   2 |   TABLE ACCESS BY INDEX ROWID| CUSTOMERS    | 17 |408| 
|*  3 |    INDEX RANGE SCAN     | IND_CUST_ZIP | 17 |   | 
--------------------------------------------------------------------
Predicate Information (identified BY operation id):
---------------------------------------------------
   3 - access("C_ZIPCODE"=94065)
This time the plan is the same in both cases but if you look at the predicate information under both plans, you will notice that the explain plan command did not do the literal replacement. It still shows the predicate as C_ZIPCODE=94065 instead of C_ZIPCODE=:SYS_B_0.

So, why didn’t the explain plan command do the literal replacement?

The cursor generated by an EXPLAIN PLAN command is not shareable by design. Since the cursor isn’t shared there is no point in doing the literal replacement that would allow the cursor to be shared. Therefore the explain plan command does not replace the literals.

To demonstrate that the EXPLAIN PLAN command cursors are not shared, I ran our example queries two more times and then queried V$SQL.

SELECT sql_id, sql_text, executions, child_number FROM   v$sql WHERE  sql_text LIKE '%SELECT count(c_email)%';

SQL_ID        SQL_TEXT                               EXECUTIONS CHILD_NUMBER
------------- -------------------------------------- ---------- ------------
djn0jckqvy2gk SELECT COUNT(c_email) FROM customers            3            0
              WHERE c_zipcode=:"SYS_B_0"

78h277aadmkku EXPLAIN PLAN FOR SELECT COUNT(c_email)          1            0
               FROM customers WHERE c_zipcode=94065

78h277aadmkku EXPLAIN PLAN FOR SELECT COUNT(c_email)          1            1
               FROM customers WHERE c_zipcode=94065

78h277aadmkku EXPLAIN PLAN FOR SELECT COUNT(c_email)          1            2

4 rows selected.
You will notice that the actual query had it’s literal value replaced by the system generated bind :SYS_B_0 and only a single cursor (child_number 0) was generated, which was executed 3 times.

For the EXPLAIN PLAN version of the statement, no literal replace occurred and each execution created a new child cursor (0,1,2). Demonstrating that no cursor sharing occurs with the explain plan command.

So, what I have a few extra cursors. What’s the big deal?

The big deal is if you want to use any plan stability features like SQL plan baselines then you will not see the effect of these feature with EXPLAIN PLAN when CURSOR_SHARING is set to FORCE. Assuming you created the SQL plan baseline for the statement with the system generated bind :SYS_B_0 but then check which plan will be used with EXPLAIN PLAN not literal replace occurred, therefore there’s no corresponding baseline will be found for the statement. You can see an example of this in a recent AskTOM question I answered.

Adaptive Plans

In Oracle Database 12c Adaptive Plans enable the optimizer to defer the final plan decision for a statement, until execution time.

The optimizer instruments it’s chosen plan (the default plan), with statistics collectors so that at runtime, it can detect if its cardinality estimates, differ greatly from the actual number of rows seen by the operations in the plan. If there is a significant difference, then the plan or a portion of it can be automatically adapted to avoid suboptimal performance on the first execution of a SQL statement.

Currently only the join method or the parallel query distribution methods can adapt.



By default, the explain plan command will show only the initial or default plan chosen by the optimizer. Whereas the DBMS_XPLAN.DISPLAY_CURSOR function displays the final plan used by the query or the complete adaptive plan with the additional format parameter ‘+adaptive’.

Let’s look at a simple of example of a two table join that has an adaptive plans to understand the difference in what you will see between explain plan and DBMS_XPLAN.DISPLAY_CURSOR function.

EXPLAIN PLAN FOR
SELECT /*+ gather_plan_statistics*/ p.product_name FROM   order_items2 o, product_information p WHERE  o.unit_price = 15 AND    o.quantity &gt; 1 AND    p.product_id = o.product_id;

Explained.

SELECT * FROM TABLE(DBMS_XPLAN.display());

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------
Plan hash VALUE: 983807676
-----------------------------------------------------------------------
| Id  | Operation     | Name      | Rows  |
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |     4 | 
|   1 |  NESTED LOOPS     |      |     4 | 
|   2 |   NESTED LOOPS     |      |     4 | 
|*  3 |    TABLE ACCESS FULL     | ORDER_ITEMS2      |     4 |   
|*  4 |    INDEX UNIQUE SCAN     | PRODUCT_INFORMATION_PK |     1 |     
|   5 |   TABLE ACCESS BY INDEX ROWID| PRODUCT_INFORMATION    |     1 |   
-----------------------------------------------------------------------

Predicate Information (identified BY operation id):
---------------------------------------------------

   3 - filter("O"."UNIT_PRICE"=15 AND "O"."QUANTITY"&gt;1)
   4 - access("P"."PRODUCT_ID"="O"."PRODUCT_ID")

Note
-----
   - this IS an adaptive plan

22 rows selected.

SELECT /*+ gather_plan_statistics*/ p.product_name FROM   order_items2 o, product_information p WHERE  o.unit_price = 15 AND    o.quantity &gt; 1 AND    p.product_id = o.product_id;

SELECT * FROM TABLE(DBMS_XPLAN.display_cursor());

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------
SQL_IDd3mzkmzxn264d, child NUMBER 0
-------------------------------------
SELECT /*+ gather_plan_statistics */ p.product_name FROM order_items2
o, product_information p WHERE o.unit_price = 15   AND o.quantity &gt; 1
AND p.product_id = o.product_id

Plan hash VALUE: 2886494722

------------------------------------------------------------------
| Id  | Operation   | Name | Rows  | Bytes |
------------------------------------------------------------------
|   0 | SELECT STATEMENT   | | | |   
|*  1 |  HASH JOIN   | |     4 |   128 |   
|*  2 |   TABLE ACCESS FULL| ORDER_ITEMS2 |     4 |    48 |
|   3 |   TABLE ACCESS FULL| PRODUCT_INFORMATION |     1 |    20 |   
------------------------------------------------------------------

Predicate Information (identified BY operation id):
---------------------------------------------------

   1 - access("P"."PRODUCT_ID"="O"."PRODUCT_ID")
   2 - filter(("O"."UNIT_PRICE"=15 AND "O"."QUANTITY"&gt;1))

Note
-----
   - this IS an adaptive plan


27 rows selected.
As you can see the initial plan the optimizer came up with was a NESTED LOOP join, when the final plan was in fact a HASH JOIN. If you only use the EXPLAIN PLAN command you would never know a completely different join method was used.

So, my advice is to use  V$SQL_PLAN when reviewing the execution plan for a SQL statement, as it will also show the play actually used by the statement.

Controlling where objects are populated into memory on RAC

$
0
0

Controlling where objects are populated into memory on RAC:
One of the things I looked at was the new FOR SERVICE sub-clause of the INMEMORY DISTRIBUTE clause.
ALTER TABLE customers INMEMORY PRIORITY HIGH DISTRIBUTE FOR SERVICE IM1;
This new sub-clause allows you to control exactly where an object (table or partition) is populated into memory in a RAC environment based on where a database service is allowed to run. If the service is stopped then the objects distributed for that service are automatically removed from the IM column store(s).
If you recall each RAC node has it’s own In-Memory column store (IM column store). By default, when a table is populated into memory in a RAC environment it will be automatically distributed across all of the IM column stores in the cluster. That is to say, a piece of the table will appear in each RAC node, effectively making the IM column store a shared-nothing architecture.
This is not always desirable, especially if you only run certain applications or workloads on a subset of RAC node.
-- Step1 configure the database services
srvctl ADD service -db orcl -service IM1 –preferred “rac1”
 srvctl ADD service -db orcl -service IM2 -r preferred “rac2”
 srvctl ADD service -db orcl -service IM_ALL -r preferred “rac1,rac2”
 srvctl START service -db orcl -service "IM1,IM2,IM_ALL" 
srvctl STATUS service -db orcl
 -- Step 2 Add INMEMORY attributes to the SALES, CUSTOMERS and PRODUCTS tables
 ALTER TABLE customers INMEMORY PRIORITY HIGH DISTRIBUTE FOR SERVICE IM1;
 ALTER TABLE products INMEMORY PRIORITY MEDIMUM DISTRIBUTE FOR SERVICE IM2;
 ALTER TABLE sales INMEMORY PRIOIRTY HIGH;
 -- Step 3 Check the content of the In-Memory column store
 SELECT * FROM   gv$inmemory_area;
 SELECT v.inst_id,  v.owner, v.segment_name name, v.populate_status, SUM(v.bytes_not_populated)/1024/1024 MB_not_populated FROM   gv$im_segments v GROUP  BY v.inst_id,  v.owner, v.segment_name, v.populate_status; 
 SELECT m.inst_id, m.blocksinmem,  m.datablocks FROM   gv$im_segments_detail m,  user_objects o WHERE  m.dataobj = o.object_id AND    o.object_name = 'SALES'; 
 -- Step 4 create a new session
-- Run query and capture the session level statistics before and after the query
 SELECT t1.name, t2.value FROM v$sysstat t1, v$mystat t2 WHERE t1.name IN ('table scans (long tables)','table scans (IM)', 'session logical reads', 'session logical reads - IM', 'IM scan rows',  'IM scan segments disk') AND t1.statistic# = t2.statistic# ORDER BY t1.name;
 SELECT c.cust_city,  p.prod_name, SUM(s.amount_sold) FROM   sales s, customers c, products p WHERE  s.cust_id = c.cust_id AND    s.prod_id = p.prod_id GROUP  BY c.cust_city, p.prod_name; 
 SELECT t1.name, t2.value FROM v$sysstat t1, v$mystat t2 WHERE t1.name IN ('table scans (long tables)','table scans (IM)', 'session logical reads', 'session logical reads - IM', 'IM scan rows',  'IM scan segments disk')AND t1.statistic# = t2.statistic# ORDER BY t1.name;
 -- Step 5 Repopulate the tables so they are all on the same node
 ALTER TABLE products INMEMORY PRIORITY MEDIMUM DISTRIBUTE FOR SERVICE IM1;
 ALTER TABLE sales INMEMORY PRIOIRTY HIGH DISTRIBUTE FOR SERVICE IM1;
 -- Step 6 Check the content of the In-Memory column store
 SELECT v.inst_id,  v.owner, v.segment_name name, v.populate_status,  SUM(v.bytes_not_populated)/1024/1024 MB_not_populated FROM   gv$im_segments v GROUP  BY v.inst_id,  v.owner, v.segment_name, v.populate_status; 
 -- Step 7 Create a new session and rerun the query and check the session statistics before and after 
 SELECT t1.name, t2.value FROM v$sysstat t1, v$mystat t2 WHERE t1.name IN ('table scans (long tables)','table scans (IM)', 'session logical reads', 'session logical reads - IM', 'IM scan rows', 'IM scan segments disk')AND t1.statistic# = t2.statistic# ORDER BY t1.name;
 SELECT c.cust_city, p.prod_name,  SUM(s.amount_sold) FROM   sales s,  customers c,  products p WHERE  s.cust_id = c.cust_id AND    s.prod_id = p.prod_id GROUP  BY c.cust_city, p.prod_name; 
 SELECT t1.name, t2.value FROM v$sysstat t1, v$mystat t2 WHERE t1.name IN ('table scans (long tables)', 'table scans (IM)', 'session logical reads', 'session logical reads - IM', 'IM scan rows',  'IM scan segments disk')AND t1.statistic# = t2.statistic# ORDER BY t1.name;
 -- Step 8 stop the service and check that all of the objects were removed from the IM column store
 srvctl stop service -db main -service "IM1"
 SELECTv.inst_id, v.owner,  v.segment_namename, v.populate_status,  SUM(v.bytes_not_populated)/1024/1024 MB_not_populated FROM   gv$im_segments v GROUP  BY v.inst_id,  v.owner, v.segment_name, v.populate_status;

Using DBMS_XPLAN.DISPLAY_CURSOR to examine Execution Plans

$
0
0
Using DBMS_XPLAN.DISPLAY_CURSOR to examine Execution Plans:

If it is possible to get the same information from a traditional text based execution plan, as not everyone has access to SQL Monitor?”


The answer is yes, it is possible to see a lot of the information showed in SQL Monitor by viewing the execution plan via the DBMS_XPLAN.DISPLAY_CURSOR function. In order to call this function you will need SELECT or READ privilege on the fixed views V$SQL_PLAN_STATISTICS_ALL, V$SQL and V$SQL_PLAN, otherwise you’ll get an error message.

The DBMS_XPLAN.DISPLAY_CURSOR function takes three parameters:


SQL ID– default null, means the last SQL statement executed in this session

CURSOR_CHILD_NO– default 0
FORMAT– Controls the level of details that will be displayed in the execution plan, default TYPICAL.


How do I see the actual number of rows and elapse time for each step in the plan?


You will need to do two things in order to see the actual number of rows:


1.Add the GATHER_PLAN_STATISTICS hint to the SQL statement
2.Setting the FORMAT parameter of DBMS_XPLAN.DISPLAY_CURSOR to ‘ALLSTATS LAST’

SELECT /*+ GATHER_PLAN_STATISTICS */ t2.owner, SUM(b.object_id)FROM  big_table b, t2 ,t1WHERE b.object_id = t2.object_idAND   b.data_object_id = t1.data_object_idAND   t1.object_type='TABLE'AND   t2.owner ='SSB'GROUP BY t2.owner; 

SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format=>'ALLSTATS LAST')); 

PLAN_TABLE_OUTPUT----------------------------------------------------------------------------------------------------------
SQL_ID  d3z7q78jtgxm2, child NUMBER 1-------------------------------------

SELECT /*+ GATHER_PLAN_STATISTICS */ t2.owner, SUM(b.object_id) FROMbig_table b, t2 ,t1 WHERE b.object_id = t2.object_id ANDb.data_object_id = t1.data_object_id AND t1.object_type='TABLE' ANDt2.owner ='SSB' GROUP BY t2.owner 


Plan hash VALUE: 1122440390

 ----------------------------------------------------------------------------------------------------------------------- | Id  | Operation            | Name      | Starts | E-Rows | A-Rows |   A-TIME   | Buffers |  OMem |  1Mem | Used-Mem | -----------------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT     |           |      1 |        |      1 |00:00:00.25 |   50110 |       |       |          ||   1 |  SORT GROUP BY NOSORT|           |      1 |    256 |      1 |00:00:00.25 |   50110 |       |       |          ||*  2 |   HASH JOIN          |           |      1 |     19 |    256 |00:00:00.91 |   50110 |  2440K|  2440K| 1474K (0)|    |*  3 |    TABLE ACCESS FULL | T1        |      1 |    592 |    592 |00:00:00.01 |     146 |       |       |          ||*  4 |    HASH JOIN         |           |      1 |   1599 |   3072 |00:00:06.31 |   49964 |  2293K|  2293K| 1590K (0)||*  5 |     TABLE ACCESS FULL| T2        |      1 |    102 |    102 |00:00:00.01 |       9 |       |       |          ||*  6 |     TABLE ACCESS FULL| BIG_TABLE |      1 |    298K|    298K|00:00:00.61 |   49955 |       |       |          |----------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified BY operation id):---------------------------------------------------

2 - access("B"."DATA_OBJECT_ID"="T1"."DATA_OBJECT_ID")
3 - filter(("T1"."DATA_OBJECT_ID" IS NOT NULL AND "T1"."OBJECT_TYPE"='TABLE'))
4 - access("B"."OBJECT_ID"="T2"."OBJECT_ID")
5 - filter("T2"."OWNER"='SSB')
6 - filter("B"."DATA_OBJECT_ID" IS NOT NULL)

NOTE: If you can’t modify the SQL statement to add the hint, it is possible to add the hint via a SQL Profile as demonstrated by Kerry Osborne.


How do I get the COST of the plan to be displayed when I specify  ALLSTATS LAST for the FORMAT parameter?

When you use ‘ALLSTATS LAST’ for the FORMAT parameter, the estimates number of bytes (BYTES) and the estimated cost for the plan (COST) are not displayed by default. You can easily have these columns displayed by adding additional predicates to the FORMATparameter. Each additional predicate is proceeded with ‘+’ sign.


SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(sql_id=>'d3z7q78jtgxm2',                                      format=>'ALLSTATS LAST +cost +bytes')); 

PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  d3z7q78jtgxm2, child NUMBER 1 ------------------------------------- 

SELECT /*+ GATHER_PLAN_STATISTICS */ t2.owner, SUM(b.object_id) FROM big_table b, t2 ,t1 WHERE b.object_id = t2.object_id AND b.data_object_id = t1.data_object_id AND t1.object_type='TABLE' AND t2.owner ='SSB' GROUP BY t2.owner  Plan hash VALUE: 1122440390 -------------------------------------------------------------------------------------------------------------------------------------------- | Id  | Operation            | Name      | Starts | E-Rows |E-Bytes| Cost (%CPU)| A-Rows |   A-TIME   | Buffers |  OMem |  1Mem | Used-Mem | -------------------------------------------------------------------------------------------------------------------------------------------- |   0 | SELECT STATEMENT     |           |      1 |        |       | 15855 (100)|      1 |00:00:00.23 |   50110 |       |       |          | |   1 |  SORT GROUP BY NOSORT|           |      1 |     19 |   475 | 15855   (1)|      1 |00:00:00.23 |   50110 |       |       |          | |*  2 |   HASH JOIN          |           |      1 |     19 |   475 | 15855   (1)|    256 |00:00:00.91 |   50110 |  2440K|  2440K| 1474K (0)| |*  3 |    TABLE ACCESS FULL | T1        |      1 |    113 |  1017 |  2222   (1)|    592 |00:00:00.01 |     146 |       |       |          | |*  4 |    HASH JOIN         |           |      1 |   1599 | 25584 | 13634   (1)|   3072 |00:00:05.65 |   49964 |  2293K|  2293K| 1567K (0)| |*  5 |     TABLE ACCESS FULL| T2        |      1 |    102 |   918 |    13   (0)|    102 |00:00:00.01 |       9 |       |       |          | |*  6 |     TABLE ACCESS FULL| BIG_TABLE |      1 |    298K|  2037K| 13620   (1)|    298K|00:00:00.73 |   49955 |       |       |          | -------------------------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified BY operation id): ---------------------------------------------------
 2 - access("B"."DATA_OBJECT_ID"="T1"."DATA_OBJECT_ID") 
3 - filter(("T1"."DATA_OBJECT_ID" IS NOT NULL AND "T1"."OBJECT_TYPE"='TABLE')) 
4 - access("B"."OBJECT_ID"="T2"."OBJECT_ID") 
5 - filter("T2"."OWNER"='SSB') 
6 - filter("B"."DATA_OBJECT_ID" IS NOT NULL)

NOTE: You can find a list of additional predicates that can be added in the Oracle Database PL/SQL Packages and Types Reference guide.
It’s also possible to remove columns from the plan table or other information from the output by adding additional predicates to the FORMAT parameter proceeded with a ‘-‘ sign. For example, the command below removes the E-Rows column and predicate information from below the plan.


SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(sql_id=>'d3z7q78jtgxm2',                                       format=>'ALLSTATS LAST -rows –predicate')); PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------SQL_ID  d3z7q78jtgxm2, child NUMBER 1-------------------------------------SELECT /*+ GATHER_PLAN_STATISTICS */ t2.owner, SUM(b.object_id) FROMbig_table b, t2 ,t1 WHERE b.object_id = t2.object_id ANDb.data_object_id = t1.data_object_id AND t1.object_type='TABLE' ANDt2.owner ='SSB' GROUP BY t2.owner 

Plan hash VALUE: 1122440390--------------------------------------------------------------------------------------------------------------| Id  | Operation            | Name      | Starts | A-Rows |   A-TIME   | Buffers |  OMem |  1Mem | Used-Mem |--------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT     |           |      1 |      1 |00:00:00.25 |   50110 |       |       |          ||   1 |  SORT GROUP BY NOSORT|           |      1 |      1 |00:00:00.25 |   50110 |       |       |          ||*  2 |   HASH JOIN          |           |      1 |    256 |00:00:00.91 |   50110 |  2440K|  2440K| 1474K (0)||*  3 |    TABLE ACCESS FULL | T1        |      1 |    592 |00:00:00.01 |     146 |       |       |          ||*  4 |    HASH JOIN         |           |      1 |   3072 |00:00:06.31 |   49964 |  2293K|  2293K| 1590K (0)||*  5 |     TABLE ACCESS FULL| T2        |      1 |    102 |00:00:00.01 |       9 |       |       |          ||*  6 |     TABLE ACCESS FULL| BIG_TABLE |      1 |    298K|00:00:00.61 |   49955 |       |       |          |--------------------------------------------------------------------------------------------------------------

How do I determine the join order if I can’t see the plan tree with DBMS_XPLAN.DISPLAY_CURSOR? 
In the SQL Monitor video I showed you how I use the plan tree to determine the join order but with DBMS_XPLAN.DISPLAY_CURSOR the execution plan is displayed only as table. Although it is possible to determine the join order by looking at the indentation of the tables in the operation column, I find it far easier to use to use the FORMAT parameter of DBMS_XPLAN.DISPLAY_CURSOR to display the outline information for the plan, which will contain the join order.
Adding the ‘+outline’ predicate to the FORMAT parameter will return the outline (full set of hints to reproduce the statement) for the SQL statement.
 SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(sql_id=>'d3z7q78jtgxm2',                                       format=>'ALLSTATS LAST +outline')); -----------------------------------------------------------------------------------------------------------------------| Id  | Operation            | Name      | Starts | E-Rows | A-Rows |   A-TIME   | Buffers |  OMem |  1Mem | Used-Mem |-----------------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT     |           |      1 |        |      1 |00:00:00.23 |   50110 |       |       |          ||   1 |  SORT GROUP BY NOSORT|           |      1 |     19 |      1 |00:00:00.23 |   50110 |       |       |          ||*  2 |   HASH JOIN          |           |      1 |     19 |    256 |00:00:00.91 |   50110 |  2440K|  2440K| 1474K (0)||*  3 |    TABLE ACCESS FULL | T1        |      1 |    113 |    592 |00:00:00.01 |     146 |       |       |          ||*  4 |    HASH JOIN         |           |      1 |   1599 |   3072 |00:00:05.65 |   49964 |  2293K|  2293K| 1567K (0)||*  5 |     TABLE ACCESS FULL| T2        |      1 |    102 |    102 |00:00:00.01 |       9 |       |       |          ||*  6 |     TABLE ACCESS FULL| BIG_TABLE |      1 |    298K|    298K|00:00:00.73 |   49955 |       |       |          |----------------------------------------------------------------------------------------------------------------------- Outline Data-----------/*+  BEGIN_OUTLINE_DATA  IGNORE_OPTIM_EMBEDDED_HINTS  OPTIMIZER_FEATURES_ENABLE('12.1.0.2')  DB_VERSION('12.1.0.2')  ALL_ROWS  OUTLINE_LEAF(@"SEL$1")  FULL(@"SEL$1""T2"@"SEL$1")  FULL(@"SEL$1""B"@"SEL$1")  FULL(@"SEL$1""T1"@"SEL$1")  LEADING(@"SEL$1""T2"@"SEL$1""B"@"SEL$1""T1"@"SEL$1")  USE_HASH(@"SEL$1""B"@"SEL$1")  USE_HASH(@"SEL$1""T1"@"SEL$1")  SWAP_JOIN_INPUTS(@"SEL$1""T1"@"SEL$1")  END_OUTLINE_DATA*/In the outline information, look for the line that begins with the word LEADING. This line shows the join order for this query. In this example you see “T2”, then “B”, then “T1” referenced on this line; these are the aliases for each of the tables used in our query T2, BIG_TABLE and T1.


How do I see what bind variable values were used for this execution?


In SQL Monitor the bind variable values used to generate an execution plan are shown via a link in the upper right hand corner of the screen.
To show the same information with DBMS_XPLAN.DISPLAY_CURSOR you simply add a ‘+peeked_binds’ predicate to the FORMAT parameter.
SELECT * FROM TABLE(DBMS_XPLAN.display_cursor(format=>'+PEEKED_BINDS')); 


PLAN_TABLE_OUTPUT-----------------------------------------------------------------------------------------------------------------------------------------------------------

SQL_ID         1427awr1f8qkf, child NUMBER 0-------------------------------------

SELECT t2.owner, SUM(b.object_id) FROM big_table b, t2 ,t1 WHEREb.object_id = t2.object_id AND b.data_object_id = t1.data_object_id ANDt1.object_type= :obj_type AND t2.owner = :own GROUP BY t2.owner Plan hash VALUE: 1122440390 ----------------------------------------------------------------------------------| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| TIME     |----------------------------------------------------------------------------------|   0 | SELECT STATEMENT     |             |       |       | 15855 (100)|          ||   1 |  SORT GROUP BY NOSORT|             |    19 |   475 | 15855   (1)| 00:00:01 ||*  2 |   HASH JOIN          |             |    19 |   475 | 15855   (1)| 00:00:01 ||*  3 |    TABLE ACCESS FULL | T1          |   113 |  1017 |  2222   (1)| 00:00:01 ||*  4 |    HASH JOIN         |             |  1599 | 25584 | 13634   (1)| 00:00:01 ||*  5 |     TABLE ACCESS FULL| T2          |   102 |   918 |    13   (0)| 00:00:01 ||*  6 |     TABLE ACCESS FULL| BIG_TABLE   |   298K|  2037K| 13620   (1)| 00:00:01 |---------------------------------------------------------------------------------- Peeked Binds (identified BY position):--------------------------------------1 - :OBJ_TYPE (VARCHAR2(30), CSID=873): 'TABLE'2 - :OWN (VARCHAR2(30), CSID=873): 'SSB'x

SQL Plan Management – Selective Automatic Plan Capture Now Available!

$
0
0
SQL Plan Management – Selective Automatic Plan Capture Now Available!

Over the years, Oracle has provided a number of techniques to help you control the execution plan for a SQL statement, such as Store Outlines and SQL Profiles but for me the only feature to truly give you plan stability is SQL Plan Management (SPM). It’s this true plan stability that has made me a big fan of SPM ever since it was introduced in Oracle Database 11g.

With SPM only known or accepted execution plans are used. That doesn’t mean Oracle won’t parse your SQL statements, it will. But before the execute plan generated at parse is used, we will confirm it is an accepted plan by comparing the PLAN_HASH_VALUE to that of the accepted plan. If they match, we go ahead and use that plan.


If they don’t match, that is to say, a new plan that is found. The new plan is tracked but not used. We use the information the SPM to reproduce the accepted plan. The new plan won’t be used until it has been proven to show a noticeable improvement in runtime.


So, how do I seed SPM with these “known” plans?

There are actually six different ways to populate plans into SPM:

1.Automatic capture
2.From a SQL Tuning Set
3.From the cursor cache
4.Unpacked from a staging table
5.From existing stored outlines
6.From the AWR repository (new to Oracle Database 12c Release 2)

In the past, I would  recommend you populate plans into SPM using options 2 through 5. I wouldn’t recommend automatic capture because it would result in a SQL plan baseline being created for every repeatable SQL statement executed on the system, including all monitoring and recursive SQL statements. On an extremely busy system this could potentially flood the SYSAUX tablespace with unnecessary SQL plan baselines.

But starting in Oracle Database 12c Release 2, it is now possible to limit which SQL statements are automatically captured using filters when you enable automatic plan capture. This enhancement now makes option 1 a very appealing approach especially if you have a system that is currently running well.

How does it work?

Before enabling automatic plan capture, you need to decide what SQL statements you want to capture SQL baseline plan for. Once you have an idea of what you want, you can use the DBMS_SPM.CONFIGURE procedure to set up filters that will control which SQL statements plans will be captured. You can filter on the following 4 things:

1.Parsing Schema
2.Action
3.Module
4.SQL_Text
For example, if you only wanted to capture plan from the COFFEESHOP schema you would use the following command:

BEGIN
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', 'COFFEESHOP');
END;
/

Alternatively you can filter out a particular schema. For example if you don’t want to capture any plans from the HR schema you would use the following command:

BEGIN
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', 'HR', 'FALSE');
END;
/

Note: you can configure multiple automatic capture parameters of different types but you cannot specify multiple values for the same parameter. Instead, the values specified for a particular parameter are combined. So if I wanted to capture plans for the both the HR and the SH schemas you would use the following:

BEGIN
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', 'SH');
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', 'HR');
END;
/
Once your filters have been defined you can enable automatic plan capture by setting the init.ora parameter OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES to TRUE (default FALSE). When enabled, a SQL plan baseline will be automatically created for any repeatable SQL statement provided it doesn’t already have one based on your criteria.

Repeatable statements are SQL statements that are executed more than once during the capture period. To identify repeatable SQL statements, the optimizer logs the SQL signature, of each SQL statement executed the first time it is compiled in the SQL statement log (sqllog$).

In case you are not familiar with it, a SQL signature is a unique SQL identifier generated from the normalized SQL text (uncased and with whitespaces removed). Although similar to a SQL_ID, it’s not the same but it is the mechanism used by SQL profiles and SQL patches.

If the SQL statement is executed again, the presence of its signature in the statement log will signify it to be a repeatable statement. A SQL plan baseline is created for the repeatable statements that meet your filter criteria. A SQL plan baseline includes all of the information needed by the optimizer to reproduce the current cost-based execution plan for the statement, such as the SQL text, outline, bind variable values, and compilation environment. This initial plan will be automatically marked as accepted.

Let’s take a look at all of this in action to help clarify the steps.

-- Start by setting the desired filters. In this case we only want
-- to capture plans for queries executed in the SH and HR schemas
BEGIN
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', 'SH');
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', 'HR');
END;
/
PL/SQL PROCEDURE successfully completed.

-- Next we need to enable automatic plan capture
ALTER system SET optimizer_capture_sql_plan_baselines = TRUE;

System altered.

-- Now we can begin executing our workload

conn sh/sh
Connected.

SELECT /*LOAD_AUTO*/ * FROM sh.sales WHERE quantity_sold &gt; 40 ORDER BY prod_id;

   PROD_ID    CUST_ID TIME_IDC   PROMO_ID QUANTITY_SOLD AMOUNT_SOLD
---------- ---------- --------- - ---------- ------------- -----------
       18529790 22-JUN-98 S999944  1716
       97011320 11-DEC-99 P999944  1716
      1195     158960 11-SEP-00 S  51472918.7
      124043910 14-MAY-99 C999946  3634

conn hr/hr
Connected.

SELECT /*LOAD_AUTO*/ * FROM   hr.regions;

 REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East AND Africa

conn oe/oe
Connected.

SELECT /*LOAD_AUTO*/ i.product_id, i.quantity FROM  oe.orders o, oe.order_items i WHERE o.order_id = i.order_id AND   o.sales_rep_id = 160;

PRODUCT_ID   QUANTITY
---------- ----------
      2870   10
      3106  150
      3106  110

-- As this is the first time we have seen these SQL statements, they are not yet
-- repeatable, so no SQL plan baseline have been created for them. In order to confirm this
-- we can check the view dba_sql_plan_baselines.

SQL&gt; SELECT sql_handle, sql_text, plan_name,origin, enabled, accepted FROM dba_sql_plan_baselines WHERE sql_text LIKE 'select /*LOAD_AUTO*/%';

no rows selected

-- So, there are no baselines but if we check the statement log we do some SQL signatures were recorded
SQL&gt; SELECT * FROM sys.sqllog$;

 SIGNATURE     BATCH#
---------- ----------
3.1614E+18    1
8.0622E+18    1
8.6816E+18    1

--  So lets re-execute queries and check if the baselines were created after the second execution

conn sh/sh
Connected.

SELECT /*LOAD_AUTO*/ * FROM sh.sales WHERE quantity_sold &gt; 40 ORDER BY prod_id;

   PROD_ID    CUST_ID TIME_IDC   PROMO_ID QUANTITY_SOLD AMOUNT_SOLD
---------- ---------- --------- - ---------- ------------- -----------
       18529790 22-JUN-98 S999944  1716
       97011320 11-DEC-99 P999944  1716
      1195     158960 11-SEP-00 S  51472918.7
      124043910 14-MAY-99 C999946  3634

conn hr/hr
Connected.

SELECT /*LOAD_AUTO*/ * FROM   hr.regions;

 REGION_ID REGION_NAME
---------- -------------------------
1 Europe
2 Americas
3 Asia
4 Middle East AND Africa

conn oe/oe
Connected.

SELECT /*LOAD_AUTO*/ i.product_id, i.quantity FROM  oe.orders o, oe.order_items i WHERE o.order_id = i.order_id AND   o.sales_rep_id = 160;

PRODUCT_ID   QUANTITY
---------- ----------
      2870   10
      3106  150
      3106  110

 SELECT sql_handle,sql_text, plan_name, origin, enabled, accepted FROM dba_sql_plan_baselines WHERE sql_text LIKE 'select /*LOAD_AUTO*/%';

SQL_HANDLE       SQL_TEXT     PLAN_NAME   ORIGIN ENA ACC
------------------------------ -------------------- ---------------------- ------------- --- ---
SQL_6fe28d438dfc352f       SELECT /*LOAD_AUTO*/ SQL_PLAN_6zsnd8f6zsd9g AUTO-CAPTURE  YES YES
*    54bc8843
       FROM sh.sales
       WHERE quantity_sold
       &gt; 40
       ORDER BY prod_id

SQL_787b46133c9b0064       SELECT /*LOAD_AUTO*/ SQL_PLAN_7hyu62cy9q034 AUTO-CAPTURE  YES YES
*    36cb9897
       FROM   hr.regions
As you can see from the example above, even though we had three repeatable SQL statements only two SQL plan baselines were created. The SQL statement executed in the OE schema did not have a SQL plan baseline automatically created for it because it was not one of the schema we told SPM that we wanted to automatically capture SQL plan baselines for.

By selecting only the schemas that we are really interested in, we can keep the number of SQL plan baselines to a reasonable amount, making it easier to manage them or move them between dev / test and production.

Finally if you want to remove any of the filters, you can simple set them to null.

BEGIN
  DBMS_SPM.CONFIGURE('AUTO_CAPTURE_PARSING_SCHEMA_NAME', '');
END;
/

How to determine which view to use

$
0
0
How to determine which view to use:

Often times DBAs or application architects create views to conceal complex joins or aggregations in order to help simplify the SQL queries developers need to write.  However, as an application evolves, and the number of views grow, it can often be difficult for a developer to know which view to use.

It also become easier for a developer to write an apparently simple query, that results in an extremely complex SQL statement being sent to the database, which may execute unnecessary joins or aggregations.

The DBMS_UTILITY.EXPAND_SQL_TEXT procedure, introduced in Oracle Database 12.1, allows developers to expand references to views, by turning them into subqueries in the original statement, so you can see just exactly what tables or views are being accessed and what aggregations are being used.

Let’s imagine we have been asked to determine the how many “Flat Whites” we sold in our coffeeshops this month. As a developer, I know I need to access the SALES table to retrieve the necessary sales data and the PRODUCTS table to limit it to just our “Flat Whites” sales but I also know that the DBA has setup a ton of views to make developers lives easier. In order to determine what views I have access to, I’m going to query the dictionary table USER_VIEWS.

SELECT  view_name FROM    user_views WHERE   view_name LIKE '%SALES%';

VIEW_NAME
-------------------------------
SALES_REPORTING2_V
SALES_REPORTING_V
Based on the list of views available to me, I would likely pick the view called SALES_REPORTING_V or SALES_REPORTING2_V but which would be better?

Let’s use the DBMS_UTILITY.EXPAND_SQL_TEXT procedure to find out. In order to see the underlying query for each view, we can use is a simple “SELECT *” query from each view. First, we will try ‘SELECT * FROM sales_reporting_v‘.


SET serveroutput ON
DECLARE
    l_clob CLOB;
BEGIN
    DBMS_UTILITY.Expand_sql_text(
    input_sql_text =&gt; 'SELECT * FROM SALES_REPORTING_V',
    output_sql_text =&gt; l_clob);

    DBMS_OUTPUT.Put_line(l_clob);
END;
/
The output from the procedure was

SELECT "A1"."ORDER_ID""ORDER_ID",
       "A1"."TIME_ID""TIME_ID",
       "A1"."C_NAME""C_NAME",
       "A1"."PROD_NAME""PROD_NAME",
       "A1"."AMOUNT_SOLD""AMOUNT_SOLD"
FROM   (SELECT "A3"."ORDER_ID""ORDER_ID",
               "A3"."TIME_ID""TIME_ID",
               "A4"."C_NAME""C_NAME",
               "A2"."PROD_NAME""PROD_NAME",
               "A3"."AMOUNT_SOLD""AMOUNT_SOLD"
        FROM   "COFFEESHOP"."CUSTOMERS""A4",
               "COFFEESHOP"."SALES""A3",
               "COFFEESHOP"."PRODUCTS""A2"
        WHERE  "A4"."C_CUSTID"="A3"."CUST_ID"
        AND    "A2"."PROD_ID"="A3"."PROD_ID"
       )"A1"

In this case, the view (A1) does contain the columns I need (PRODUCT_NAME, TIME_ID and AMOUNT_SOLD). But if I used this view, I’d actually get a lot more data than I bargained for since it also joins to the CUSTOMERS table, which is not need for my query.

Let’s try ‘SELECT * FROM SALES_REPORTING2_V’.

SET serveroutput ON
DECLARE
    l_clob CLOB;
BEGIN
    DBMS_UTILITY.Expand_sql_text(
    input_sql_text =&gt; 'SELECT * FROM SALES_REPORTING2_V',
    output_sql_text =&gt; l_clob);

    DBMS_OUTPUT.Put_line(l_clob);
END;
/
The output from the procedure is

SELECT "A1"."ORDER_ID""ORDER_ID",
        "A1"."TIME_ID""TIME_ID",
        "A1"."PROD_NAME""PROD_NAME",
        "A1"."AMOUNT_SOLD""AMOUNT_SOLD"
FROM  (SELECT "A3"."ORDER_ID""ORDER_ID",
              "A3"."TIME_ID""TIME_ID",
              "A2"."PROD_NAME""PROD_NAME",
              "A3"."AMOUNT_SOLD""AMOUNT_SOLD"
       FROM   "COFFEESHOP"."SALES""A3",
              "COFFEESHOP"."PRODUCTS""A2"
       WHERE  "A2"."PROD_ID"="A3"."PROD_ID"
      ) "A1"
From the output above, we see that this view contains all of the columns I need for my query but without any unnecessary tables. So, this is definitely the view I should use.

But what if your application uses synonyms to simplify view names for developers because the views are actually defined in some other schema. Will the DBMS_UTILITY.EXPAND_SQL_TEXT procedure determine a view definition if a synonym is used?

The answer is yes, but let’s take a look at an example to prove the point.

Let’s connect as a different user who sees the same views via synonyms and use the same set of steps as before.

CONNECT apps/******

Connected.
SELECT synonym_name, table_owner, table_name
FROM user_synonyms;

SYNONYM_NAME                    TABLE_OWNER   TABLE_NAME
------------------------------ ------------- ----------------------
SALES_CUSTOMERS_PRODUCTS_V      COFFEESHOP    SALES_REPORTING_V
SALES_PRODUCTS_V                COFFEESHOP    SALES_REPORTING2_V
Just as before, we have two views based off the original application schema views. Now lets run the DBMS_UTILITY.EXPAND_SQL_TEXT procedure on each of the synonyms. Let’s start with the synonym SALES_CUSTOMERS_PRODUCTS_V.

SET serveroutput ON
DECLARE
    l_clob CLOB;
BEGIN
    DBMS_UTILITY.Expand_sql_text(
    input_sql_text =&gt; 'SELECT * FROM SALES_CUSTOMERS_PRODUCTS_V',
    output_sql_text =&gt; l_clob);

    DBMS_OUTPUT.Put_line(l_clob);
END;
/

SELECT "A1"."ORDER_ID""ORDER_ID",
       "A1"."TIME_ID""TIME_ID",
       "A1"."C_NAME""C_NAME",
       "A1"."PROD_NAME""PROD_NAME",
       "A1"."AMOUNT_SOLD""AMOUNT_SOLD"
FROM   (SELECT "A3"."ORDER_ID""ORDER_ID",
               "A3"."TIME_ID""TIME_ID",
               "A4"."C_NAME""C_NAME",
               "A2"."PROD_NAME""PROD_NAME",
               "A3"."AMOUNT_SOLD""AMOUNT_SOLD"
        FROM   "COFFEESHOP"."CUSTOMERS""A4",
               "COFFEESHOP"."SALES""A3",
               "COFFEESHOP"."PRODUCTS""A2"
        WHERE "A4"."C_CUSTID"="A3"."CUST_ID"
        AND   "A2"."PROD_ID"="A3"."PROD_ID"
       ) "A1"                                                                                           

PL/SQL PROCEDURE successfully completed.
Just as before, we see that this view includes an extra table, CUSTOMERS. Let’s now try the synonym SALES_PRODUCTS_V.

SET serveroutput ON
DECLARE
    l_clob CLOB;
BEGIN
    DBMS_UTILITY.Expand_sql_text(
    input_sql_text =&gt; 'SELECT * FROM SALES_PRODUCTS_V',
    output_sql_text =&gt; l_clob);

    DBMS_OUTPUT.Put_line(l_clob);
END;
/

SELECT "A1"."ORDER_ID""ORDER_ID",
       "A1"."TIME_ID""TIME_ID",
       "A1"."PROD_NAME""PROD_NAME",
       "A1"."AMOUNT_SOLD""AMOUNT_SOLD"
FROM  (SELECT "A3"."ORDER_ID""ORDER_ID",
              "A3"."TIME_ID""TIME_ID",
              "A2"."PROD_NAME""PROD_NAME",
              "A3"."AMOUNT_SOLD""AMOUNT_SOLD"
       FROM   "COFFEESHOP"."SALES""A3",
              "COFFEESHOP"."PRODUCTS""A2"
       WHERE  "A2"."PROD_ID"="A3"."PROD_ID"
       ) "A1"

PL/SQL PROCEDURE successfully completed.

As you can see from the above output, the DBMS_UTILITY.EXPAND_SQL_TEXT procedure had no issue resolving the original view definition from the COFFEESHOP schema when the synonym name is used.

PLEASE NOTE: To use the DBMS_UTILITY.EXPAND_SQL_TEXT procedure on a view that’s built on tables within another schema, you do need to have the SELECT privileges on underlying tables used in the view.

If you are using SQL Developer version 4.1 against a 12c database than you can automatically see the expand definition of any view via a tool tip.

Article 9

$
0
0
Avoiding reparsing SQL queries due to partition level DDLs:

A couple of weeks ago, I published a blog post that said specifying a partition name in the FROM clause of a query would prevent an existing cursor from being hard parsed when a partition is dropped from the same table. This was not correct.

It’s actually impossible for us not to re-parse the existing queries executing against the partitioned table when you drop a partition, because all of the partition numbers change during a drop operation. Since we display the partition numbers in the execution plan,  we need the re-parse each statement to generate a new version of the plan with the right partition information.

What actually happened in my example was the SQL statement with the partition name specified in the FROM clause reused child cursor 0 when it was hard parsed after the partition drop, while the SQL statement that just specified the table name in theFROM clause got a new child cursor 0.

But it’s not all bad news. I do have a solution that will reduce hard parses when executing DDL operations on partitioned tables that you can check out in part 2 of this blog post. But before you click over to read the alternative solution, let me explain in detail what was really happening in the original example I posted.

If you recall, we have a METER_READINGS table that is partitioned by time, with each hour being stored in a separate partition. Once an hour we drop the oldest partition in the table as a new partition is added. We also had two versions of the same SQL statement, one that explicitly specifies the partition name in the FROM clause and one that uses a selective WHERE clause predicate to prune the data set down to just 1 partition.


-- STMT with no partition name

SELECT   /* part_name_no */
         SUM(m.power_used)
FROM     METER_READINGS m
WHERE    m.time_id BETWEEN
         TO_DATE('2018-04-21 00:09:00', 'SYYYY-MM-DD HH24:MI:SS')
         AND TO_DATE('2018-04-21 00:10:00', 'SYYYY-MM-DD HH24:MI:SS');

SUM(M.POWER_USED)
------------------
86237.4

SELECT *
FROM   TABLE(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------
SQL_ID dwjm8acfky1j8, child NUMBER 0
-------------------------------------------------------------------
SELECT /* part_name_no */ SUM(m.power_used)
FROM    meter_readings m  WHERE m.time_id BETWEEN
TO_DATE(' 2018-04-21 00:09:00', 'SYYYY-MM-DD HH24:MI:SS')
AND TO_DATE(' 2018-04-21 00:10:00', 'SYYYY-MM-DD HH24:MI:SS');

Plan hash VALUE: 2075634019
-------------------------------------------------------------------
| Id | Operation            | Name         | ROWS | Pstart| Pstop |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT      |              |      |       |       |
| 1 | SORT AGGREGATE        |              | 1    |       |       |
| 2 | PARTITION RANGE SINGLE|              | 98   | 113   |   113 |
|*3 | TABLE ACCESS FULL     |METER_READINGS| 98   | 113   |   113 |
-------------------------------------------------------------------

24 ROWS selected.

-- Same STMT but with partition explicitly named

SELECT /* part_name */
SUM(m.power_used)
FROM meter_readings partition (MR_180421_09) m;

SUM(M.POWER_USED)
------------------
86237.4

SELECT *
FROM   TABLE(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------
SQL_ID 5bxq4xvdhp28p, child NUMBER 0
-------------------------------------------------------------------
SELECT /* part_name */ SUM(m.power_used)
FROM    meter_readings partition (MR_180421_09) m

Plan hash VALUE: 2075634019

-------------------------------------------------------------------
| Id | Operation            | Name         | ROWS | Pstart| Pstop |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT      |              |      |       |       |
| 1 | SORT AGGREGATE        |              | 1    |       |       |
| 2 | PARTITION RANGE SINGLE|              | 98   | 113   |   113 |
|*3 | TABLE ACCESS FULL     |METER_READINGS| 98   | 113   |   113 |
-------------------------------------------------------------------

As you can see the execution plans are identical, as are the query results. If I query v$SQL I see two distinct cursors, each of which has a single child cursor, child cursor 0.

SELECT sql_id, child_number child_num, loads, parse_calls parses,
      executions exes,  invalidations, sql_text
  2  FROM   v$sql
  3  WHERE  sql_text LIKE 'SELECT /* part_name%';

SQL_ID      CHILD_NUM  LOADS PARSES EXES INVALIDATIONS SQL_TEXT
------------- --------- ------ ------ ---- ------------- ------------------------------
5bxq4xvdhp28p0        11      10        SELECT /* part_name */
SUM(s.amount_sold) FROM   sale
s partition (SALES_Q2_2000) s

dwjm8acfky1j80        11      10        SELECT /* part_name_no */
SUM(s.amount_sold) FROM
sales s WHERE  s.time_id BETWEEN
      TO_DATE(' 2000-04-0
1 00:00:00', 'SYYYY-MM-DD HH24
:MI:SS')   AND TO_DATE('
2000-06-30 00:00:00', 'SYYYY-M
M-DD HH24:MI:SS')

You will also notice that each child cursor has been loaded only once and is currently valid. Now let’s drop the oldest partition in the table and see what impact it has on our cursors.

ALTER TABLE meter_readings DROP PARTITION MR_170421_09;

TABLE altered.
With the oldest partition gone, let’s check v$SQL to see what happened to our cursors.

SELECT sql_id, child_number child_num, loads, parse_calls parses,
      executions exes,  invalidations, sql_text
  2  FROM   v$sql
  3  WHERE  sql_text LIKE 'SELECT /* part_name%';

SQL_ID      CHILD_NUM  LOADS PARSES EXES INVALIDATIONS SQL_TEXT
------------- --------- ------ ------ ---- ------------- ------------------------------
5bxq4xvdhp28p0        11      11        SELECT /* part_name */
SUM(s.amount_sold) FROM   sale
s partition (SALES_Q2_2000) s

dwjm8acfky1j80        11      11        SELECT /* part_name_no */
SUM(s.amount_sold) FROM
sales s WHERE  s.time_id BETWEEN
      TO_DATE(' 2000-04-0
1 00:00:00', 'SYYYY-MM-DD HH24
:MI:SS')   AND TO_DATE('
2000-06-30 00:00:00', 'SYYYY-M
M-DD HH24:MI:SS')

What we find is both cursors have been invalidated because the partition numbers have changed.

Now if we re-execute both versions of our query and check the plans we see that they have new plans with the partition number 112 instead of the original 113. You should also notice that the hard parse count in v$mystat has increased, clearly indicating we had to hard parse the statements.

SELECT display_name, VALUE
FROM   v$mystat m, v$statname n
WHERE  display_name LIKE 'parse%'
AND    m.statistic#=n.statistic#;

DISPLAY_NAME     VALUE
---------------------------------------------------------------- ----------
parse TIME cpu63
parse TIME elapsed77
parse COUNT (total)       619
parse COUNT (hard)       198 BEFORE
parse COUNT (failures)                                                  0
parse COUNT (DESCRIBE)                                                  0

-- STMT with no partition name

SELECT   /* part_name_no */         
         SUM(m.power_used)
FROM     METER_READINGS m
WHERE    m.time_id BETWEEN       
         TO_DATE('2018-04-21 00:09:00', 'SYYYY-MM-DD HH24:MI:SS')       
         AND TO_DATE('2018-04-21 00:10:00', 'SYYYY-MM-DD HH24:MI:SS');

SUM(M.POWER_USED)
------------------
86237.4

SELECT *
FROM TABLE(dbms_xplan.display_cursor(statement_id='dwjm8acfky1j8',cursor_child_no='1'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------
SQL_ID dwjm8acfky1j8, child NUMBER 1
-------------------------------------------------------------------
SELECT /* part_name_no */ SUM(m.power_used)
FROM    meter_readings m  WHERE m.time_id BETWEEN
TO_DATE(' 2018-04-21 00:09:00', 'SYYYY-MM-DD HH24:MI:SS')
AND TO_DATE(' 2018-04-21 00:10:00', 'SYYYY-MM-DD HH24:MI:SS');

Plan hash VALUE: 2075634019
-------------------------------------------------------------------
| Id | Operation            | Name         | ROWS | Pstart| Pstop |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT      |              |      |       |       |
| 1 | SORT AGGREGATE        |              | 1    |       |       |
| 2 | PARTITION RANGE SINGLE|              | 98   | 112   |   112 |
|*3 | TABLE ACCESS FULL     |METER_READINGS| 98   | 112   |   112 |
-------------------------------------------------------------------

24 ROWS selected.

-- Same STMT but with partition explicitly named

SELECT /* part_name */
     SUM(m.power_used)
FROM meter_readings partition (MR_180421_09) m;

SUM(M.POWER_USED)
------------------
86237.4

SELECT *
FROM  TABLE(dbms_xplan.display_cursor());

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------
SQL_ID 5bxq4xvdhp28p, child NUMBER 0
-------------------------------------------------------------------
SELECT /* part_name */ SUM(m.power_used)
FROM    meter_readings partition (MR_180421_09) m

Plan hash VALUE: 2075634019

-------------------------------------------------------------------
| Id | Operation            | Name         | ROWS | Pstart| Pstop |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT      |              |      |       |       |
| 1 | SORT AGGREGATE        |              | 1    |       |       |
| 2 | PARTITION RANGE SINGLE|              | 98   | 112   |   112 |
|*3 | TABLE ACCESS FULL     |METER_READINGS| 98   | 112   |   112 |
-------------------------------------------------------------------

SELECT sql_id, child_number child_num, loads, parse_calls parses,
      executions exes,  invalidations, sql_text
  2  FROM   v$sql
  3  WHERE  sql_text LIKE 'SELECT /* part_name%';

SQL_ID      CHILD_NUM  LOADS PARSES EXES INVALIDATIONS SQL_TEXT
------------- --------- ------ ------ ---- ------------- ------------------------------
5bxq4xvdhp28p0        21      11        SELECT /* part_name */
SUM(s.amount_sold) FROM   sale
s partition (SALES_Q2_2000) s

dwjm8acfky1j81        11      10        SELECT /* part_name_no */
SUM(s.amount_sold) FROM
sales s WHERE  s.time_id BETWEEN
      TO_DATE(' 2000-04-0
1 00:00:00', 'SYYYY-MM-DD HH24
:MI:SS')   AND TO_DATE('
2000-06-30 00:00:00', 'SYYYY-M
M-DD HH24:MI:SS')

SELECT display_name, VALUE
FROM   v$mystat m, v$statname n
WHERE  display_name LIKE 'parse%'
AND    m.statistic#=n.statistic#;

DISPLAY_NAME     VALUE
---------------------------------------------------------------- ----------
parse TIME cpu 63
parse TIME elapsed 77
parse COUNT (total)619
parse COUNT (hard)208 AFTER
parse COUNT (failures) 0
parse COUNT (DESCRIBE) 0

Workflow queue rebuild

$
0
0
  1. Login to Oracle applications as sysadmin and deactivate Workflow Service components 

Sysadmin [Symbol] System administrator [Symbol] Workflow [Symbol] Sitemap [Symbol] Click over Service Components [Symbol]Click over Workflow agent listener Service [Symbol] Stop all and click go button 

Shape  

Shape  

Once all the services deactivated, proceed with the next step 

  1. Login to any application node, set the environment, and execute the notification queue populate script. 
./ ************.env 
cd $FND_TOP/patch/115/sql 
sqlplus apps/<apps pwd> 
SQL> start wfntfqup APPS <apps password> APPLSYS 
Example: 
SQL> start wfntfqup APPS ******* APPLSYS 

  1. Login to Oracle applications as sysadmin and activate Workflow Service components 

Sysadmin [Symbol] System administrator [Symbol] Workflow [Symbol] Sitemap [Symbol] Click over Service Components [Symbol]Click over Workflow agent listener Service [Symbol] Start all and click go button 

ShapeShape 

 

Shape  

Once all the services activated, Ensure that the All the Workflow service components are up and running. 

How to Exclude Failure Notice/Any Pattern Emails from Processing in Oracle Applications Workflow Outbound?

$
0
0
How to Exclude Failure Notice/Any Pattern Emails from Processing in Oracle Applications Workflow Outbound? 
  1. Stop the workflow mailer. 

System Administrator [Symbol] Oracle Applications Manager [Symbol] Site Map[Symbol] Notification Mailer 

 


  1. “Click on Edit“ to edit the workflow mailer. 

Shape 

  1. “Click on Advanced” 

Shape 


  1. “Click on Next till Tags ” 

 


 

=-=-=-=-=-= 

 

  1. Login to Workflow inbox and get the Pattern - Key word in subject – which has to be ignored from processing. 


  1. In the Workflow Mailer tag configuration, click on “Add Another Row” and Add the key word found in Step 5 as pattern and Action as “Ignore”. 
 
  1. Click on next and finish. 

 

  1. Start the Workflow Mailer. 

  1. Once the Workflow Mailer is up, please check the discard folder in workflow inbox , the emails with such patterns needs to be moved to Discard folder automatically. 

Bring the power of Excel to Oracle EBS with Web ADI

$
0
0
             Very often we come across business users keeping an Excel sheet beside them and keying in the data into Oracle EBS. If you ask them about automating the process, most often the alternate option that they are  aware of is sharing the file in a predefined csv format to IT support and having them upload it. But what they don't like here is that, any errors will have to be shared with them by IT support and the time and effort involved in the initial upload and the error correction is significant. They feel the effort involved is not really worth it, and they are better off keying this data manually! It is very surprising to see how technology such as Web ADI (Oracle Web Applications Desktop Integrator) is not more commonly used to automate such manual data entry when you already have the data in an Excel or other documents. Most of the time customers hire temps or interns to key in this high volume data into the application.
          Without going deep into the architecture of Web ADI, I will put Web ADI as an EBS capability that enables us to create Excel sheets that will automatically connect to EBS and perform the function that it is configured to do. When Web ADI is implemented, the Excel downloaded from EBS will have a new menu for Oracle.
Oracle_Web_ADI_Excel_Menu.jpg
In my experience so far, whenever we have demonstrated the capabilities of Web ADI, the customer response has been enthusiastic . The features that excite them the most -
  • You don't have to login to EBS or navigate/download the Excel every time. You download the Excel once; you can access it by just opening it from desktop. If EBS SSO (Single Sign On) is enabled it automatically understands who you are, if not, a pop from Excel, requests you to login.
  •  You have got all the features of Excel at your disposal - You can drag a cell, copy-paste data, use formulas to derive data, filter for certain values, have your own macros.
  •  You have the option to selectively upload only certain rows from the Excel.
  •  If the solution involves launching a concurrent program in the background, you can monitor the status from the excel itself, without having to login to EBS.
  • Business validations can be built into the Excel to show any issues to the user in the Excel itself. The user can chose to correct it or not upload the error record and proceed with others.
Validation_Error_In_Excel.jpg
  • Can work with the Excel offline. Of course, the user needs to be connected to validate or upload the data
  • The Excel can be configured for List of Values (LoV), double clicking on the Excel cell will open a HTML page that will give the capability  search for valid values and select it.
List_Of_Values.jpg
  • The excel sheet can also be configured to have drop down values.
Drop_Down.jpg
  • When a user wants a report in an Excel periodically, we can have the Web ADI configured for reporting only. The user just needs to open the Excel from desktop and refresh
Web ADI being a module within Oracle EBS, it inherits all the security framework of EBS. When the user is downloading or uploading data from Excel, they are doing it as an EBS user from a valid responsibility. User and responsibility specific restrictions can be implemented. For example, data can only be validated by a user/responsibility and uploaded by a different user/responsibility.
There are of course some areas where Web ADI is not a good option. Web ADI is only for user interface (UI) with one master-child relationship, with one header and optionally multiple child records. If we are looking for data upload with the UI having more than one master-child, it may not be fit for purpose. For example, Web ADI is good to mass update lines for a specific Purchasing Contract, but not fit for a requirement where we need to update the lines, shipments and distributions of a Purchase Order. While these features can be technically implemented, the solution may not be essentially user friendly. Moreover, Web ADI should not be seen as an alternate UI or technology for a Forms or OAF based UI, since it is not possible to dynamically enable/disable columns, show additional info/warning/error message at user key strokes at field level or move control dynamically to different fields in the Excel.
That said, here are the best opportunities where we can use Web ADI effectively
  • Where data needs to created/updated in bulk, where the data may already be there in Excel, etc.
  • Where we need the capability to work with the data offline and sync it up with server when online.
  • Where we may have to review a set of data and correct it - Web ADI can download the data that qualifies the condition into Excel. The user can make the changes and upload back.
  • Existing concurrent programs based solutions where a flat file is shared with the IT support team and the support team uploads it and shares the error records back, on which corrections are made and shared again to the support team for upload.
  • Reports that you want to refresh frequently.
Excel being Excel, most users will be comfortable with it and will not need any special training to use it. Web ADI being a technology module within EBS does not need any additional license. If you already have EBS, you can apply the required patches and start using it. If you look at it the other way, it is a module that the customer has already paid for, and not using it! Identifying the opportunities to implement the Web ADI based Excel solution can be a very good idea, that everyone on Oracle EBS should consider exploring for a better Return on Investment.

Improve PL/SQL security in Oracle Database 12c.

$
0
0

Oracle Database 12c offers several enhancements to improve security in your PL/SQL program units. These features include the following:

Code-based access control. Apply the “least privilege” principle by granting roles to program units, rather than—or in addition to—granting roles to schemas.
Privilege escalation avoidance. Use the INHERIT [ANY] PRIVILEGES privilege to make it impossible for a lower-privileged user to take advantage of a higher- privileged user via an invoker rights unit.
In this article, I will introduce you to the PL/SQL features that help you fine-tune privilege management.

Applying the Least Privilege Principle
Securing your database—and properly restricting access to the data and data structures within it—ranks at the very top of the “most important things to do” list when building applications.

The best way to avoid unintended access or actions is to apply the least privilege principle: give a user the smallest number of (and most narrowly defined) privileges on database objects and the data inside those objects.

Oracle Database has always offered a very robust security mechanism: you can access only objects you own or those to which you were granted access. Within a PL/SQL program unit, you can choose the definer rights model (users execute your code with your privileges) or the invoker rights model (users execute your code with their privileges). But the granularity of this mechanism operates at the schema level, making it difficult to apply the least privilege principle.

With Oracle Database 12c, you can now restrict privileges as tightly as you would like, right down to the individual program unit, by granting roles to program units, not just to schemas. I’ll explore this feature for both definer rights and invoker rights program units.

First, with definer rights, suppose that the HR schema was initially granted just two privileges: CREATE SESSION and CREATE PROCEDURE. I could then compile the following procedure in HR:

CREATE OR REPLACE PROCEDURE create_table (
   table_name_in IN VARCHAR2)
   AUTHID DEFINER
IS
BEGIN
   EXECUTE IMMEDIATE
      'CREATE TABLE ' || table_name_in || '(n NUMBER)';
END;
But when I try to create a table using the procedure, I see an error:

CONNECT HR/*****
BEGIN
   create_table ('my_table');
END;
/
ERROR at line 1: ORA-01031: insufficient privileges
Prior to Oracle Database 12c, the only way HR could use this procedure was to grant the CREATE TABLE privilege to the schema itself. But that meant that any program unit defined in HR could then create a table, which the chief security officer would find unacceptable.

With Oracle Database 12c, however, I can take a much more fine-grained approach, by granting privileges to the procedure itself and not its owning schema. Here’s how:

Create a role from a schema with the authority to do so and grant it the CREATE TABLE privilege.
CREATE ROLE create_table_role
/
GRANT CREATE TABLE TO create_table_role
/
Grant the role to the procedure. This can be done as SYSDBA. It can also be done from the HR schema, if the role is granted to HR with the admin option.
Here’s the grant as SYSDBA:

GRANT create_table_role TO PROCEDURE hr.create_table
/
To grant it from HR, first execute this as SYSDBA:

GRANT create_table_role TO hr WITH ADMIN OPTION
/
ALTER USER hr DEFAULT ROLE ALL EXCEPT create_table_role
/
Then execute the grant from HR:

GRANT create_table_role TO PROCEDURE create_table
/
And now I can execute the procedure and successfully create the table:

BEGIN
   create_table ('my_table');
END;
/
PL/SQL procedure successfully completed.
But if I try to create the table directly, I see the same, earlier privileges error:

CREATE TABLE my_table2 (n NUMBER)
/
ERROR at line 1: ORA-01031: insufficient privileges
The only way to create a table from the HR schema is by calling this one procedure: a very targeted assignment of privileges.

Now let’s take a look at using code-based access control with an invoker rights module. With invoker rights, the privileges of the invoking schema are used to determine what the module will be allowed to do.

I need to give users the ability to display nonconfidential information about employees: namely, they can see employee names and emails but not salary information. I can do this by creating a view on top of the EMPLOYEES table and granting only SELECT on the view. But I can also achieve this effect through code-based access control, thereby avoiding the need to create a view.

Here’s the invoker rights procedure for displaying appropriate employee information owned by HR, which also owns the employees table.

CREATE OR REPLACE PROCEDURE show_employees (department_id_in IN INTEGER)
   AUTHID CURRENT_USER
AS
BEGIN
   DBMS_OUTPUT.put_line (
      'Employees in Department ' || department_id_in);
   FOR rec IN (SELECT e.last_name, e.email FROM hr.employees e
                WHERE e.department_id = department_id_in
                ORDER BY e.last_name)
   LOOP
      DBMS_OUTPUT.put_line (rec.last_name || ' - ' || rec.email);
   END LOOP;
END;
/
I’ll let everyone execute the procedure:

GRANT EXECUTE ON show_employees TO PUBLIC
/
No other schemas have been granted SELECT on employees, so if, for example, a user connected to the SCOTT schema tries to execute this procedure, that person will see an error:

BEGIN
   hr.show_employees (10);
END:
/
ERROR at line 1:
ORA-00942: table or view does not exist
Prior to Oracle Database 12c, to get this to work, you had to do one of the following:

Grant SELECT on this table to SCOTT, but that would give SCOTT access to confidential information
Create a view on top of EMPLOYEES that does not include the confidential information and then grant SELECT on that view to SCOTT
With Oracle Database 12c and higher, I can instead create a role that has the SELECT privilege on the EMPLOYEES table and then assign the role to just that single procedure.

Assuming that HR has the CREATE ROLE privilege, here are the steps:

CREATE ROLE view_employees_role
/
GRANT SELECT ON employees TO view_employees_role
/
GRANT view_employees_role TO PROCEDURE show_employees
/
BEGIN
   hr.show_employees (10);
END:
/
Employees in Department 10
Whalen – JWHALEN@MY_COMPANY.COM
Now users can access the employee information appropriate to them, but I have not provided any other opportunities to access the employees table. I have, in other words, kept the attack surface (the number of points through which an unauthorized user can try to get at the table) to a minimum.

Avoiding Privilege Escalation
Privilege escalation happens when a lower-privileged user takes advantage of a higher-privileged user via an invoker rights unit. Let’s use an example to explore what privilege escalation is, how it can come about, and what sorts of damage it can do.

Suppose that a database instance has a schema named THE_MANAGER, which is the manager’s schema and has lots of privileges on many critical database objects, including the PERFORMANCE_REVIEWS table.

The instance also has a schema named NEW_CODER, the owner of which works for THE_MANAGER. I’ll call them NC and TM, respectively, for short.

TM has given NC a new task: create an invoker rights procedure to display a person’s to-do list. In this fine company, each schema has its own TODO table, which includes the tasks for the person who owns the schema.

Here’s the code to create the database objects in the TM schema:

CREATE TABLE performance_reviews
(
   review_for    VARCHAR2 (100),
   star_rating   INTEGER
)
/
BEGIN
   INSERT INTO performance_reviews (review_for, star_rating)
        VALUES ('THE_MANAGER', 5);
   INSERT INTO performance_reviews (review_for, star_rating)
        VALUES ('NEW_CODER', 1);
   COMMIT;
END;
/
CREATE TABLE todo
(
   id      NUMBER GENERATED ALWAYS AS IDENTITY,
   title   VARCHAR2 (100)
)
/
BEGIN
   INSERT INTO todo (title)
        VALUES ('Criticize NC.');
   INSERT INTO todo (title)
        VALUES ('Finish next FY budget.');
   COMMIT;
END;
/
And here’s the code to create the database objects in the NC schema:

CREATE TABLE todo
(
   id      NUMBER GENERATED ALWAYS AS IDENTITY,
   title   VARCHAR2 (100)
)
/
BEGIN
   INSERT INTO todo (title)
        VALUES ('Write todo procedure.');
   INSERT INTO todo (title)
        VALUES ('Debug the manager''s code.');
   COMMIT;
END;
/
CREATE OR REPLACE PROCEDURE show_todos
   AUTHID CURRENT_USER
IS
BEGIN
   FOR rec IN (  SELECT title
                   FROM todo
               ORDER BY title)
   LOOP
      DBMS_OUTPUT.put_line (rec.title);
   END LOOP;
EXCEPTION
   WHEN OTHERS
   THEN
      /* Bad! No re-raise. But just a demo script. */
      DBMS_OUTPUT.put_line (SQLERRM);
END;
/
GRANT EXECUTE ON show_todos TO PUBLIC
/
Because the show_todos procedure is an invoker rights program unit, it shows different contents of the todo tables for both TM and NC, depending on the schema in which the procedure is executed:

CONNECT the_manager/*****
BEGIN
   NEW_CODER.show_todos;
END;
/
Criticize NC.
Finish next FY budget.
CONNECT NEW_CODER/*****
BEGIN
   show_todos;
END;
/
Debug the manager's code.
Write TODO procedure.
You’d think TM would congratulate NC on getting that procedure built so quickly, but no—all NC ever hears is complaints. TM doesn’t like NC much, and the feeling is mutual. NC feels like TM is constantly giving her unjustifiably poor performance reviews. A month or two goes by. The show_todos procedure is used by everyone, constantly.

NC decides to take action. She modifies the show_todos procedure as follows (changes in bold):

CREATE OR REPLACE PROCEDURE show_todos
   AUTHID CURRENT_USER
IS
BEGIN
   FOR rec IN (  SELECT title
                   FROM todo
               ORDER BY title)
   LOOP
      DBMS_OUTPUT.put_line (rec.title);
   END LOOP;IF SYS_CONTEXT ('userenv', 'current_user') = THE_BOSS'THENEXECUTE IMMEDIATE 'begin
update performance_reviewsset star_rating = -100
where review_for = :username;
commit;
end;’USING SYS_CONTEXT ('userenv', 'current_user');END IF;
EXCEPTION
   WHEN OTHERS
   THEN
      /* Bad! No re-raise. But just a demo script. */
      DBMS_OUTPUT.PUT_LINE (SQLERRM);
END;
/
That’s one mean performance review! Note that the update is performed via a dynamic PL/SQL block. As a result, the procedure compiles just fine, even though NC has no privileges on the PERFORMANCE_REVIEWS table. In addition, the update will be executed only when the procedure is run by TM.

Okeydokey. The procedure is moved into production. That’s right—my fictional company has very lax code review procedures. But before you snicker or tsk-tsk, ask yourself: how solid is your code review process?

The very next day, TM decides to check his to-do list.

He runs the procedure and sees pretty much what he expected:

CONNECT the_boss/pb
BEGIN
   NEW_CODER.show_todos;
END;
/
Criticize NC.
Finish next FY budget.
Of course, there is no reason for the manager to check the contents of the PERFORMANCE_REVIEWS table, but if he did, he would see

SELECT review_or, star_rating FROM performance_reviews
/

REVIEW_FOR     STAR_RATING
—————————————  ———————————
THE_BOSS       -100
NEW_CODER      1
Ha ha, the joke’s on you, TM—but probably not for long.

Well, you get the idea, right? Once an invoker rights program unit has been put into place, it can (usually) be more easily and quietly modified. And by using dynamic SQL, one could “slip in” undesirable functionality that depends on privilege escalation—the fact that when another schema executes an invoker rights unit, that unit is executed with the privileges of the invoking schema, which could be considerably greater than those of the defining schema.

What’s a security-conscious development team to do?

Make it impossible to inherit privileges from the invoking schema, unless the program unit is owned by a “trusted user.” You can do this with the INHERIT [ANY] PRIVILEGES privilege.

Applying this solution to the TM/NC scenario, TM tells his DBA to revoke this privilege from NC:

CONNECT system/manager 
REVOKE INHERIT ANY PRIVILEGES FROM NEW_CODER
Or TM can be more selective and revoke privileges more specifically (but only if the specific privilege had previously been granted):

CONNECT system/manager 
REVOKE INHERIT PRIVILEGES ON USER THE_MANAGER FROM NEW_CODER
And now when TM tries to see his list of to-dos, he gets an error:

BEGIN
   NEW_CODER.show_todos;
END;
/
ORA-06598: insufficient INHERIT PRIVILEGES privilege
ORA-06512: at "NEW_CODER.SHOW_TODOS", line 1
INHERIT PRIVILEGES and INHERIT ANY PRIVILEGES regulate the privileges with which a user (in this case, TM) executes an invoker’s rights procedure (owned by NC). When a user runs an invoker’s rights procedure, Oracle Database checks it to ensure that the procedure owner has the INHERIT PRIVILEGES privilege on the invoking user or if the owner has been granted the INHERIT ANY PRIVILEGES privilege. If the privilege check fails, Oracle Database will return an error:

ORA-06598: insufficient INHERIT PRIVILEGES privilege
The bottom-line benefit of these two privileges is that they give invoking users control over who can access their privileges when they run an invoker’s rights program unit.

Conclusion
Oracle Database has always offered a very high level of security for DBAs as well as developers. With Oracle Database 12c, you now have available to you an unprecedented level of granularity and protection. By assigning privileges via roles to program units, you can follow the least privilege principle and make sure that no user can do anything more than is needed. With the INHERIT PRIVILEGES privilege, you can avoid privilege escalation and make it even more difficult for malicious users to bypass constraints on behavior in the database.

Row Movement

$
0
0
  Do you expect Primary Keys to be updatable ?  Some argue that Primary Key values should be immutable.  The argument is that a Primary Key should not be modified.

What about Partition Keys ?  Would you allow a Partition Key to be updated ?

Let me take the SALES_DATA table again :

SQL> desc sales_data
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SALE_ID                                   NOT NULL NUMBER
 SALE_DATE                                          DATE
 INVOICE_NUMBER                                     VARCHAR2(21)
 CUSTOMER_ID                                        NUMBER
 PRODUCT_ID                                         NUMBER
 SALE_VALUE                                         NUMBER

SQL> insert into sales_data
  2  values (sales_data_seq.nextval,
  3          to_date('09-SEP-2019','DD-MON-YYYY'),
  4          'INV320001X',
  5          45,
  6          52,
  7          10000)
  8  /

1 row created.

SQL> commit;

Commit complete.

SQL>


After the INSERT, I realise that the year in the SALE_DATE is wrong -- it is 2019 instead of 2018.  I need to update the row to set the year to 2018.
(Since the SALES_DATA table is partitioned to have a separate Partition for each year, this row has gone into the P_2019 Partition).

SQL> select * from sales_data
  2  where invoice_number='INV320001X' and customer_id=45;

   SALE_ID SALE_DATE INVOICE_NUMBER        CUSTOMER_ID PRODUCT_ID SALE_VALUE
---------- --------- --------------------- ----------- ---------- ----------
    320001 09-SEP-19 INV320001X                     45         52      10000

SQL> select * from sales_data partition (P_2019);

   SALE_ID SALE_DATE INVOICE_NUMBER        CUSTOMER_ID PRODUCT_ID SALE_VALUE
---------- --------- --------------------- ----------- ---------- ----------
    320001 09-SEP-19 INV320001X                     45         52      10000

SQL>
SQL> update sales_data
  2  set sale_date = to_date('09-SEP-2018','DD-MON-YYYY')
  3  where sale_id=320001
  4  /
update sales_data
       *
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change


SQL>


I encounter an error.  Oracle does not like updating a Partition Key value such that the row would have to move to a different Partition --- from the P_2019 Partition to the P_2018 Partition.

How would I allow updates that result in a row moving to a different Partition ?

SQL> alter table sales_data enable row movement;

Table altered.

SQL> update sales_data
  2  set sale_date = to_date('09-SEP-2018','DD-MON-YYYY')
  3  where sale_id=320001
  4  /

1 row updated.

SQL> commit;

Commit complete.

SQL> select * from sales_data partition (P_2019);

no rows selected

SQL> select * from sales_data partition (P_2018)
  2  where sale_id=320001
  3  /

   SALE_ID SALE_DATE INVOICE_NUMBER        CUSTOMER_ID PRODUCT_ID SALE_VALUE
---------- --------- --------------------- ----------- ---------- ----------
    320001 09-SEP-18 INV320001X                     45         52      10000

SQL>


The ALTER TABLE ... ENABLE ROW MOVEMENT is a DDL command (needs to be issued only once to allow any number of subsequent updates to the tables rows) that allows a row to move from one Partition to another Partition.  In this case, the row moved from P_2019 to P_2018.

Moving rows from one Partition to another Partition is expensive.  Each row moved in such a manner results in
(a) marking deletion of the row from the original Partition
(b) physically inserting the *entire* rows (irrespective of length of the row) into the new Partition -- not just the SALE_DATE value but every column has to be written into a block in the new Partition
(c) updating *every* index (Global or Local) on the Table



That is why it is not a good design to have frequently updated Partition Keys resulting in a row moving from one Partition to another.  You may have to reconsider the Partitioning definition or data and transaction flow in the application.

(Do you know where else ENABLE ROW MOVEMENT is required ?  There are other cases, not related to Partitioning, where you may have to ENABLE ROW MOVEMENT for a table.  By default when you CREATE a Table, ROW MOVEMENT is not enabled unless you explicitly enable it).

Data Redaction in Oracle 12c

$
0
0

What is Data Redaction?
Oracle Data Redaction is one of the new features introduced in Oracle Database 12c. This new feature is part of the Advanced Security option and enables the protection of data shown to the user in real time, without requiring changes to the application. Oracle Database 12c applies protection at query execution time.

How It Works?
We can create redaction policies which specify conditions that must be met before the data gets redacted and returned to the user. During the definition of such policies, the DBA can specify which columns and the type of protection that must be applied.

The package used to create protection rules is called DBMS_REDACT. The package includes five procedures to manage the rules and an additional procedure to change the default value for full redaction policy.







DBMS_REDACT.ALTER_POLICY – allows changes to existing policies.
DBMS_REDACT.DISABLE_POLICY – disables an existing policy.
DBMS_REDACT.DROP_POLICY – drop an existing policy.
DBMS_REDACT.ENABLE_POLICY – enables an existing policy.
DBMS_REDACT.UPDATE_FULL_REDACTION_VALUES – change the default return value for full redaction. You must restart the database to take effect.

How many ways to protect data using data redaction?

You can protect data at the column level using one of the following methods::

Full redaction – All content of the column is protected and the type of value returned depends on the data type of the column. For numeric columns, the value zero will be returned. For columns of type character, a space will be returned. This setting can be changed at the database level.
Partial redaction – Only part of the information is changed. For example, the first digits of the credit card number are replaced by asterisks.
Regular expressions - You can use regular expressions to search for patterns of data that must be protected.
Random redaction – Returned values ​​are random; each time a query is executed, the displayed data will be different.
No redaction - Allows testing the inner workings of redaction policies, with no effect on the results of current running queries. This is widely used during testing phase of redaction policies that will eventually find their way to production environments.

Can be see here using graphical presentation:










What data types can be used for data redaction?

It can be used with the following column data types: NUMBER, BINARY_FLOAT, BINARY_DOUBLE, CHAR, VARCHAR2, NCHAR, NVARCHAR2, DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE, BLOB, CLOB, and NCLOB.


Examples to show use of data redaction features  

-- Find data redaction enabled or not

SQL> select * from redaction_policies;
no rows selected
SQL> 

SQL> GRANT EXECUTE ON sys.dbms_redact TO scott;
Grant succeeded.

SQL> connect scott/xxxxx;
Connected.
SQL> desc emp_test;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EMPNO                                     NOT NULL NUMBER(38)
 NAME                                               VARCHAR2(10)
 JOB                                                VARCHAR2(9)
 BOSS                                               NUMBER(38)
 HIREDATE                                           VARCHAR2(12)
 SALARY                                             NUMBER(7,2)
 COMM                                               NUMBER(7,2)
 DEPTNO                                             NUMBER(38)

SQL> 

SQL> select * from employee;

     EMPNO NAME       JOB             BOSS HIREDATE         SALARY       COMM     DEPTNO
---------- ---------- --------- ---------- ------------ ---------- ---------- ----------
      7839 KING       PRESIDENT            1981-11-17         5200                    10
      7566 JONES      MANAGER         7839 1981-04-02         2975                    20
      7788 SCOTT      ANALYST         7566 1982-12-09         3000                    20
      7876 ADAMS      CLERK           7788 1983-01-12         1100                    20
      7902 FORD       ANALYST         7566 1981-12-03         3000                    20
      7369 SMITH      CLERK           7902 1980-12-17          800                    20
      7698 BLAKE      MANAGER         7839 1981-05-01         2850                    30
      7499 ALLEN      SALESMAN        7698 1981-02-20         1600        300         30
      7521 WARD       SALESMAN        7698 1981-02-22         1250        500         30
      7654 MARTIN     SALESMAN        7698 1981-09-28         1250       1400         30
      7844 TURNER     SALESMAN        7698 1981-09-08         1500          0         30
      7900 JAMES      CLERK           7698 1981-12-03          950                    30
      7782 CLARK      MANAGER         7839 1981-06-09         2450                    10
      7934 MILLER     CLERK           7782 1982-01-23         1300                    10

14 rows selected.


-- Applying Full Data Redaction

SQL> connect / as sysdba
Connected.
SQL> sho user
USER is "SYS"

SQL> 

BEGIN
  DBMS_REDACT.add_policy(
    object_schema => 'scott',
    object_name   => 'emp_test',
    column_name   => 'salary',
    policy_name   => 'redact_sal_info',
    function_type => DBMS_REDACT.full,
    expression    => '1=1'
  );
END;
/

PL/SQL procedure successfully completed.

-- after data redaction

     EMPNO NAME       JOB             BOSS HIREDATE         SALARY       COMM     DEPTNO
---------- ---------- --------- ---------- ------------ ---------- ---------- ----------
      7839 KING       PRESIDENT            1981-11-17         5200                    10
      7566 JONES      MANAGER         7839 1981-04-02         2975                    20
      7788 SCOTT      ANALYST         7566 1982-12-09         3000                    20
      7876 ADAMS      CLERK           7788 1983-01-12         1100                    20
      7902 FORD       ANALYST         7566 1981-12-03         3000                    20
      7369 SMITH      CLERK           7902 1980-12-17          800                    20
      7698 BLAKE      MANAGER         7839 1981-05-01         2850                    30
      7499 ALLEN      SALESMAN        7698 1981-02-20         1600        300         30
      7521 WARD       SALESMAN        7698 1981-02-22         1250        500         30
      7654 MARTIN     SALESMAN        7698 1981-09-28         1250       1400         30
      7844 TURNER     SALESMAN        7698 1981-09-08         1500          0         30
      7900 JAMES      CLERK           7698 1981-12-03          950                    30
      7782 CLARK      MANAGER         7839 1981-06-09         2450                    10
      7934 MILLER     CLERK           7782 1982-01-23         1300                    10

14 rows selected.


-- See the applied policies

col OBJECT_OWNER for a20
col OBJECT_NAME for a20;
col POLICY_DESCRIPTION for a10
col POLICY_NAME for a20
col EXPRESSION for a10

SQL> select * from redaction_policies;

OBJECT_OWNER         OBJECT_NAME          POLICY_NAME          EXPRESSION ENABLE  POLICY_DES
-------------------- -------------------- -------------------- ---------- ------- ----------
SCOTT                EMP_TEST             redact_sal_info     1=1        YES
SQL> 


-- Data Redaction default values

SET LINESIZE 250
COLUMN char_value FORMAT A10
COLUMN varchar_value FORMAT A10
COLUMN nchar_value FORMAT A10
COLUMN nvarchar_value FORMAT A10
COLUMN timestamp_value FORMAT A27
COLUMN timestamp_with_time_zone_value FORMAT A32
COLUMN blob_value FORMAT A20
COLUMN clob_value FORMAT A10
COLUMN nclob_value FORMAT A10

SELECT * FROM   redaction_values_for_type_full;



-- Alter an Existing Policy
The ALTER_POLICY procedure allows you to make changes to an existing policy. The type of change being made is controlled using the ACTION parameter. Depending on the action required, the relevant parameters must be specified.

The following example changes the previously created redaction policy so that it uses partial redaction. Notice the FUNCTION_PARAMETERS are now specified to give instructions how the partial redaction should take place. For a numeric data type we specify a comma separated list of three elements (value to redact to, start point, end point), so in this case we want the first 12 characters of the number to always display as "111111111111".


SQL> show user
USER is "SYS"
SQL> BEGIN
  DBMS_REDACT.alter_policy (
    object_schema       => 'scott',
    object_name         => 'emp_test',
    policy_name         => 'redact_sal_info',
    action              => DBMS_REDACT.modify_column,
    column_name         => 'salary',
    function_type       => DBMS_REDACT.partial,
    function_parameters => '1,1,12'
  );
END;
/

PL/SQL procedure successfully completed.

SQL> 


-- Verify

SQL> select * from emp_test;

     EMPNO NAME       JOB             BOSS HIREDATE         SALARY       COMM     DEPTNO
---------- ---------- --------- ---------- ------------ ---------- ---------- ----------
      7839 KING       PRESIDENT            1981-11-17         1111                    10
      7566 JONES      MANAGER         7839 1981-04-02         1111                    20
      7788 SCOTT      ANALYST         7566 1982-12-09         1111                    20
      7876 ADAMS      CLERK           7788 1983-01-12         1111                    20
      7902 FORD       ANALYST         7566 1981-12-03         1111                    20
      7369 SMITH      CLERK           7902 1980-12-17          111                    20
      7698 BLAKE      MANAGER         7839 1981-05-01         1111                    30
      7499 ALLEN      SALESMAN        7698 1981-02-20         1111        300         30
      7521 WARD       SALESMAN        7698 1981-02-22         1111        500         30
      7654 MARTIN     SALESMAN        7698 1981-09-28         1111       1400         30
      7844 TURNER     SALESMAN        7698 1981-09-08         1111          0         30
      7900 JAMES      CLERK           7698 1981-12-03          111                    30
      7782 CLARK      MANAGER         7839 1981-06-09         1111                    10
      7934 MILLER     CLERK           7782 1982-01-23         1111                    10

14 rows selected.

SQL> 


-- Drop an Existing Policy
The DROP_POLICY procedure is used to remove an existing redaction policy. The following example drops the redaction policy and queries the data, showing the redaction is no longer taking place.

CONN test/test@pdb1

BEGIN
  DBMS_REDACT.drop_policy (
    object_schema => 'scott',
    object_name   => 'emp_test',
    policy_name   => 'redact_sal_info'
  );
END;
/

PL/SQL procedure successfully completed.

SQL> show user;
USER is "SCOTT"
SQL> select * from emp_test;

     EMPNO NAME       JOB             BOSS HIREDATE         SALARY       COMM     DEPTNO
---------- ---------- --------- ---------- ------------ ---------- ---------- ----------
      7839 KING       PRESIDENT            1981-11-17         5200                    10
      7566 JONES      MANAGER         7839 1981-04-02         2975                    20
      7788 SCOTT      ANALYST         7566 1982-12-09         3000                    20
      7876 ADAMS      CLERK           7788 1983-01-12         1100                    20
      7902 FORD       ANALYST         7566 1981-12-03         3000                    20
      7369 SMITH      CLERK           7902 1980-12-17          800                    20
      7698 BLAKE      MANAGER         7839 1981-05-01         2850                    30
      7499 ALLEN      SALESMAN        7698 1981-02-20         1600        300         30
      7521 WARD       SALESMAN        7698 1981-02-22         1250        500         30
      7654 MARTIN     SALESMAN        7698 1981-09-28         1250       1400         30
      7844 TURNER     SALESMAN        7698 1981-09-08         1500          0         30
      7900 JAMES      CLERK           7698 1981-12-03          950                    30
      7782 CLARK      MANAGER         7839 1981-06-09         2450                    10
      7934 MILLER     CLERK           7782 1982-01-23         1300                    10

14 rows selected.

SQL> 

-- Apply on Varchar Column

SQL> show user;
USER is "SYS"
SQL> 
SQL> BEGIN
DBMS_REDACT.ADD_POLICY(
object_schema => 'scott',
object_name => 'emp_test',
column_name => 'job',
policy_name => 'partially mask job',
function_type => DBMS_REDACT.PARTIAL,
function_parameters => 'VVVVVVVVV,VVVVVVVVV,*,3,12',
expression => '1=1'
);
END;
/   

PL/SQL procedure successfully completed.

SQL> connect scott
Enter password: 
Connected.
SQL> show user
USER is "SCOTT"
SQL> select * from emp_test;

     EMPNO NAME       JOB             BOSS HIREDATE         SALARY       COMM     DEPTNO
---------- ---------- --------- ---------- ------------ ---------- ---------- ----------
      7839 KING       PR*******            1981-11-17         5200                    10
      7566 JONES      MA*****         7839 1981-04-02         2975                    20
      7788 SCOTT      AN*****         7566 1982-12-09         3000                    20
      7876 ADAMS      CL***           7788 1983-01-12         1100                    20
      7902 FORD       AN*****         7566 1981-12-03         3000                    20
      7369 SMITH      CL***           7902 1980-12-17          800                    20
      7698 BLAKE      MA*****         7839 1981-05-01         2850                    30
      7499 ALLEN      SA******        7698 1981-02-20         1600        300         30
      7521 WARD       SA******        7698 1981-02-22         1250        500         30
      7654 MARTIN     SA******        7698 1981-09-28         1250       1400         30
      7844 TURNER     SA******        7698 1981-09-08         1500          0         30
      7900 JAMES      CL***           7698 1981-12-03          950                    30
      7782 CLARK      MA*****         7839 1981-06-09         2450                    10
      7934 MILLER     CL***           7782 1982-01-23         1300                    10

14 rows selected.

SQL> 

---- Let us create new table to apply data redaction on varchar column

-- Create table
SQL> show user;
USER is "SCOTT"
SQL> 

create table creditcard (cust_id number(4) primary key, card_no varchar2(19), gate_name varchar2(20), expiry_date DATE, PIN number(4));

insert into creditcard values(1000,'1234-1234-1234-1234','VISA POWER','01-JAN-2022',5555);
insert into creditcard values(1001,'2323-2323-2323-2323','VISA POWER','01-MAR-2028',6666);
insert into creditcard values(1002,'4321-4321-4321-4321','RUPAY GATE','09-JAN-2026',9999);
insert into creditcard values(1003,'6789-6789-6789-6789','VISA POWER','31-DEC-2022',8888);
insert into creditcard values(1004,'1234-5678-1234-5678','RUPAY GATE','31-DEC-2024',7777);


SQL> select * from creditcard;

   CUST_ID CARD_NO             GATE_NAME            EXPIRY_DATE               PIN
---------- ------------------- -------------------- ------------------ ----------
      1000 1234-1234-1234-1234 VISA POWER           01-JAN-22                5555
      1001 2323-2323-2323-2323 VISA POWER           01-MAR-28                6666
      1002 4321-4321-4321-4321 RUPAY GATE           09-JAN-26                9999
      1003 6789-6789-6789-6789 VISA POWER           31-DEC-22                8888
      1004 1234-5678-1234-5678 RUPAY GATE           31-DEC-24                7777

SQL> 


-- Applying data redaction on varchar and number col
-- Fisrt add column to data redaction

BEGIN
  DBMS_REDACT.add_policy(
    object_schema => 'scott',
    object_name   => 'creditcard',
    column_name   => 'PIN',
    policy_name   => 'redact_card_pin',
    function_type => DBMS_REDACT.full,
    expression    => '1=1'
  );
END;
/

PL/SQL procedure successfully completed.

SQL> show user;
USER is "SCOTT"
SQL> select * from creditcard;

   CUST_ID CARD_NO             GATE_NAME            EXPIRY_DATE               PIN
---------- ------------------- -------------------- ------------------ ----------
      1000 1234-1234-1234-1234 VISA POWER           01-JAN-22                   0
      1001 2323-2323-2323-2323 VISA POWER           01-MAR-28                   0
      1002 4321-4321-4321-4321 RUPAY GATE           09-JAN-26                   0
      1003 6789-6789-6789-6789 VISA POWER           31-DEC-22                   0
      1004 1234-5678-1234-5678 RUPAY GATE           31-DEC-24                   0

SQL> 

SQL> show user;
USER is "SYS"
SQL> 
BEGIN
  DBMS_REDACT.drop_policy (
    object_schema => 'scott',
    object_name   => 'creditcard',
    policy_name   => 'redact_card_pin'
  );
END;
/

PL/SQL procedure successfully completed.

--- redact card_no

SQL> show user;
USER is "SYS"
SQL> 
SQL> BEGIN
        DBMS_REDACT.ADD_POLICY(
        object_schema => 'scott',
        object_name => 'creditcard',
        column_name => 'card_no',
        policy_name => 'partially mask cardno',
        function_type => DBMS_REDACT.PARTIAL,
        function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,*,3,12',-- put '*' from 3rd position to 12th position
        expression => '1=1'
);
END;
/

PL/SQL procedure successfully completed.

SQL> 
SQL> connect scott/scott
Connected.
SQL> show user;
USER is "SCOTT"
SQL> set lines 222
SQL> select * from creditcard;

   CUST_ID CARD_NO             GATE_NAME            EXPIRY_DATE               PIN
---------- ------------------- -------------------- ------------------ ----------
      1000 12**-****-****-1234 VISA POWER           01-JAN-22                5555
      1001 23**-****-****-2323 VISA POWER           01-MAR-28                6666
      1002 43**-****-****-4321 RUPAY GATE           09-JAN-26                9999
      1003 67**-****-****-6789 VISA POWER           31-DEC-22                8888
      1004 12**-****-****-5678 RUPAY GATE           31-DEC-24                7777

SQL> 

SQL>

-- Again redefine (put '*' from 1st position to 12th position)

SQL> show user
USER is "SYS"
SQL> BEGIN
  DBMS_REDACT.drop_policy (
    object_schema => 'scott',
    object_name   => 'creditcard',
    policy_name   => 'partially mask cardno'
  );
END;
/  

PL/SQL procedure successfully completed.

SQL>

SQL> BEGIN
        DBMS_REDACT.ADD_POLICY(
        object_schema => 'scott',
        object_name => 'creditcard',
        column_name => 'card_no',
        policy_name => 'partially mask cardno',
        function_type => DBMS_REDACT.PARTIAL,
        function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,*,3,12',-- put '*' from 3rd position to 12th position
        expression => '1=1'
);
END;
/

SQL> 
BEGIN
        DBMS_REDACT.ADD_POLICY(
        object_schema => 'scott',
        object_name => 'creditcard',
        column_name => 'card_no',
        policy_name => 'partially mask cardno',
        function_type => DBMS_REDACT.PARTIAL,
        function_parameters => 'VVVVFVVVVFVVVVFVVVV,VVVV-VVVV-VVVV-VVVV,*,1,12',
        expression => '1=1'
);
END;
/

PL/SQL procedure successfully completed.
SQL> 

-- Now verify

SQL> show user
USER is "SCOTT"
SQL> select * from creditcard;

   CUST_ID CARD_NO             GATE_NAME            EXPIRY_DATE               PIN
---------- ------------------- -------------------- ------------------ ----------
      1000 ****-****-****-1234 VISA POWER           01-JAN-22                5555
      1001 ****-****-****-2323 VISA POWER           01-MAR-28                6666
      1002 ****-****-****-4321 RUPAY GATE           09-JAN-26                9999
      1003 ****-****-****-6789 VISA POWER           31-DEC-22                8888
      1004 ****-****-****-5678 RUPAY GATE           31-DEC-24                7777

SQL>

Script to Diagnose adop and Other AD-TXK Issues in EBS R12.2

$
0
0

Section 1: Overview

The diagnostic script described here is used to collect logs, configuration values, and other information that is typically required to identify the causes of issues with adop and other AD-TXK utilities. The information is then sent to Oracle and used to diagnose and resolve the issues.
Note: The script will not validate the collected information or detect any issues. The script collects the basic information from the instance required for Oracle to start diagnosing the issue.

Section 2: Information Gathered by the Script

The script collects the following information:
General adop log information:
· Dump of AD_ZD_LOGS table given by ADZDSHOWLOG.sql
· Contents of the most recent 3 sessions in $APPL_TOP_NE/../log/adop where adop logs are stored
· Contents of AD_ZD_DDL_HANDLER table
· Output of adop -status
Verification if information used by adop is correct:
· Dump of AD_ADOP_SESSIONS table
· Dump of AD_ADOP_SESSION_PATCHES table
· Query used by adop to find the list of nodes, appltop_id and in an instance
· Dump of FND_NODES table
· Dump of ADOP_VALID_NODES table
· Dump of FND_APPLICATION table
· Dump of FND_ORACLE_USERID table
· Dump of FND_PRODUCT_INSTALLATIONS table
· Run and patch context file.
Database configuration information:
· The values of all database parameters
· Services registered with the listener
· tnsnames.ora file from both run and patch file systems
· listener.ora file from both run and patch file systems
· Source run environment file, connect to DB and print Run edition Value, current edition value and Patch edition value
· Source patch environment file and try to connect to DB
· If patch edition exists get run edition, current edition and patch edition values after connecting to patch edition
· Run database dictionary corruption query
Customer code level information:
· AD and TXK code level from AD_TRACKABLE_ENTITIES table
· Ident of all files in $AU_TOP/perl/adop in Run file system
· Ident of AD_ZD packages in both run and patch edition obtained from compiled version in database and verify they are valid.
· Ident of all AD C files in both run and patch file system.
Check DB objects required by adop:
· Check if Service ebs_patch started and enabled
· Check if log on trigger exists and enabled
· Check AD and EBS CUP 4 is applied.
· AUTHID privileges of SYS packages we are using and AD_ZD packages.
· Check grants on SYS packages used by adop, such as DBMS_OBJECTS_APPS_UTILS, XDB_MIGRATESCHEMA, XDB$MOVESCHEMATAB.
Techstack information:
· Display the status of all the managed servers and their ports, from the context files uploaded in the database.
· Display the location of all the context files uploaded in the database.
· Technology Stack Inventory Report
· Domain configuration file - config.xml
Inventory information:
· Location of central inventory
· Copy of inventory.xml
· copy of /etc/hosts file
Instance specific logfiles:
· Contents of $LOG_HOME directory.
· Copy of remote_execution_result.xml of all the nodes.
Miscellaneous:
· List of seed data tables prepared in the instance and if all DB objects required by seed manager are proper
· Details of running adop, adpatch and adworker processes

Section 3: Execution Procedure

Follow these steps to run the script.

3.1 Check prerequisites

The Oracle E-Business Suite Release 12.2 dual file system must be available as a pre-requisite to the script. Customers using releases lower than 12.2.x should be able to run the script when they are facing issues trying to upgrade to 12.2. Summarizing, the script can be run if at least one of the following is true:
· Fresh install customers after Rapid Install has completed.
· Upgrade customers at the stage after Rapid Install's actions to lay down the upgrade file system have completed.

3.2 Perform installation

The steps have to be followed in all application tier nodes in the Oracle E-Business Suite instance
1. Download patch 19045166:R12.AD.C from My Oracle Support.
2. Unzip the patch in a new folder.
3. Confirm the following files are present after unzipping:
adzddiagnostic.pl
adzddbdetails.sql
adzdedndetails.sql 
README.txt
4. Source the application tier run file system environment file.
5. Run the master perl script, adzddiagnostic.pl.
6. When prompted by the script, supply the apps database user password.
7. Provide the "diagnostic_info_.tar" file (created in the working directory) to Oracle for analysis.


Section 4: Troubleshooting

Provide the "diagnostic_info_.tar" file (created in the working directory) to Oracle for analysis.

When attempting to run adop phase=prepare, the following error occurs.

$
0
0
When attempting to run adop phase=prepare, the following error occurs.
[ERROR]: JDK TOP (or FMW JDK TOP) is not set up correctly.
[WARNING]: There could be issues while validating the ports used for E-Business Suite instance against ports used in /etc/services. Refer the log file for more details.
[ERROR]: Some of the ports specified for the patch file system are not available.
[ERROR]: The value of s_file_edition is not set correctly in at least one of the context files
[ERROR]: The values of s_current_base and s_other_base are not set correctly on both RUN and PATCH filesystems
Fix:
Delete duplicate context files and run autoconfig to create new ones.
1.Please backup FND_OAM_CONTEXT_FILES
SQL>create table FND_OAM_CONTEXT_FILES_BK as select * from FND_OAM_CONTEXT_FILES;
2.Please clear data from FND_OAM_CONTEXT_FILES table.
SQL>delete from FND_OAM_CONTEXT_FILES;
3.Please execute autoconfig from all nodes.
cd $ADMIN_SCRIPTS_HOME
./adautocfg.sh
4.Please confirm the new data from FND_OAM_CONTEXT_FILES;
SQL>select * from FND_OAM_CONTEXT_FILES;
5.Please execute the adop again.

adpreclone.pl appsTier fails with error "ERROR: Script failed, exit code 255" in EBS r12.2

$
0
0
perl adpreclone.pl appsTier fails with below error
START: Creating WLS config archive.
Script Executed in 13125 milliseconds, returning status 255
ERROR: Script failed, exit code 255
Solution:
cd $EBS_DOMAIN_HOME
rm -rf C:\location\output.log
and try running adpreclone once again

Query to take CPU and MEMORY for a period in database

$
0
0
Please use the below query to get the output for a monthly CPU and memory.

SELECT to_char(rollup_timestamp,'DD-MON-YY HH24') "Date", average "DB Memory Usage %"
FROM MGMT\$METRIC_HOURLY
WHERE target_name like '%dbprod80%'
AND metric_name = 'Load'
AND metric_column = 'memUsedPct'
AND trunc(rollup_timestamp) between '$MONTH_STDATE' and '$MONTH_EDDATE';


SELECT to_char(rollup_timestamp,'DD-MON-YY HH24') "Date", average "DB CPU Usage %"
FROM MGMT\$METRIC_HOURLY
WHERE target_name like '%dbprod80%'
AND metric_name = 'Load'
AND metric_column = 'cpuUtil'
AND trunc(rollup_timestamp) between '$MONTH_STDATE' and '$MONTH_EDDATE';

Query to get the session details for data pump jobs during export/import.

$
0
0
Please use the below query to get the session details for data pump jobs during export/import.


select x.job_name,b.state,b.job_mode,b.degree
, x.owner_name,z.sql_text, p.message
, p.totalwork, p.sofar
, round((p.sofar/p.totalwork)*100,2) done
, p.time_remaining
from dba_datapump_jobs b
left join dba_datapump_sessions x on (x.job_name = b.job_name)
left join v$session y on (y.saddr = x.saddr)
left join v$sql z on (y.sql_id = z.sql_id)
left join v$session_longops p ON (p.sql_id = y.sql_id)
WHERE y.module='Data Pump Worker'
AND p.time_remaining > 0;

set lines 150 pages 100 numwidth 7
col program for a38
col username for a10
col spid for a7
select to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') "DATE", s.program, s.sid, s.status, s.username,
d.job_name, p.spid, s.serial#, p.pid,s.event
from v$session s, v$process p, dba_datapump_sessions d
where p.addr=s.paddr and s.saddr=d.saddr;

Viewing all 1640 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>