Quantcast
Channel: Andrew's Oracle Blog
Viewing all 330 articles
Browse latest View live

ORA-28405

$
0
0
This post is an update to an earlier one, which I have now deleted. I tested the first part of it in an Oracle 11.1.0.6 database. First I created a role which was identified by a password:
 
SQL> conn / as sysdba
Connected.
SQL> create role low identified by secret_password
  2  /
 
Role created.
 
SQL>
 
I granted the role to a user and made sure it had no default roles:
 
SQL> grant create session, low
  2  to andrew identified by reid
  3  /
 
Grant succeeded.
 
SQL> alter user andrew default role none
  2  /
 
User altered.
 
SQL>
 
I connected as the user and tried to activate the role but this failed with an ORA-01979  as I had not supplied the password:
 
SQL> conn andrew/reid
Connected.
SQL> set role low
  2  /
set role low
*
ERROR at line 1:
ORA-01979: missing or invalid password for role 'LOW'
 
SQL> select role from session_roles
  2  /
 
no rows selected
 
SQL>
 
When I supplied the password, I was able to activate the role successfully:
 
SQL> set role low identified by secret_password
  2  /
 
Role set.
 
SQL> select role from session_roles
  2  /
 
ROLE
------------------------------
LOW
 
SQL>
 
I created another role without a password:
 
SQL> conn / as sysdba
Connected.
SQL> create role high
  2  /
 
Role created.
 
SQL>
 
… granted the first role to it:
 
SQL> grant low to high
  2  /
 
Grant succeeded.
 
SQL>
 
… and granted the 2nd role to the user:
 
SQL> grant high to andrew
  2  /
 
Grant succeeded.
 
SQL>
 
I then connected as the user and activated the 2nd role. This had the effect of also activating the 1st role without needing to supply the password. This has always seemed wrong to me:
 
SQL> conn andrew/reid
Connected.
SQL> select role from session_roles
  2  /
 
no rows selected
 
SQL> set role high
  2  /
 
Role set.
 
SQL> select role from session_roles
  2  /
 
ROLE
------------------------------
HIGH
LOW
 
SQL>
 
I went to a presentation given by Simon Pane from Pythian when I was at the UKOUG conference last week. He said that this behaviour changed in Oracle 11.2.0.4 so I decided to repeat the test in a database on this version:
 
SQL> conn / as sysdba
Connected.
SQL> create role low identified by secret_password
  2  /
 
Role created.
 
SQL> grant create session, low
  2  to andrew identified by reid
  3  /
 
Grant succeeded.
 
SQL> alter user andrew default role none
  2  /
 
User altered.
 
SQL> conn andrew/reid
Connected.
SQL> set role low
  2  /
set role low
*
ERROR at line 1:
ORA-01979: missing or invalid password for role 'LOW'
 
SQL> select role from session_roles
  2  /
 
no rows selected
 
SQL> set role low identified by secret_password
  2  /
 
Role set.
 
SQL> select role from session_roles
  2  /
 
ROLE
------------------------------
LOW
 
SQL> conn / as sysdba
Connected.
SQL> create role high
  2  /
 
Role created.
 
SQL>
 
It all worked as before until I tried to grant the 1st role to the 2nd role where this failed with an ORA-28405, which seems much more sensible to me:
 
SQL> grant low to high
  2  /
grant low to high
*
ERROR at line 1:
ORA-28405: cannot grant secure role to a role
 
SQL>
 
So when I granted the 2nd role to the user:
 
SQL> grant high to andrew
  2  /
 
Grant succeeded.
 
SQL> conn andrew/reid
Connected.
SQL> select role from session_roles
  2  /
 
no rows selected
 
SQL>
 
… he was able to activate the 2nd role but the 1st one, which had the password, was not activated:
 
SQL> set role high
  2  /
 
Role set.
 
SQL> select role from session_roles
  2  /
 
ROLE
------------------------------
HIGH
 
SQL>

Clustering_Factor

$
0
0
I attended the UKOUG conference early in December. While I was there, I went to a presentation by Chris Saxon, where he explained how Oracle decides when to use an index. The example below, which I ran in an Oracle 11.1 database, is based on what I learnt.
 
I used to believe that if a where clause only returned a small percentage of the rows in a table, Oracle would use an index if it could. Then I would be surprised whenever Oracle did not follow this rule. However, I now know that it is not as simple as this; it also depends on the clustering factor.
 
I ran the query below to look at the DBA_SEGMENTS view and show the number of rows for segments owned by SYSTEM, the total number of rows and the percentage of rows for segments owned by SYSTEM. The important point for this example is that SYSTEM owned less than 2% of the segments:
 
SQL> l
  1  select
  2  (select count(*)
  3   from dba_segments
  4   where owner = 'SYSTEM') as system,
  5  (select count(*)
  6   from dba_segments) as total,
  7  (select count(*)
  8   from dba_segments
  9   where owner = 'SYSTEM') /
 10  (select count(*)
 11   from dba_segments) * 100
 12  as percentage
 13* from dual
SQL> /
 
    SYSTEM      TOTAL PERCENTAGE
---------- ---------- ----------
       434      23153 1.87448711
 
SQL>
 
I copied 3 columns from every row in this view into a table:
 
SQL> create table tab1
  2  as select owner, segment_name, bytes
  3  from dba_segments
  4  /
 
Table created.
 
SQL>
 
Based on what I used to think, if I queried this table to find all the rows for segments belonging to SYSTEM, Oracle would use an index if it had one. The problem with this idea is that even with an index, Oracle needs to read every block containing a row belonging to SYSTEM. So, once I had created the table, I counted the number of blocks with one (or more) of these rows. The rows were in several blocks as they were scattered throughout the table:
 
SQL> select count
  2  (distinct dbms_rowid.rowid_block_number(rowid))
  3  from tab1
  4  where owner = 'SYSTEM'
  5  /
 
COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                 84
 
SQL>
 
… then I counted the number of blocks in the table:
 
SQL> select blocks
  2  from user_segments
  3  where segment_name = 'TAB1'
  4  /
 
   BLOCKS
----------
       128
 
SQL>
 
Although less than 2% of the table’s rows were for segments belonging to SYSTEM, even with an index in place, Oracle would have to read 84/128x100 = (almost) 66% of the blocks to get to them.
 
I created an index on the owner column:
 
SQL> create index ind1 on tab1(owner)
  2  /
 
Index created.
 
SQL>
 
I checked the clustering_factor of the index. If this is close to the number of BLOCKS in the table, the data is well clustered (in relation to the index in question). If the clustering_factor is higher, approaching the number of ROWS in the table, the data is not well clustered. In this case, the rows for segments belonging to SYSTEM were all over the place so the data was not well clustered:
 
SQL> select clustering_factor
  2  from user_indexes
  3  where index_name = 'IND1'
  4  /
 
CLUSTERING_FACTOR
-----------------
             1087
 
SQL>
 
I gathered statistics for the table. I found that if I did not do this, Oracle had to do some dynamic sampling when querying the table and I want to look at this in a future post, not now:
 
SQL> exec dbms_stats.gather_table_stats(-
> ownname=>'ANDREW',tabname=>'TAB1');
 
PL/SQL procedure successfully completed.
 
SQL>
 
I traced my session and ran a test query, which calculated how much space the segments belonging to SYSTEM occupied:
 
SQL> alter session set sql_trace = true
  2  /
 
Session altered.
 
SQL> select sum(bytes)
  2  from tab1
  3  where owner = 'SYSTEM'
  4  /
 
SUM(BYTES)
----------
  52953088
 
SQL> alter session set sql_trace = false
  2  /
 
Session altered.
 
SQL>
 
… then I ran the trace file through tkprof and found that Oracle had not used the index when running the query. It had to read well over half of the table’s blocks so a full table scan was much quicker:
 
********************************************************************************
 
select sum(bytes)
from tab1
where owner = 'SYSTEM'
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0        120          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0        120          0           1
 
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 1698  (ANDREW)
 
Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=120 pr=0 pw=0 time=0 us)
    434   TABLE ACCESS FULL TAB1 (cr=120 pr=0 pw=0 time=6 us cost=11 size=5655 card=435)
 
 
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      1   SORT (AGGREGATE)
    434    TABLE ACCESS   MODE: ANALYZED (FULL) OF 'TAB1' (TABLE)
 
