Tuesday, February 28, 2012

Database Login Trigger to prevent access from specific user HOST & DB User


There are couple of ways to restrict database access from some particular user HOST or DATABASE USER or even combination of HOST & DB USER.

We can create a database login trigger that would check for each DB connection at login time and prevent login for the specified hosts & DB users.

Here in my example, I'm trying to protect HR,SCOTT and SALES schemas connection from PC_USER1 & PC_USER2 PC.
CREATE OR REPLACE TRIGGER SYSTEM.LOGON_DENY
AFTER LOGON
ON DATABASE
declare
OSUSER varchar2 (200);
HOSTNAME varchar2 (200);
       begin
       select sys_context ('USERENV', 'OS_USER') into OSUSER from dual;
        select sys_context ('USERENV', 'HOST') into HOSTNAME from dual;
        if sys_context('USERENV','SESSION_USER')in ('HR','SCOTT','SALES')
        and sys_context ('USERENV', 'HOST') in ('PC_USER1','PC_USER2')
        then
  raise_application_error(-20001,'Denied!  You are not allowed to logon from host '||HOSTNAME|| ' using '|| OSUSER);
         end if;
 end;
/
Trigger Created

Now lets try to login as SCOTT from the PC named PC_USER1 and see if that works?
Y:\>sqlplus scott/tiger@LABDB
SQL*Plus: Release 10.2.0.1.0 - Production on Tue Feb 28 16:03:06 2012
Copyright (c) 1982, 2005, Oracle.  All rights reserved.

ERROR:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: Denied!  You are not allowed to logon from host PC_USER1 using
SCOTT
ORA-06512: at line 10

Friday, February 24, 2012

10053 trace with DBMS_SQLDIAG.DUMP_TRACE -11gR2


1.    Find sql_id for the query that you want to trace.
SELECT sql_id,child_number FROM v$sql WHERE sql_text LIKE '%your sql text here%';

2.    Execute DBMS_SQLDIAG.DUMP_TRACE with your sql_id
execute DBMS_SQLDIAG.DUMP_TRACE(p_sql_id=>'sql_id',  p_child_number=>0, 
p_component=>'Compiler',
p_file_id=>' test1053');

3.    Tracefile with the postfix “test1053” that I used here will be generated under diag trace directory:

$ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace
-rw-r----- 1 oracle oinstall   28906 Feb 24 14:35 testdb_ora_32607_test1053.trm
-rw-r----- 1 oracle oinstall   91040 Feb 24 14:35 testdb_ora_32607_ test1053.trc


Another way is to:
SQL> alter session set max_dump_file_size = unlimited;
SQL> ALTER SESSION SET EVENTS 'trace[rdbms.SQL_Optimizer.*][sql:sql_id]';
SQL> --Run the query here --
To disable the trace:
SQL> ALTER SESSION SET EVENTS 'trace[rdbms.SQL_Optimizer.*] off';

Tuesday, February 21, 2012

Oracle Virtual Indexes


While adding some indexes to production table to see some performance improvements; Virtual Index creation is very handy. It doesn’t occupy spaces and you could compare query plan to see if that going to help query performance. Here is a demonstration about how that can be achieved.

Create a sample table where we’ll test Virtual Index impact.
SQL> CREATE TABLE objects_tab AS SELECT * FROM all_objects;
Table created.

SQL> ALTER TABLE objects_tab ADD (
  CONSTRAINT objects_tab_pk PRIMARY KEY (object_id)
);  2    3

Table altered.

SQL> EXEC DBMS_STATS.gather_table_stats(USER, 'objects_tab', cascade=>TRUE);
PL/SQL procedure successfully completed.

If we query the table using the primary key, we can see this reflected in the execution plan.
SQL> SELECT * FROM objects_tab WHERE object_id = 10;

Execution Plan
----------------------------------------------------------
Plan hash value: 2097082964

----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |    98 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| OBJECTS_TAB    |     1 |    98 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | OBJECTS_TAB_PK |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

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

   2 - access("OBJECT_ID"=10)

Now If we query the table using a non-indexed column, we see a full table scan.
SQL> SET AUTOTRACE TRACEONLY EXP
SQL> SELECT * FROM objects_tab WHERE object_name = 'USER_TABLES';

Execution Plan
----------------------------------------------------------
Plan hash value: 821620785

---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |     2 |   196 |   163   (1)| 00:00:03 |
|*  1 |  TABLE ACCESS FULL| OBJECTS_TAB |     2 |   196 |   163   (1)| 00:00:03 |
---------------------------------------------------------------------------------

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

   1 - filter("OBJECT_NAME"='USER_TABLES')

To create the virtual index on this column, simply add the NOSEGMENT clause to the CREATE INDEX statement.

SQL> CREATE INDEX objects_tab_object_name_vi ON objects_tab(object_name) NOSEGMENT;
Index created.

If we repeat the previous query we can see the virtual index is not visible to the optimizer. 
SQL> SET AUTOTRACE TRACEONLY EXP
SQL> SELECT * FROM objects_tab WHERE object_name = 'USER_TABLES';

Execution Plan
----------------------------------------------------------
Plan hash value: 821620785

---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |     2 |   196 |   163   (1)| 00:00:03 |
|*  1 |  TABLE ACCESS FULL| OBJECTS_TAB |     2 |   196 |   163   (1)| 00:00:03 |
---------------------------------------------------------------------------------

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

   1 - filter("OBJECT_NAME"='USER_TABLES')

*** NO REFLECTION

To make the virtual index available we must set the _use_nosegment_indexes parameter. 
SQL> ALTER SESSION SET "_use_nosegment_indexes" = TRUE;
Session altered.

If we repeat the query we can see that the virtual index is now used. 
SQL> SET AUTOTRACE TRACEONLY EXP
SQL>  SELECT * FROM objects_tab WHERE object_name = 'USER_TABLES';

Execution Plan
----------------------------------------------------------
Plan hash value: 4006507992
----------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                            |     2 |   196 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| OBJECTS_TAB                |     2 |   196 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | OBJECTS_TAB_OBJECT_NAME_VI |     2 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_NAME"='USER_TABLES')
The virtual index does not appear in the USER_INDEXES view, but it present in the USER_OBJECTS view.


SQL> select index_name from user_indexes where index_name = 'OBJECTS_TAB_OBJECT_NAME_VI';

no rows selected

SQL> SELECT object_name FROM user_objects WHERE object_name = 'OBJECTS_TAB_OBJECT_NAME_VI';

OBJECT_NAME
----------------------------------------------------------------OBJECTS_TAB_OBJECT_NAME_VI

Statistics can be gathered on virtual indexes in the same way as regular indexes, but as we have seen previously, there will be no record of this in the USER_INDEXES view.

SQL> EXEC DBMS_STATS.gather_index_stats(USER, 'objects_tab_object_name_vi');

PL/SQL procedure successfully completed.

Oracle will prevent us from creating another virtual index with the same column list, but it will allow us to create a real index with the same column list.

CREATE INDEX objects_tab_object_name_vi2 ON objects_tab(object_name) NOSEGMENT
                                                        *
ERROR at line 1:
ORA-01408: such column list already indexed


SQL> CREATE INDEX objects_tab_object_name_i ON objects_tab(object_name);

Index created.