********************************************************************************
 
I repeated the test but this time I sorted the data on the owner column first so the rows for segments belonging to SYSTEM would be together:
 
SQL> create table tab2
  2  as select owner, segment_name, bytes
  3  from dba_segments
  4  order by owner
  5  /
 
Table created.
 
SQL>
 
These rows were only in 3 blocks:
 
SQL> select count
  2  (distinct dbms_rowid.rowid_block_number(rowid))
  3  from tab2
  4  where owner = 'SYSTEM'
  5  /
 
COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                  3
 
SQL>
 
… but (obviously), the total number of blocks was the same as in TAB1:                                                
 
SQL> select blocks
  2  from user_segments
  3  where segment_name = 'TAB2'
  4  /
 
   BLOCKS
----------
       128
 
SQL>
 
… so, to read all the rows for segments belonging to SYSTEM, Oracle would only need to access 3/128x100 = (just over) 2.3% of the blocks in the table (assuming it had an index to tell it where to go):
 
SQL> create index ind2 on tab2(owner)
  2  /
 
Index created.
 
SQL>
 
The clustering_factor was very close to the number of BLOCKS in the table so, as expected, the data was well clustered for the index:
 
SQL> select clustering_factor
  2  from user_indexes
  3  where index_name = 'IND2'
  4  /
 
CLUSTERING_FACTOR
-----------------
              116
 
SQL> exec dbms_stats.gather_table_stats(-
> ownname=>'ANDREW',tabname=>'TAB2');
 
PL/SQL procedure successfully completed.
 
SQL> alter session set sql_trace = true
  2  /
 
Session altered.
 
SQL> select sum(bytes)
  2  from tab2
  3  where owner = 'SYSTEM'
  4  /
 
SUM(BYTES)
----------
  52953088
 
SQL> alter session set sql_trace = false
  2  /
 
Session altered.
 
SQL>
 
… and the trace file showed that Oracle used the index when it ran the query against TAB2:
 
********************************************************************************
 
select sum(bytes)
from tab2
where owner = 'SYSTEM'
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0          6          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0          6          0           1
 
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 1698  (ANDREW)
 
Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=6 pr=0 pw=0 time=0 us)
    434   TABLE ACCESS BY INDEX ROWID TAB2 (cr=6 pr=0 pw=0 time=3 us cost=5 size=5499 card=423)
    434    INDEX RANGE SCAN IND2 (cr=3 pr=0 pw=0 time=0 us cost=2 size=0 card=423)(object id 219070)
 
 
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      1   SORT (AGGREGATE)
    434    TABLE ACCESS   MODE: ANALYZED (BY INDEX ROWID) OF 'TAB2'
               (TABLE)
    434     INDEX   MODE: ANALYZED (RANGE SCAN) OF 'IND2' (INDEX)
 
********************************************************************************
 
The only problem with this is that my tables were only queried on one column. In real-life, tables are often queried on several columns. You can add indexes to speed up these queries but the data can only be stored one way round. It is therefore unlikely that all of the indexes will have an optimal clustering_factor. To illustrate this, I found a table with 21 indexes in an Oracle 11.2 production database:
 
SQL> select count(*) from dba_indexes
  2  where table_owner = 'UIMSMGR'
  3  and table_name = 'UABOPEN'
  4  /
 
  COUNT(*)
----------
        21
 
SQL>
 
I checked how many blocks and rows it had:
 
SQL> select blocks, num_rows from dba_tables
  2  where owner = 'UIMSMGR'
  3  and table_name = 'UABOPEN'
  4  /
 
    BLOCKS   NUM_ROWS
---------- ----------
   4606999   99046543
 
SQL>
 
I checked the clustering_factor of each index:
 
SQL> select clustering_factor from dba_indexes
  2  where table_owner = 'UIMSMGR'
  3  and table_name = 'UABOPEN'
  4  order by 1
  5  /
 
CLUSTERING_FACTOR
-----------------
                0
                0
             1324
             1640
          1284785
          4319498
          4431395
          5113131
         10048510
         11847679
         14469614
         14808490
         20604699
         21757869
         22821251
         23166573
         23336072
         24103639
         24306402
         25662626
         26071543
 
21 rows selected.
 
SQL>
 
The first five indexes have a clustering_factor well below the number of blocks in the table. I’m not sure what this means so I will try to look at it in a future post.
 
The next three indexes have a clustering_factor close to the number of blocks. The data is well clustered for these indexes.
 
However, the final 13 indexes have a higher clustering_factor. The data is not well clustered for these indexes so Oracle is less likely to use them.

Clustering_Factor = 0

$
0
0
I looked at clustering_factor in the previous post and wondered what might cause it to be well below the number of blocks in the table. Chris Saxon suggested it might be due to rows with null values. I decided to look first at the two indexes with a clustering_factor of zero in the same Oracle 11.2 database and checked which columns they were on. Then I looked to see how many of the rows had null values in the columns concerned. The table had > 100 million rows and I found it much quicker to see if any rows had not null values instead. In both cases, there were none at all i.e. all rows in the table had null values in the columns in question:
 
SQL> select owner, index_name
  2  from dba_indexes
  3  where table_owner = 'UIMSMGR'
  4  and table_name = 'UABOPEN'
  5  and clustering_factor = 0
  6  /
 
OWNER                     INDEX_NAME
------------------------- -------------------------
TFE_INDEXES               TFE_UABOPEN_COPA_CODE
UIMSMGR                   UABOPEN_COURT_CASE_INDEX
 
SQL> select column_position, column_name
  2  from dba_ind_columns
  3  where index_owner = 'TFE_INDEXES'
  4  and index_name = 'TFE_UABOPEN_COPA_CODE'
  5  order by 1
  6  /
 
COLUMN_POSITION COLUMN_NAME
--------------- ------------------------------
              1 UABOPEN_COPA_CODE
 
SQL> select column_position, column_name
  2  from dba_ind_columns
  3  where index_owner = 'UIMSMGR'
  4  and index_name = 'UABOPEN_COURT_CASE_INDEX'
  5  order by 1
  6  /
 
COLUMN_POSITION COLUMN_NAME
--------------- ------------------------------
              1 UABOPEN_COURT_CASE_NUMBER
 
SQL> select count(*)
  2  from uabopen
  3  where uabopen_copa_code is not null
  4  /
 
  COUNT(*)
----------
         0
 
SQL> select count(*)
  2  from uabopen
  3  where uabopen_court_case_number is not null
  4  /
 
  COUNT(*)
----------
         0
 
SQL>
 
As for the indexes with clustering_factor> 0 but less than the number of blocks in the table, I guess that some but not all of the rows had null values in the indexed columns. This is something I will leave you to check for yourselves if you feel so inclined.

INSERT /*+ APPEND */ Hint Does Not Seem to Work Consistently

$
0
0
I ran the following SQL in an Oracle 11.2.0.1 database:

SQL> create table tab1
  2  (col1 number)
  3  /
 
Table created.
 
SQL> alter session set sql_trace = true
  2  /
 
Session altered.
 
SQL> insert /*+ append */ into tab1 select 1 from dual
  2  /
 
1 row created.
 
SQL> commit
  2  /
 
Commit complete.
 
SQL> insert /*+ append */ into tab1 values(2)
  2  /
 
1 row created.
 
SQL> commit
  2  /
 
Commit complete.
 
SQL> select * from tab1
  2  /
 
      COL1
----------
         1
         2
 
SQL> alter session set sql_trace = false
  2  /
 
Session altered.
 
SQL>

Then I ran the trace file through tkprof. For some reason, the first SQL used a Direct Path load:

SQL ID: f57gyxg0uqcgb
Plan Hash: 1432430773
insert /*+ append */ into tab1 select 1 from dual
 
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.02       0.02          0          3         30           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.02       0.02          0          3         30           1
 
Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 8354  (ORACLE)
 
Rows     Row Source Operation
-------  ---------------------------------------------------
      0  LOAD AS SELECT  (cr=0 pr=0 pw=0 time=0 us)
      1   FAST DUAL  (cr=0 pr=0 pw=0 time=0 us cost=2 size=0 card=1)
 
 
Rows     Execution Plan
-------  ---------------------------------------------------
      0  INSERT STATEMENT   MODE: ALL_ROWS
      0   LOAD AS SELECT OF 'TAB1'
      1        FAST DUAL

 
... but the second one didn’t. The strange reformatting of the insert statement took place in tkprof, not in Blogger:

SQL ID: 6pb8960hm1hpy
Plan Hash: 0
insert /*+ append */ into tab1
values
(2)
 
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          7         25           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.00       0.00          0          7         25           1
 
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 8354  (ORACLE)
 
Rows     Row Source Operation
-------  ---------------------------------------------------
      0  LOAD TABLE CONVENTIONAL  (cr=7 pr=0 pw=0 time=0 us)
 
 
Rows     Execution Plan
-------  ---------------------------------------------------
      0  INSERT STATEMENT   MODE: ALL_ROWS
      0   LOAD TABLE CONVENTIONAL OF 'TAB1'


P.S. Shortly after publishing this post, a number of people corrected it in the comments below. I won't be changing the post; if I did, the comments would then be out of place. However, I will be testing and understanding the comments before using them as the basis for another post in the near future.

SELECTs Do Not Block UPDATEs

$
0
0
I went on my first DBA course in 1997 and the lecturer there explained that readers do not block writers in an Oracle database. I had an issue recently which appeared to contradict this so I have reproduced it below in an Oracle 11.2.0.1 database.
 
I noticed in OEM that there was some issue in the database. As usual, click on the images to enlarge them and bring them into focus if you need to:


I looked at the Blocking Sessions screen and saw that Andrew was blocking Fred:


I clicked on the link to see the SQL which Fred was running:


I clicked on the link to see the SQL which Andrew was running:


So it seemed that: 

SELECT COUNT(*)
FROM DBA_TABLES
 
was blocking:
 
update andrew.tab1
set col1 = 2
where col1 = 1
 
As you might have guessed, I set this test up on purpose and this is what really happened. Andrew created a table:
 
SQL> conn andrew/reid
Connected.
SQL> create table tab1 (col1 number)
  2  /
 
Table created.
 
SQL>
 
He then allowed Fred to update it:
 
SQL> grant update on tab1 to fred
  2  /
 
Grant succeeded.
 
SQL>
 
He added a row to the table, saved it and updated it, setting up a lock in the process:
 
SQL> insert into tab1
  2  values (1)
  3  /
 
1 row created.
 
SQL> commit
  2  /
 
Commit complete.
 
SQL> update tab1
  2  set col1 = 2
  3  where col1 = 1
  4  /
 
1 row updated.
 
SQL>
 
Finally, he counted the rows in DBA_TABLES again and again:
 
SQL> declare
  2    row_count number;
  3  begin
  4    while (1=1) loop
  5    select count(*) into row_count
  6    from dba_tables;
  7    end loop;
  8  end;
  9  /
 
Fred then came along and tried to update the same row in TAB1 but had to wait:
 
SQL> conn fred/bloggs
Connected.
SQL> update andrew.tab1
  2  set col1 = 2
  3  where col1 = 1
  4  /
 
So, what is the point of this example?
 
If you look at OEM’s Blocking Sessions screen and see a user blocking another user, the SQL shown against the blocking user is the SQL he/she is currently running. This may or may not be the cause of the lock which is holding up the user(s) underneath.

Creating Tables in the UNDO Tablespace??

$
0
0
I was reading an article written by Martin Widlake in Oracle Scene Issue 58 (Autumn/Winter 2015). It said:

The second new item is the UNDO tablespace. This is a special tablespace that is only used for internal purposes and one that users cannot put any tables or indexes into.

This seemed perfectly reasonable so I wondered what might happen if I tried to do it. In an Oracle 9.2.0.7 database Oracle returned an error:

SQL> create table tab1
  2  (col1 number)
  3  tablespace undo_1
  4  /
create table tab1
*
ERROR at line 1:
ORA-30022: Cannot create segments in undo tablespace

SQL>

In an Oracle 11.2.0.1 database, I was not allowed to use the UNDO tablespace as a user’s default tablespace:

SQL> l
  1  create user andrew
  2  identified by reid
  3* default tablespace undotbs1
SQL> /
create user andrew
*
ERROR at line 1:
ORA-30033: Undo tablespace cannot be specified as
default user tablespace

SQL>

... but I was allowed to create a table in it:

SQL> create table tab1
  2  (col1 number)
  3  tablespace undotbs1
  4  /

Table created.

SQL> 

Does anybody know if this is a bug? I will update this post if I find out.

Postscript written on 19th February 2016:

I wrote the post above yesterday and, if you check the comments below, you will see that a couple of people have now helped me to understand what happened. It was all down to deferred segment creation. I have discussed this before here and here (and other places too) but it still catches me out from time to time. I returned to the Oracle 11.2.0.1 database and ran Andrzej’s SQL for confirming that UNDOTBS1was an UNDO tablespace:

SQL> select contents from dba_tablespaces
  2  where tablespace_name = 'UNDOTBS1'
  3  /

CONTENTS
---------
UNDO

SQL>

Then I confirmed that deferred segment creation was turned on:

SQL> l
  1  select value from v$parameter
  2* where name = 'deferred_segment_creation'
SQL> /

VALUE
--------------------
TRUE

SQL>

I created another table in the UNDO tablespace:

SQL> create table dom (col1 number)
  2  tablespace undotbs1
  3  /

Table created.

SQL>

That worked but, as Dom suggested, when I tried to insert a row, Oracle needed to create a segment and was unable to do so:

SQL> insert into dom values (1)
  2  /
insert into dom values (1)
            *
ERROR at line 1:
ORA-30022: Cannot create segments in undo tablespace

SQL>

Finally, I turned deferred segment creation off:

SQL> l
  1  alter session
  2* set deferred_segment_creation = false
SQL> /

Session altered.

SQL>

Once I had done that, I was unable to create a table in the UNDO tablespace at all:

SQL> create table andrzej (col1 number)
  2  tablespace undotbs1
  3  /
create table andrzej (col1 number)
*
ERROR at line 1:
ORA-30022: Cannot create segments in undo tablespace

SQL>

I’m guessing that when Andrzej created his example, deferred_segment_creation was set to false at the session level as above, at the system level like this:

SQL> alter system set deferred_segment_creation = false
  2  /

System altered.

SQL>

Or via an initialisation parameter in his database’s parameter file.

RMAN Backup Without Compression

$
0
0
I wanted to test the effect of RMAN backup compression on an Oracle 11.2.0.1 database running on Windows. I configured the flash recovery area, closed the database and mounted it. Then I did a backup without compression:

C:\Users\AJ0294094>rman nocatalog target=andrew/reid

Recovery Manager: Release 11.2.0.1.0 - Production on Fri Mar 11 18:42:42 2016

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

connected to target database: DEBELDV2 (DBID=4080010922)
using target database control file instead of recovery catalog

RMAN> shutdown

database closed
database dismounted
Oracle instance shut down

RMAN> startup mount

connected to target database (not started)
Oracle instance started
database mounted

Total System Global Area    1043886080 bytes

Fixed Size                     2182344 bytes
Variable Size                612369208 bytes
Database Buffers             423624704 bytes
Redo Buffers                   5709824 bytes

RMAN> backup database;

Starting backup at 11-MAR-16
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=156 device type=DISK
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00001 name=E:\ORACLE\ORADATA\DEBELDV2\SYSTEM01.DBF
input datafile file number=00003 name=E:\ORACLE\ORADATA\DEBELDV2\UNDOTBS01.DBF
input datafile file number=00002 name=E:\ORACLE\ORADATA\DEBELDV2\SYSAUX01.DBF
input datafile file number=00004 name=E:\ORACLE\ORADATA\DEBELDV2\USERS01.DBF
channel ORA_DISK_1: starting piece 1 at 11-MAR-16
channel ORA_DISK_1: finished piece 1 at 11-MAR-16
piece handle=E:\ORACLE\FLASH_RECOVERY_AREA\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_1
1\O1_MF_NNNDF_TAG20160311T184324_CG60XXJ6_.BKP tag=TAG20160311T184324 comment=NO
NE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:35
channel ORA_DISK_1: starting full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
including current control file in backup set
including current SPFILE in backup set
channel ORA_DISK_1: starting piece 1 at 11-MAR-16
channel ORA_DISK_1: finished piece 1 at 11-MAR-16
piece handle=E:\ORACLE\FLASH_RECOVERY_AREA\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_1
1\O1_MF_NCSNF_TAG20160311T184324_CG610XWY_.BKP tag=TAG20160311T184324 comment=NO
NE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 11-MAR-16

RMAN>

I checked the size of the backup. It was just under 1 gigabyte (N.B. this was an empty database just created by dbca):

E:\oracle\flash_recovery_area\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_11>dir
Volume in drive E is Seagate Backup Plus Drive
Volume Serial Number is 6E45-7AAF

Directory of E:\oracle\flash_recovery_area\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_
11

03/11/2016  06:45 PM    <DIR>          .
03/11/2016  06:45 PM    <DIR>          ..
03/11/2016  06:45 PM         9,830,400 O1_MF_NCSNF_TAG20160311T184324_CG610XWY_.
BKP
03/11/2016  06:44 PM       981,147,648 O1_MF_NNNDF_TAG20160311T184324_CG60XXJ6_.
BKP
               2 File(s)    990,978,048 bytes
               2 Dir(s)  579,413,495,808 bytes free

E:\oracle\flash_recovery_area\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_11>

I did not need to keep this backup so I deleted it:

C:\Users\AJ0294094>rman nocatalog target=andrew/reid

Recovery Manager: Release 11.2.0.1.0 - Production on Fri Mar 11 19:08:46 2016

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

connected to target database: DEBELDV2 (DBID=4080010922, not open)
using target database control file instead of recovery catalog

RMAN> delete backup;

allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=156 device type=DISK

List of Backup Pieces
BP Key  BS Key  Pc# Cp# Status      Device Type Piece Name
------- ------- --- --- ----------- ----------- ----------
1       1       1   1   AVAILABLE   DISK        E:\ORACLE\FLASH_RECOVERY_AREA\DE
BELDV2\DEBELDV2\BACKUPSET\2016_03_11\O1_MF_NNNDF_TAG20160311T184324_CG60XXJ6_.BK
P
2       2       1   1   AVAILABLE   DISK        E:\ORACLE\FLASH_RECOVERY_AREA\DE
BELDV2\DEBELDV2\BACKUPSET\2016_03_11\O1_MF_NCSNF_TAG20160311T184324_CG610XWY_.BK
P

Do you really want to delete the above objects (enter YES or NO)? YES
deleted backup piece
backup piece handle=E:\ORACLE\FLASH_RECOVERY_AREA\DEBELDV2\DEBELDV2\BACKUPSET\20
16_03_11\O1_MF_NNNDF_TAG20160311T184324_CG60XXJ6_.BKP RECID=1 STAMP=906230605
deleted backup piece
backup piece handle=E:\ORACLE\FLASH_RECOVERY_AREA\DEBELDV2\DEBELDV2\BACKUPSET\20
16_03_11\O1_MF_NCSNF_TAG20160311T184324_CG610XWY_.BKP RECID=2 STAMP=906230701
Deleted 2 objects


RMAN>

I checked at the OS level that the backup files had disappeared (they had).

Continued in the next post.

RMAN Backup With Compression

$
0
0
I repeated the test from the previous post but this time I tried to compress the backup. At this stage I need your help. I believe I have used the free compression which does not require an extra licence. If somebody who knows more about this than me could add a comment below, telling me if I am right or wrong, that would be very helpful. I backed up the database as before:

C:\Users\AJ0294094>rman nocatalog target=andrew/reid

Recovery Manager: Release 11.2.0.1.0 - Production on Fri Mar 11 19:19:38 2016

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

connected to target database: DEBELDV2 (DBID=4080010922, not open)
using target database control file instead of recovery catalog

RMAN> backup as compressed backupset database;

Starting backup at 11-MAR-16
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=156 device type=DISK
channel ORA_DISK_1: starting compressed full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
input datafile file number=00001 name=E:\ORACLE\ORADATA\DEBELDV2\SYSTEM01.DBF
input datafile file number=00003 name=E:\ORACLE\ORADATA\DEBELDV2\UNDOTBS01.DBF
input datafile file number=00002 name=E:\ORACLE\ORADATA\DEBELDV2\SYSAUX01.DBF
input datafile file number=00004 name=E:\ORACLE\ORADATA\DEBELDV2\USERS01.DBF
channel ORA_DISK_1: starting piece 1 at 11-MAR-16
channel ORA_DISK_1: finished piece 1 at 11-MAR-16
piece handle=E:\ORACLE\FLASH_RECOVERY_AREA\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_1
1\O1_MF_NNNDF_TAG20160311T192012_CG632XB5_.BKP tag=TAG20160311T192012 comment=NO
NE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:15
channel ORA_DISK_1: starting compressed full datafile backup set
channel ORA_DISK_1: specifying datafile(s) in backup set
including current control file in backup set
including current SPFILE in backup set
channel ORA_DISK_1: starting piece 1 at 11-MAR-16
channel ORA_DISK_1: finished piece 1 at 11-MAR-16
piece handle=E:\ORACLE\FLASH_RECOVERY_AREA\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_1
1\O1_MF_NCSNF_TAG20160311T192012_CG6359MJ_.BKP tag=TAG20160311T192012 comment=NO
NE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:01
Finished backup at 11-MAR-16

RMAN> exit


Recovery Manager complete.

C:\Users\AJ0294094>

... and when I checked the size of the backup on disk, it was roughly a quarter of the size of the backup I did before:

E:\oracle\flash_recovery_area\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_11>dir
Volume in drive E is Seagate Backup Plus Drive
Volume Serial Number is 6E45-7AAF

Directory of E:\oracle\flash_recovery_area\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_
11

03/11/2016  07:21 PM    <DIR>          .
03/11/2016  07:21 PM    <DIR>          ..
03/11/2016  07:21 PM         1,097,728 O1_MF_NCSNF_TAG20160311T192012_CG6359MJ_.
BKP
03/11/2016  07:21 PM       235,659,264 O1_MF_NNNDF_TAG20160311T192012_CG632XB5_.
BKP
               2 File(s)    236,756,992 bytes
               2 Dir(s)  579,868,397,568 bytes free

E:\oracle\flash_recovery_area\DEBELDV2\DEBELDV2\BACKUPSET\2016_03_11>

Bind Variables

$
0
0
This example, tested on Oracle 11, shows how you can define bind variables in SQL*Plus, assign values to them in PL/SQL then display those values afterwards back in SQL*Plus:

SQL> variable bv1 varchar2(3)
SQL> variable bv2 number
SQL> begin
  2  select 'ABC' into :bv1 from dual;
  3  select 123 into :bv2 from dual;
  4  end;
  5  /

PL/SQL procedure successfully completed.

SQL> print bv1

BV1
--------------------------------
ABC

SQL> print bv2

       BV2
----------
       123

SQL> execute :bv1 := 'XYZ';

PL/SQL procedure successfully completed.

SQL> execute :bv2 := 456;

PL/SQL procedure successfully completed.

SQL> print bv1

BV1
--------------------------------
XYZ

SQL> print bv2

       BV2
----------
       456

SQL>

%TYPE Declaration Gives PLS-00302

$
0
0
A developer reported a problem with a %TYPE declaration which was returning PLS-00302 in an Oracle 10 database. The cause turned out to be a variation on a problem which I have already reported. However, as it took me some time to work out, I have reproduced it below. First I created a user, called USER1, who would own a table:

SQL> conn / as sysdba
Connected.
SQL> create user user1
  2  identified by user1
  3  default tablespace users
  4  quota 10m on users
  5  /
 
User created.
 
SQL> grant create session, create table
  2  to user1
  3  /
 
Grant succeeded.
 
SQL>

Then I created a user, called USER2, to declare a variable using %TYPE, basing it on a column in the table created by USER1:

SQL> create user user2 identified by user2
  2  /
 
User created.
 
SQL> grant create session, create synonym
  2  to user2
  3  /
 
Grant succeeded.
 
SQL> 

USER1 created a table called TAB1 and allowed USER2 to see it:

SQL> conn user1/user1
Connected.
SQL> create table tab1
  2  (col1 number)
  3  /
 
Table created.
 
SQL> grant select on tab1 to user2
  2  /
 
Grant succeeded.
 
SQL> 

USER2 declared a variable called BLAH using %TYPE to base it on column COL1 in table TAB1. This was successful:

SQL> conn user2/user2
Connected.
SQL> declare
  2  blah user1.tab1.col1%type;
  3  begin
  4  null;
  5  end;
  6  /
 
PL/SQL procedure successfully completed.
 
SQL> 

USER2 created a synonym called USER1. N.B. It is not a good idea for an object in one schema to have the same name as a schema elsewhere in the database.

SQL> create synonym user1 for user_tables
  2  /
 
Synonym created.
 
SQL> 

USER2 tried to declare a variable called BLAH as before. This time, Oracle probably thought that USER1 referred to the synonym created in the previous step rather than the username created at the start of the post. The declaration therefore failed:

SQL> declare
  2  blah user1.tab1.col1%type;
  3  begin
  4  null;
  5  end;
  6  /
blah user1.tab1.col1%type;
           *
ERROR at line 2:
ORA-06550: line 2, column 12:
PLS-00302: component 'TAB1' must be declared
ORA-06550: line 2, column 6:
PL/SQL: Item ignored
 
SQL>

How to See When an Oracle Role Was Created

$
0
0
You can get the date and time a role was created from the CTIME column in SYS.USER$. You can see what I mean in the example below, which I tested in an Oracle 11.2.0.4 database. First I created a role between DATE_AND_TIME1 and DATE_AND_TIME2. As they were the same, to the nearest second, the role must have been created at 16:11:05 on 27th May 2016.

I then waited 5 seconds and queried the CTIME column in SYS.USER$ for BLAH. This showed 16:11:05 on 27th May 2016 i.e. the date and time when the role was created:


SQL> select to_char(sysdate,'DD-MON-YY HH24:MI:SS')  2  date_and_time1
  3  from dual
  4  /

DATE_AND_TIME1
------------------
27-MAY-16 16:11:05

SQL> create role blah
  2  /

Role created.

SQL> select to_char(sysdate,'DD-MON-YY HH24:MI:SS')
  2  date_and_time2
  3  from dual
  4  /

DATE_AND_TIME2
------------------
27-MAY-16 16:11:05

SQL> exec dbms_lock.sleep(5);

PL/SQL procedure successfully completed.

SQL> select to_char(sysdate,'DD-MON-YY HH24:MI:SS')
  2  date_and_time3
  3  from dual
  4  /

DATE_AND_TIME3
------------------
27-MAY-16 16:11:10

SQL> select to_char(ctime,'DD-MON-YY HH24:MI:SS')
  2  role_created
  3  from sys.user$
  4  where name = 'BLAH'
  5  /

ROLE_CREATED
------------------
27-MAY-16 16:11:05

SQL>

ORA-01109

$
0
0
I read that you cannot take a tablespace offline if the database is only mounted so I decided to test this in an Oracle 11.2.0.4 database. I mounted the database and tried to take the USERS tablespace offline. Oracle returned an ORA-01109. I opened the database then I was able to take the tablespace offline:
 
SQL> startup mount
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size                  2252448 bytes
Variable Size             310378848 bytes
Database Buffers          201326592 bytes
Redo Buffers                7979008 bytes
Database mounted.
SQL> alter tablespace users offline
  2  /
alter tablespace users offline
*
ERROR at line 1:
ORA-01109: database not open

SQL> alter database open
  2  /

Database altered.

SQL> alter tablespace users offline
  2  /

Tablespace altered.

SQL> alter tablespace users online
  2  /

Tablespace altered.

SQL>

ARCHIVE_LAG_TARGET on Oracle 11.2.0.4

$
0
0
I read about this parameter, which is supposed to force a redo log switch after a certain number of seconds. I decided to try it out in an Oracle 11.2.0.4 database. This is the record from the alert log showing me setting the parameter:

Wed May 25 18:00:39 2016
ALTER SYSTEM SET archive_lag_target=1800 SCOPE=BOTH;
Wed May 25 18:00:39 2016


I expected this to force a redo log switch every 30 minutes but Oracle did the switches every 15 minutes instead. The database was doing no work at the time:

Wed May 25 18:00:39 2016
Thread 1 advanced to log sequence 77 (LGWR switch)
  Current log# 2 seq# 77 mem# 0: /database/ANDREW/DB1/redo02a.log
  Current log# 2 seq# 77 mem# 1: /database/ANDREW/DB1/redo02b.log
Wed May 25 18:15:39 2016
Thread 1 advanced to log sequence 78 (LGWR switch)
  Current log# 3 seq# 78 mem# 0: /database/ANDREW/DB1/redo03a.log
  Current log# 3 seq# 78 mem# 1: /database/ANDREW/DB1/redo03b.log
Wed May 25 18:30:39 2016
Thread 1 advanced to log sequence 79 (LGWR switch)
  Current log# 1 seq# 79 mem# 0: /database/ANDREW/DB1/redo01a.log
  Current log# 1 seq# 79 mem# 1: /database/ANDREW/DB1/redo01b.log
Etc
Etc


I looked on the Internet for a bug which might explain this but could not find one. Then I decided it was time for a bit of trial and error investigation. I dropped one member from each redo log group. I assume you know how to do this if you are reading this blog. Just don’t forget that you cannot drop members from the current redo log group:

SQL> alter database drop logfile member
  2  '/database/ANDREW/DB1/redo03b.log'
  3  /
alter database drop logfile member
*
ERROR at line 1:
ORA-01609: log 3 is the current log for thread 1 - cannot drop members
ORA-00312: online log 3 thread 1: '/database/ANDREW/DB1/redo03a.log'
ORA-00312: online log 3 thread 1: '/database/ANDREW/DB1/redo03b.log'

SQL> alter system switch logfile
  2  /

System altered.

SQL> alter database drop logfile member
  2  '/database/ANDREW/DB1/redo03b.log'
  3  /

Database altered.

SQL>


However, the redo log switches still occurred every 15 minutes:

Thu May 26 10:24:16 2016
Thread 1 advanced to log sequence 143 (LGWR switch)
  Current log# 2 seq# 143 mem# 0: /database/ANDREW/DB1/redo02a.log
Thu May 26 10:39:15 2016
Thread 1 advanced to log sequence 144 (LGWR switch)
  Current log# 3 seq# 144 mem# 0: /database/ANDREW/DB1/redo03a.log
Thu May 26 10:54:15 2016
Thread 1 advanced to log sequence 145 (LGWR switch)
  Current log# 1 seq# 145 mem# 0: /database/ANDREW/DB1/redo01a.log


I repeated the test on an Oracle 9.2.0.7 database but the parameter worked as advertised i.e. the redo log switches occurred at 30 minute intervals:

Thu May 26 10:28:17 2016
ALTER SYSTEM SET archive_lag_target=1800 SCOPE=MEMORY;
Thu May 26 10:58:08 2016
Thread 1 advanced to log sequence 3
  Current log# 2 seq# 3 mem# 0: /cisdpt/livdpt1/liv_redo1/redo2_1.dbf
  Current log# 2 seq# 3 mem# 1: /cisdpt/livdpt1/liv_redo2/redo2_2.dbf
Thu May 26 11:28:08 2016
Thread 1 advanced to log sequence 4
  Current log# 3 seq# 4 mem# 0: /cisdpt/livdpt1/liv_redo1/redo3_1.dbf
  Current log# 3 seq# 4 mem# 1: /cisdpt/livdpt1/liv_redo2/redo3_2.dbf


I noticed that the Oracle 9 database had db_writer_processes set to 1 whereas in the Oracle 11.2.0.4 database, it was set to 2. I changed it to 1 but this made no difference either. At this point I decided this was taking more time than I wanted to spend on it so I stopped. If anybody has any idea where I am going wrong, perhaps they could let me know.

ORA-39710 and ORA-00704

$
0
0
I tried to use dbua to upgrade a database from Oracle 11.2.0.4 to Oracle 12.1.0.2. Part way through, my PC lost connection with the UNIX server hosting the database. I tried to connect to the database but got an ORA-39710 so I forced the database to close with shutdown abort:

NLFINUT1 /export/home/oracle > sqlplus /

SQL*Plus: Release 12.1.0.2.0 Production on Mon Jul 18 14:03:08 2016

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

ERROR:
ORA-39710: only connect AS SYSDBA is allowed when OPEN in UPGRADE mode

Enter user-name: / as sysdba

Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing options

SQL> shutdown abort
ORACLE instance shut down.
SQL>


A colleague restored the database for me. dbua had updated /var/opt/oracle/oratab to show the new database version so I changed it back to 11.2.0.4:

NLFINUT1:/oracle/app/oracle/product/11.2.0.4:N

However, I forgot to rerun . oraenv to pick up the old Oracle software so when I tried to open the database, I got an ORA-00704:

NLFINUT1 /export/home/oracle > sqlplus / as sysdba

SQL*Plus: Release 12.1.0.2.0 Production on Mon Jul 18 14:13:38 2016

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup
ORACLE instance started.

Total System Global Area  650117120 bytes
Fixed Size                  2917400 bytes
Variable Size             230693864 bytes
Database Buffers          411041792 bytes
Redo Buffers                5464064 bytes
Database mounted.
ORA-01092: ORACLE instance terminated. Disconnection forced
ORA-00704: bootstrap process failure
ORA-00604: error occurred at recursive SQL level 2
ORA-00904: "I"."UNUSABLEBEGINNING#": invalid identifier
Process ID: 21064
Session ID: 212 Serial number: 60960

SQL>


I reran . oraenv then I was able to open the database:

NLFINUT1 /export/home/oracle > sqlplus / as sysdba

SQL*Plus: Release 11.2.0.4.0 Production on Mon Jul 18 14:52:08 2016

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup
ORACLE instance started.

Total System Global Area  622338048 bytes
Fixed Size                  2184112 bytes
Variable Size             327158864 bytes
Database Buffers          285212672 bytes
Redo Buffers                7782400 bytes
Database is aangekoppeld.
Database is geopend.
SQL>


(It is used in the Netherlands so some of the messages are in Dutch.)

Making a Hot Backup and Doing an Incomplete Recovery

$
0
0
This post shows how to do a hot backup followed by an incomplete recovery. I ran it on an Oracle 11.2.0.4 test database. First I checked that the database was in ARCHIVELOG mode:

SQL> select log_mode from v$database;

LOG_MODE
------------
ARCHIVELOG

SQL>


Then I decided where to copy the hot backup.

The directory listing below shows a sub-directory called backup. This contains a cold backup I made earlier in case the test goes horribly wrong and I need to start again.

After this, you can see the database’s 3 control files. In a real life situation, these would be on separate physical disks but this is only a test so having them in the same place is OK.

Next comes a directory called hot_backup. The hot backup will go in here.

Finally you can see the database’s redo logs and datafiles:


GBASRDB1 /database/ANDREW/DB1 > ls -l
total 6585680
drwxr-xr-x   2 oracle   dba         4096 Jun  3 17:51 backup
-rw-r-----   1 oracle   dba      11517952 Aug  9 16:59 control01.ctl
-rw-r-----   1 oracle   dba      11517952 Aug  9 16:59 control02.ctl
-rw-r-----   1 oracle   dba      11517952 Aug  9 16:59 control03.ctl
drwxr-xr-x   2 oracle   dba         4096 Jun  7 18:40 hot_backup
-rw-r-----   1 oracle   dba      52429312 Aug  9 16:50 redo01a.log
-rw-r-----   1 oracle   dba      52429312 Aug  9 16:50 redo02a.log
-rw-r-----   1 oracle   dba      52429312 Aug  9 16:59 redo03a.log
-rw-r-----   1 oracle   dba      796925952 Aug  9 16:55 sysaux01.dbf
-rw-r-----   1 oracle   dba      796925952 Aug  9 16:58 system01.dbf
-rw-r-----   1 oracle   dba      524296192 Aug  9 16:50 temp01.dbf
-rw-r-----   1 oracle   dba      524296192 Aug  9 16:55 undotbs01.dbf
-rw-r-----   1 oracle   dba      524296192 Aug  9 16:50 users01.dbf
GBASRDB1 /database/ANDREW/DB1 >


I identified the files to backup:

SQL> l
  1  select tablespace_name, file_name
  2  from dba_data_files
  3* order by 1,2
SQL> /

TABLESPACE_NAME FILE_NAME
--------------- -----------------------------------
SYSAUX          /database/ANDREW/DB1/sysaux01.dbf
SYSTEM          /database/ANDREW/DB1/system01.dbf
UNDOTBS1        /database/ANDREW/DB1/undotbs01.dbf
USERS           /database/ANDREW/DB1/users01.dbf

SQL>


... and checked the sequence number of the current redo log:

SQL> l
  1  select group#, status, sequence#
  2  from v$log
  3* order by 1
SQL> /

    GROUP# STATUS            SEQUENCE#
---------- ---------------- ----------
         1 INACTIVE               1423
         2 CURRENT                1424
         3 INACTIVE               1422

SQL>


I put the database into hot backup mode:

SQL> alter database begin backup
  2  /

Database altered.

SQL>


I copied the datafiles into the hot_backup directory:

GBASRDB1 /database/ANDREW/DB1 > cp sysaux01.dbf hot_backup
GBASRDB1 /database/ANDREW/DB1 > cp system01.dbf hot_backup
GBASRDB1 /database/ANDREW/DB1 > cp undotbs01.dbf hot_backup
GBASRDB1 /database/ANDREW/DB1 > cp users01.dbf hot_backup
GBASRDB1 /database/ANDREW/DB1 >


I created a marker table:

SQL> create table system.andrew_was_here(col1 number)
  2  /

Table created.

SQL> insert into system.andrew_was_here values(1234567)
  2  /

1 row created.

SQL> commit
  2  /

Commit complete.

SQL>


I took the database out of hot backup mode:

SQL> alter database end backup
  2  /

Database altered.

SQL>
 


I archived any outstanding redo and forced a log switch:

SQL> alter system archive log current
  2  /

System altered.

SQL>


I checked the sequence number of the current redo log:

SQL> l
  1  select group#, status, sequence#
  2  from v$log
  3* order by 1
SQL> /

    GROUP# STATUS            SEQUENCE#
---------- ---------------- ----------
         1 INACTIVE               1423
         2 ACTIVE                 1424
         3 CURRENT                1425

SQL>


All the redo needed for recovery should be in log no 1424 so I copied this into the hot_backup directory: 


GBASRDB1 /database/ANDREW/DB1 > cp 1_1424_913830195.dbf hot_backup
GBASRDB1 /database/ANDREW/DB1 >
 


Finally, I took a backup of the controlfile:

SQL> alter database backup controlfile
  2  to '/database/ANDREW/DB1/hot_backup/bkup.ctl'
  3  /

Database altered.

SQL>


As a first example, I decided to restore to this hot backup so I deleted the database’s datafiles, control files and online redo logs. Obviously, this loses any changes made after the backup:

GBASRDB1 /database/ANDREW/DB1 > rm *
rm: backup is a directory
rm: hot_backup is a directory
GBASRDB1 /database/ANDREW/DB1 > ls
backup      hot_backup
GBASRDB1 /database/ANDREW/DB1 >
 


Then I closed the database:

GBASRDB1 /database/ANDREW/DB1 > sqlplus / as sysdba

SQL*Plus: Release 11.2.0.4.0 Production on Wed Aug 10 17:39:59 2016

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> shutdown abort
ORACLE instance shut down.
SQL>


I restored the files from the hot_backup directory and made sure there were 3 control files again:

GBASRDB1 /database/ANDREW/DB1 > cp hot_backup/* .
GBASRDB1 /database/ANDREW/DB1 > ls
1_1424_913830195.dbf  bkup.ctl              sysaux01.dbf          undotbs01.dbf
backup                hot_backup            system01.dbf          users01.dbf
GBASRDB1 /database/ANDREW/DB1 > mv bkup.ctl control01.ctl
GBASRDB1 /database/ANDREW/DB1 > cp control01.ctl control02.ctl
GBASRDB1 /database/ANDREW/DB1 > cp control01.ctl control03.ctl
GBASRDB1 /database/ANDREW/DB1 >


I mounted the database:

GBASRDB1 /database/ANDREW/DB1 > sqlplus / as sysdba

SQL*Plus: Release 11.2.0.4.0 Production on Wed Aug 10 18:02:36 2016

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup mount
ORACLE instance started.

Total System Global Area  521936896 bytes
Fixed Size                  2252448 bytes
Variable Size             322961760 bytes
Database Buffers          188743680 bytes
Redo Buffers                7979008 bytes
Database mounted.
SQL>


I recovered the database using the backup controlfile, applying all the archived redo at my disposal:

SQL> recover database using backup controlfile until cancel
ORA-00279: change 4101278 generated at 08/09/2016 17:47:29 needed for thread 1
ORA-00289: suggestion : /database/ANDREW/DB1/1_1424_913830195.dbf
ORA-00280: change 4101278 for thread 1 is in sequence #1424

Specify log: {<RET>=suggested | filename | AUTO | CANCEL}

ORA-00279: change 4102126 generated at 08/09/2016 18:06:23 needed for thread 1
ORA-00289: suggestion : /database/ANDREW/DB1/1_1425_913830195.dbf
ORA-00280: change 4102126 for thread 1 is in sequence #1425
ORA-00278: log file '/database/ANDREW/DB1/1_1424_913830195.dbf' no longer
needed for this recovery

Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
cancel
Media recovery cancelled.
SQL>


I opened the database:

SQL> alter database open resetlogs
  2  /

Database altered.

SQL>
 


... and checked that my table was still there: 

SQL> select * from system.andrew_was_here
  2  /

      COL1
----------
   1234567

SQL>
 


In the process, Oracle recreated the online redo log files for me:

SQL> select group#, sequence#, status
  2  from v$log
  3  /

    GROUP#  SEQUENCE# STATUS
---------- ---------- ----------------
         1          1 CURRENT
         2          0 UNUSED
         3          0 UNUSED

SQL>

SUBSTR Versus LIKE in Oracle 11.2

$
0
0
I was reading an old SQL tuning book which was printed in 2002. It said that a where clause with like could often use an index whereas a similar clause using substr could not. I wondered if this might still be the case in an Oracle 11.2.0.1 database. To find out, I created a table:

SQL> conn andrew/reid
Connected.
SQL> create table tab1 as
  2  select table_name from dba_tables
  3  /

Table created.

SQL>


... and made sure it had plenty of data:

SQL> begin
  2  for a in 1..12 loop
  3  insert into tab1 select * from tab1;
  4  end loop;
  5  end;
  6  /

PL/SQL procedure successfully completed.

SQL> select count(*) from tab1
  2  /

  COUNT(*)
----------
  13348864

SQL>


I added an extra row which I could look for later:

SQL> insert into tab1 values('DAILY_FORECAST')
  2  /

1 row created.

SQL>


...added an index to help find it:

SQL> create index ind1 on tab1(table_name)
  2  /

Index created.

SQL>


...and collected statistics:

SQL> exec dbms_stats.gather_table_stats(-
> ownname=>'andrew', -
> tabname=>'tab1', -
> cascade=>true);

PL/SQL procedure successfully completed.

SQL>


I used like to find the row and it took 0.39 seconds:

SQL> alter session set sql_trace = true
  2  /

Session altered.

SQL> set timing on
SQL> select count(*) from tab1
  2  where table_name like 'DAILY%'
  3  /

  COUNT(*)
----------
         1

Elapsed: 00:00:00.39

SQL>


... but when I used substr, it took 28.79 seconds:

SQL> select count(*) from tab1
  2  where substr(table_name,1,5) = 'DAILY'
  3  /

  COUNT(*)
----------
         1

Elapsed: 00:00:28.79

SQL> set timing off
SQL> alter session set sql_trace = false
  2  /

Session altered.

SQL>


I ran the trace file through tkprof to see how Oracle had executed the SQL. The statement which used substr had done a full table scan:

********************************************************************************

select count(*) from tab1
where substr(table_name,1,5) = 'DAILY'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      7.16      28.78      38936      38940          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      7.16      28.78      38936      38940          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 8891  (ANDREW)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=38940 pr=38936 pw=0 time=0 us)
      1   TABLE ACCESS FULL TAB1 (cr=38940 pr=38936 pw=0 time=0 us cost=9169 size=2135824 card=133489)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      1   SORT (AGGREGATE)
      1    TABLE ACCESS   MODE: ANALYZED (FULL) OF 'TAB1' (TABLE)

********************************************************************************


...but the statement which used like had used the index:

********************************************************************************

select count(*) from tab1
where table_name like 'DAILY%'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.01          3          3          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.02       0.02          3          3          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 8891  (ANDREW)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=3 pr=3 pw=0 time=0 us)
      1   INDEX RANGE SCAN IND1 (cr=3 pr=3 pw=0 time=0 us cost=19 size=65792 card=4112)(object id 211183)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      1   SORT (AGGREGATE)
      1    INDEX   MODE: ANALYZED (RANGE SCAN) OF 'IND1' (INDEX)

********************************************************************************

How to Automatically Trace a User's Sessions

$
0
0
This post shows how you can use a logon trigger to automatically trace all sessions for a given user. This can be useful where an application is launched from a desktop but fails before the DBA has had time to identify the SID and SERIAL# to start tracing the session. I tested it in an Oracle 11.1.0.6 database running on Windows 8.

First I created a database user:


SQL> create user ford
  2  identified by fiesta
  3  /

User created.

SQL> grant create session,
  2        create trigger,
  3        alter session to ford
  4  /

Grant succeeded.

SQL>


Then I logged in as the user and created a trigger:

SQL> conn ford/fiesta
Connected.
SQL> create or replace trigger immediate_trace
  2  after logon on ford.schema
  3  begin
  4  execute immediate 'alter session set sql_trace = true';
  5  end;
  6  /

Trigger created.

SQL>


I logged in again and ran some SQL:

SQL> conn ford/fiesta
Connected.
SQL> select sysdate from dual
  2  /

SYSDATE
---------
03-SEP-16

SQL>


This produced a trace file ready for further analysis e.g. by tkprof. It included, among other statements, the following:

=====================
PARSING IN CURSOR #3 len=66 dep=1 uid=89 oct=47 lid=89 tim=16846197344563 hv=836160175 ad='b719a518' sqlid='21fcha0sxdkpg'
begin
execute immediate 'alter session set sql_trace = true';
end;
END OF STMT
EXEC #3:c=0,e=23597,p=0,cr=0,cu=0,mis=1,r=1,dep=1,og=1,tim=16846197344558
=====================


and

=====================
PARSING IN CURSOR #3 len=24 dep=0 uid=89 oct=3 lid=89 tim=16846207456189 hv=2343063137 ad='b71c8490' sqlid='7h35uxf5uhmm1'
select sysdate from dual
END OF STMT
PARSE #3:c=0,e=307,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=16846207456188
EXEC #3:c=0,e=14,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=16846207456292
FETCH #3:c=0,e=6,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=1,tim=16846207456323
STAT #3 id=1 cnt=1 pid=0 pos=1 obj=0 op='FAST DUAL  (cr=0 pr=0 pw=0 time=0 us cost=2 size=0 card=1)'
FETCH #3:c=0,e=2,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=16846207502363

*** 2016-09-03 18:30:07.555
XCTEND rlbk=0, rd_only=1
=====================


Incidentally, if you find a trigger like this and you are not sure what it is for, you can find out as follows:

SQL> conn / as sysdba
Connected.
SQL> l
  1  select description
  2  from dba_triggers
  3  where owner = 'FORD'
  4* and trigger_name = 'IMMEDIATE_TRACE'
SQL> /

DESCRIPTION
--------------------------------------------------
immediate_trace
after logon on ford.schema

SQL> l
  1  select trigger_body
  2  from dba_triggers
  3  where owner = 'FORD'
  4* and trigger_name = 'IMMEDIATE_TRACE'
SQL> /

TRIGGER_BODY
----------------------------------------------------------
begin
execute immediate 'alter session set sql_trace = true';
end;

SQL>
 


So far so good but the observant among you may notice that the above is simply a replacement for a couple of posts I published some time ago. You may be wondering what has changed since then. Well, I need to trace sessions for a database user again but this time:

(1) The database produces lots of other trace files which I don't want to look at.
(2) Several OS users log in with this database user but I only want trace files to be produced for one of them, who happens to be called Tobias.

I modified the logon trigger as follows:


SQL> conn ford/fiesta
Connected.
SQL> l
  1  create or replace trigger immediate_trace
  2  after logon on ford.schema
  3  declare
  4    osuser varchar2(200);
  5  begin
  6    select sys_context('USERENV', 'OS_USER')
  7      into osuser from dual;
  8    if osuser = 'NEWPC\Tobias'
  9      then
 10      execute immediate
 11        'alter session set tracefile_identifier = FORD';
 12      execute immediate
 13        'alter session set sql_trace = true';
 14    end if;
 15* end;
SQL> /

Trigger created.

SQL>


Then using my usual OS user i.e. Andrew, I logged into the database user:

SQL> conn ford/fiesta
Connected.
SQL> l
  1  select sys_context('USERENV', 'OS_USER')
  2* from dual
SQL> /

SYS_CONTEXT('USERENV','OS_USER')
--------------------------------------------------

NEWPC\Andrew

SQL> select 'Andrew was here' from dual
  2  /

'ANDREWWASHERE'
---------------
Andrew was here

SQL>


This did not create a trace file.

I went to the Windows 8 account creation screen and set up a user called Tobias. This isn't a Microsoft blog so I'm not going to show you how to do that. I connected to Windows with this new OS user and logged into the database with it:


SQL> conn ford/fiesta
Connected.
SQL> l
  1  select sys_context('USERENV', 'OS_USER')
  2* from dual
SQL> /

SYS_CONTEXT('USERENV','OS_USER')
--------------------------------------------------

NEWPC\Tobias

SQL> select 'Tobias was here' from dual
  2  /

'TOBIASWASHERE'
---------------
Tobias was here

SQL>


This created a trace file, which I could easily see among the other trace files as it was called:

orcl_ora_2548_FORD

... and when I looked inside it, I could see that it had been created by Tobias, not by Andrew:

=====================
PARSING IN CURSOR #4 len=34 dep=0 uid=89 oct=3 lid=89 tim=16850923590446 hv=3733526637 ad='b2ff1f78' sqlid='80cq653g8k63d'
select 'Tobias was here' from dual
END OF STMT
PARSE #4:c=0,e=343,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=16850923590444
EXEC #4:c=0,e=14,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=16850923590540
FETCH #4:c=0,e=5,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=1,tim=16850923590565
STAT #4 id=1 cnt=1 pid=0 pos=1 obj=0 op='FAST DUAL  (cr=0 pr=0 pw=0 time=0 us cost=2 size=0 card=1)'
FETCH #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=16850923590715

*** 2016-09-03 20:06:37.565
XCTEND rlbk=0, rd_only=1
=====================

Deferred Segment Creation not Supported for Partitioned Tables in Oracle 11.2.0.1

$
0
0
I tried to create a partitioned table with deferred segment creation in an Oracle 11.2.0.1 database.

First I tried to do so explicitly but this did not work:

SQL> create table partitioned_table
  2  (refno number)
  3  segment creation deferred
  4  partition by range (refno)
  5  (partition partition1 values less than (10)
  6   tablespace users,
  7   partition partition2 values less than (maxvalue)
  8   tablespace users)
  9  /
create table partitioned_table
*
ERROR at line 1:
ORA-14223: Deferred segment creation is not supported
for this table

SQL>

Then I tried to set the appropriate parameter at session level but when I created a partitioned table, I found that it had 2 segments:

SQL> alter session set deferred_segment_creation = true
  2  /

Session altered.

SQL> create table partitioned_table
  2  (refno number)
  3  partition by range (refno)
  4  (partition partition1 values less than (10)
  5   tablespace users,
  6   partition partition2 values less than (maxvalue)
  7   tablespace users)
  8  /

Table created.

SQL> select count(*) from dba_segments
  2  where segment_name = 'PARTITIONED_TABLE'
  3  /

  COUNT(*)
----------
         2

SQL>

However, when I logged in to an Oracle 11.2.0.4 database, I found that I was able to create a partitioned table with deferred segment creation. (I understand that this was introduced in Oracle 11.2.0.2 but have no database to check this on):

SQL> create table partitioned_table
  2  (refno number)
  3  segment creation deferred
  4  partition by range (refno)
  5  (partition partition1 values less than (10)
  6   tablespace users,
  7   partition partition2 values less than (maxvalue)
  8   tablespace users)
  9  /

Table created.

SQL>

As you would expect, the table had no segments:

SQL> select count(*) from dba_segments
  2  where segment_name = 'PARTITIONED_TABLE'
  3  /

  COUNT(*)
----------
         0

SQL>

...and, as I added data, partitions were only created when they were actually needed:

SQL> insert into partitioned_table values (1)
  2  /

1 row created.

SQL> select count(*) from dba_segments
  2  where segment_name = 'PARTITIONED_TABLE'
  3  /

  COUNT(*)
----------
         1

SQL> insert into partitioned_table values (10)
  2  /

1 row created.

SQL> select count(*) from dba_segments
  2  where segment_name = 'PARTITIONED_TABLE'
  3  /

  COUNT(*)
----------
         2

SQL>

Datapump Does not Export Permissions on Objects Owned by SYS

$
0
0
This post was sponsored by Technimove 

I was reminded recently that Datapump does not export permissions on objects owned by SYS so I decided to write a post about it for my blog. It was tested on an Oracle 11.2.0.1 database. First I created a user called USER1:

SQL> conn / as sysdba
Connected.
SQL> create user user1
  2  identified by user1
  3  /

User created.

SQL> grant create session to user1
  2  /

Grant succeeded.

SQL>


I logged in as USER1 and showed that it did not have execute permission on SYS.DBMS_LOCK:

SQL> conn user1/user1
Connected.
SQL> exec dbms_lock.sleep(1);
BEGIN dbms_lock.sleep(1); END;

      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'DBMS_LOCK' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

SQL>
 

I granted permission then logged in as USER1 again to check it had worked:

SQL> conn / as sysdba
Connected.
SQL> grant execute on dbms_lock to user1
  2  /

Grant succeeded.

SQL> conn user1/user1
Connected.
SQL> exec dbms_lock.sleep(1);

PL/SQL procedure successfully completed.

SQL>


I used expdp to export the schema with the following parameters:

content=all
directory=andrews_datapump_dir
dumpfile=andrew.dmp
logfile=andrew_exp.log
schemas=user1


I used impdp to import the dumpfile into a different schema with the following parameters:

content=all
directory=andrews_datapump_dir
dumpfile=andrew.dmp
logfile=andrew_imp.log
remap_schema=user1:user2
 

…but when I logged in as USER2, it did not have execute permission on SYS.DBMS_LOCK:

SQL> conn user2/user1
Connected.
SQL> exec dbms_lock.sleep(1);
BEGIN dbms_lock.sleep(1); END;

      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'DBMS_LOCK' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

SQL>

SELECT ANY SEQUENCE

$
0
0
I used to think that a user with SELECT ANY TABLE and SELECT ANY DICTIONARY could see anything in a database. I found out today that these 2 privileges do not allow you to SELECT from another user’s sequence. You can see what I mean in the example below, which I tested in an Oracle 11.1 database. First I created a user to own a sequence:
 
SQL> conn / as sysdba
Connected.
SQL> create user user1
  2  identified by user1
  3  /
 
User created.
 
SQL> grant create session, create sequence
  2  to user1
  3  /
 
Grant succeeded.
 
SQL>
 
Then I created a second user to SELECT from the first user’s sequence:
 
SQL> create user user2
  2  identified by user2
  3  /
 
User created.
 
SQL> grant create session,
  2  select any table,
  3  select any dictionary to user2
  4  /
 
Grant succeeded.
 
SQL>
 
USER1 then created a sequence:
 
SQL> conn user1/user1
Connected.
SQL> create sequence sequence1
  2  /
 
Sequence created.
 
SQL> select sequence1.nextval from dual
  2  /
 
   NEXTVAL
----------
         1
 
SQL>
 
… but USER2 could not SELECT from it:
 
SQL> conn user2/user2
Connected.
SQL> select user1.sequence1.nextval from dual
  2  /
select user1.sequence1.nextval from dual
             *
ERROR at line 1:
ORA-01031: insufficient privileges
 
SQL>
 
One way round this is to GRANT the SELECT ANY SEQUENCE privilege like this:
 
SQL> conn / as sysdba
Connected.
SQL> grant select any sequence to user2
  2  /
 
Grant succeeded.
 
SQL> conn user2/user2
Connected.
SQL> select user1.sequence1.nextval from dual
  2  /
 
   NEXTVAL
----------
         2
 
SQL>
Viewing all 330 articles
Browse latest View live