Quantcast
Viewing all 330 articles
Browse latest View live

ORA-01951, ORA-01952 and ORA-01045

I saw a strange question on a forum and decided to reproduce it in an Oracle 12.1 database. First I created a user:
SQL> conn / as sysdba
Connected.
SQL> create user a identified by b
  2  /
 
User created.
 
SQL>
 
Then I found that the user could apparently login without the CREATE SESSION privilege:
 
SQL> conn a/b
Connected.
SQL>
 
After logging in, the user had a role and two privileges:
 
SQL> col role format a30
SQL> select * from session_roles
  2  /
 
ROLE
------------------------------
CONNECT
 
SQL> col privilege format a30
SQL> select * from session_privs
  2  /
 
PRIVILEGE
------------------------------
SET CONTAINER
CREATE SESSION
 
SQL>
 
However, when I tried to revoke the role I got an ORA-01951 and when I tried to revoke the privilege, I got an ORA-01952:
 
SQL> conn / as sysdba
Connected.
SQL> revoke connect from a
  2  /
revoke connect from a
*
ERROR at line 1:
ORA-01951: ROLE 'CONNECT' not granted to 'A'
 
SQL> revoke create session from a
  2  /
revoke create session from a
*
ERROR at line 1:
ORA-01952: system privileges not granted to 'A'
 
SQL>
 
I noticed that CONNECT had been granted to PUBLIC:
 
SQL> col granted_role format a30
SQL> select granted_role
  2  from dba_role_privs
  3  where grantee = 'PUBLIC'
  4  /
 
GRANTED_ROLE
------------------------------
CONNECT
 
SQL>
 
… so I revoked it:
 
SQL> revoke connect from public
  2  /
 
Revoke succeeded.
 
SQL>
 
… and the problem disappeared:
 
SQL> conn a/b
ERROR:
ORA-01045: user A lacks CREATE SESSION privilege; logon denied
 
Warning: You are no longer connected to ORACLE.

DBMS_SYSTEM.KCFRMS

I read about this in the book advertised at the end of this post. It was tested on Oracle 11.2. V$SESSION_EVENT holds similar information to V$SYSTEM_EVENT but it is broken down by session (only currently logged in sessions appear - there is no history). There is a MAX_WAIT column which shows the maximum time a session has had to wait for a particular event.There is no timestamp on this so you cannot tell when the longest wait took place. However, if you have a session which is about to start another step in a process, you can zeroise MAX_WAIT so you know the maximum wait time in that step once it has finished. This zeroises all MAX_WAIT values for all events in all sessions. It also resets MAXIORTM and MAXIOWTM in V$FILESTAT. I decided to give it a try. First I checked the current values:

SQL> conn / as sysdba
Connected.
SQL> select sum(max_wait) from v$session_event
  2  /
 
SUM(MAX_WAIT)
-------------
      1323729
 
SQL> select sum(maxiortm), sum(maxiowtm)
  2  from v$filestat
  3  /
 
SUM(MAXIORTM) SUM(MAXIOWTM)
------------- -------------
         2972          8171
 
SQL>

Then I ran the command to zeroise them:

SQL> exec dbms_system.kcfrms();
 
PL/SQL procedure successfully completed.
 
SQL>

Finally I checked the figures again:

SQL> select sum(max_wait) from v$session_event
  2  /
 
SUM(MAX_WAIT)
-------------
            0
 
SQL> select sum(maxiortm), sum(maxiowtm)
  2  from v$filestat
  3  /
 
SUM(MAXIORTM) SUM(MAXIOWTM)
------------- -------------
            0             0
 
SQL>

Rollback to Savepoint Does Not Release Locks

I read that rolling back to a savepoint releases locks. This sounded reasonable so I decided to check it out in an Oracle 11.2 database. I logged in as user John (in blue) and noted my SID for future reference. Then I created a table, inserted a row, committed the change and created a savepoint. Finally I updated the row but did not commit the change, thus setting up a lock:
 
SQL> conn john/smith
Connected.
SQL> select distinct sid from v$mystat
  2  /
 
       SID
----------
       683
 
SQL> create table tab1
  2  (col1 number)
  3  /
 
Table created.
 
SQL> insert into tab1 values(1)
  2  /
 
1 row created.
 
SQL> commit
  2  /
 
Commit complete.
 
SQL> select * from tab1
  2  /
 
      COL1
----------
         1
 
SQL> savepoint sp1
  2  /
 
Savepoint created.
 
SQL> update tab1 set col1 = 2
  2  /
 
1 row updated.
 
SQL> select * from tab1
  2  /
 
      COL1
----------
         2
 
SQL>
 
I logged into a new session as user Fred (in red), noted my SID again and tried to update the same table. This did nothing, as you might expect:
 
SQL> conn fred/bloggs
Connected.
SQL> select distinct sid from v$mystat
  2  /
 
       SID
----------
         5
 
SQL> update john.tab1 set col1 = 3
  2  /
 
I returned to John’s session and checked that I could see the lock in the DBA_WAITERS view. Then I rolled back to the savepoint and checked that the original value had reappeared in the table (it had). However, the lock was still shown in DBA_WAITERS:
 
SQL> show user
USER is "JOHN"
SQL> select holding_session, waiting_session
  2  from dba_waiters
  3  /
 
HOLDING_SESSION WAITING_SESSION
--------------- ---------------
            683               5
 
SQL> rollback to savepoint sp1
  2  /
 
Rollback complete.
 
SQL> select * from tab1
  2  /
 
      COL1
----------
         1
 
SQL> select holding_session, waiting_session
  2  from dba_waiters
  3  /
 
HOLDING_SESSION WAITING_SESSION
--------------- ---------------
            683               5
 
SQL>
 
I guessed that Oracle must still be holding some kind of lock between the two transactions. There was clearly no lock on the data any more as Donald was able to start a new session (in green) and update it with no problems:
 
SQL> conn donald/duck
Connected.
SQL> update john.tab1 set col1 = 4
  2  /
 
1 row updated.
 
SQL> select * from john.tab1
  2  /
 
      COL1
----------
         4
 
SQL> commit
  2  /
 
Commit complete.
 
SQL>
 
User John finished his transaction and the lock disappeared:
 
SQL> show user
USER is "JOHN"
SQL> commit
  2  /
 
Commit complete.
 
SQL> select holding_session, waiting_session
  2  from dba_waiters
  3  /
 
no rows selected
 
SQL>
 
… and this allowed Fred’s earlier update to finish:
 
SQL> conn fred/bloggs
Connected.
SQL> select distinct sid from v$mystat
  2  /
 
       SID
----------
         5
 
SQL> update john.tab1 set col1 = 3
  2  /
 
1 row updated.
 
SQL>

Case Sensitive Passwords in Oracle 11

In version 11, Oracle passwords became case sensitive. You can see what I mean in the example below:

SQL> conn / as sysdba
Connected.
SQL> alter user system identified by manager
  2  /
 
User altered.
 
SQL> conn system/manager
Connected.
SQL> conn system/MANAGER
ERROR:
ORA-01017: invalid username/password; logon denied
 
Warning: You are no longer connected to ORACLE.
SQL>
 
The DBA_USERS view no longer contains the encrypted password, except when the user is identified externally:
 
SQL> conn / as sysdba
Connected.
SQL> alter user system identified externally
  2  /
 
User altered.
 
SQL> select password from dba_users
  2  where username = 'SYSTEM'
  3  /
 
PASSWORD
------------------------------
EXTERNAL
 
SQL> alter user system identified by manager
  2  /
 
User altered.
 
SQL> select password from dba_users
  2  where username = 'SYSTEM'
  3  /
 
PASSWORD
------------------------------
 
SQL>
 
You can see the Oracle 10 encrypted password in the NAME column of SYS.USER$ and the Oracle 11 encrypted value of the password in the SPARE4 column of the same table:
 
SQL> select password from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
PASSWORD
------------------------------
D4DF7931AB130E37
 
SQL> select spare4 from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
SPARE4
-----------------------------------------------------------------
S:9FA0ADFDC164534A13A388167C4D75BC6C9CF35F7A9CC58F1CDBB1DC7653
 
SQL>
 
If you use the Oracle 10 encrypted value to reset the password, the Oracle 11 encrypted value disappears from the SPARE4 column:
 
SQL> alter user system identified by values 'D4DF7931AB130E37'
  2  /
 
User altered.
 
SQL> select password from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
PASSWORD
------------------------------
D4DF7931AB130E37
 
SQL> select spare4 from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
SPARE4
-----------------------------------------------------------------
 
SQL>
 
… and the password is no longer case sensitive:
 
SQL> conn system/manager
Connected.
SQL> conn system/MANAGER
Connected.
SQL>
 
Conversely, if you use the Oracle 11 encrypted value to reset the password, the Oracle 10 encrypted value disappears from the PASSWORD column:
 
SQL> alter user system identified by values
  2  'S:FA743A680B015952982BB2B501B7BC623F1AA11093AC95E58D67B18A2AC2'
  3  /
 
User altered.
 
SQL> select password from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
PASSWORD
------------------------------
 
SQL> select spare4 from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
SPARE4
-----------------------------------------------------------------
S:FA743A680B015952982BB2B501B7BC623F1AA11093AC95E58D67B18A2AC2
 
SQL>
 
… and the password becomes case sensitive again:
 
SQL> conn system/manager
Connected.
SQL> conn system/MANAGER
ERROR:
ORA-01017: invalid username/password; logon denied
 
Warning: You are no longer connected to ORACLE.
SQL>

Segment Creation Deferred and ORA-02266

If you try to truncate a table with a primary key which is referenced by an enabled foreign key, you usually get an ORA-02266 error. This happens straight away if the table is set up with segment creation immediate. However, if the table is set up with segment creation deferred, the error is not reported until the segment has been created. You can see what I mean in the example below, which I tested on Oracle 11.2:
 
SQL> create table dept1
  2  (dept_code varchar2(4),
  3   dept_desc varchar2(20),
  4   constraint dept1_constraint
  5   primary key(dept_code))
  6   segment creation deferred
  7  /
 
Table created.
 
SQL> create table emp1
  2  (empno varchar2(6),
  3   dept_code varchar2(4),
  4   constraint emp1_constraint
  5   foreign key (dept_code)
  6   references dept1(dept_code))
  7  /
 
Table created.
 
SQL> select count(*) from dba_segments
  2  where segment_name = 'DEPT1'
  3  /
 
  COUNT(*)
----------
         0
 
SQL> truncate table dept1
  2  /
 
Table truncated.
 
SQL> insert into dept1 values('0001','SALES')
  2  /
 
1 row created.
 
SQL> select count(*) from dba_segments
  2  where segment_name = 'DEPT1'
  3  /
 
  COUNT(*)
----------
         1
 
SQL> truncate table dept1
  2  /
truncate table dept1
               *
ERROR at line 1:
ORA-02266: unique/primary keys in table referenced by
enabled foreign keys
 
SQL> create table dept2
  2  (dept_code varchar2(4),
  3   dept_desc varchar2(20),
  4   constraint dept2_constraint
  5   primary key(dept_code))
  6   segment creation immediate
  7  /
 
Table created.
 
SQL> create table emp2
  2  (empno varchar2(6),
  3   dept_code varchar2(4),
  4   constraint emp2_constraint
  5   foreign key (dept_code)
  6   references dept2(dept_code))
  7  /
 
Table created.
 
SQL> truncate table dept2
  2  /
truncate table dept2
               *
ERROR at line 1:
ORA-02266: unique/primary keys in table referenced by
enabled foreign keys
 
SQL>

SUCCESS and FAILURE Columns in DBA_STMT_AUDIT_OPTS

I tested this in Oracle 10.2. DBA_STMT_AUDIT_OPTS records auditing options which are currently in effect. If no auditing has been requested, it will be empty:
 
SQL> select user_name, audit_option, success, failure
  2  from dba_stmt_audit_opts
  3  /
 
no rows selected
 
SQL>
 
You can audit successful connections as follows:
 
SQL> audit create session by system whenever successful
  2  /
 
Audit succeeded.
 
SQL>
 
This will then be recorded in DBA_STMT_AUDIT_OPTS. The SUCCESS column will be set to BY ACCESS to show that only successful connections are being audited:   
 
SQL> select user_name, audit_option, success, failure
  2  from dba_stmt_audit_opts
  3  /
 
USER_NAME  AUDIT_OPTION    SUCCESS    FAILURE
---------- --------------- ---------- ----------
SYSTEM     CREATE SESSION  BY ACCESS  NOT SET
 
SQL>
 
You can stop the auditing like this:
 
SQL> noaudit create session by system whenever successful
  2  /
 
Noaudit succeeded.
 
SQL>
 
…and the entry will disappear from DBA_STMT_AUDIT_OPTS:
 
SQL> select user_name, audit_option, success, failure
  2  from dba_stmt_audit_opts
  3  /
 
no rows selected
 
SQL>
 
Here is how you audit unsuccessful connections. Note that in the context of the AUDIT and NOAUDIT commands, CREATE SESSION and CONNECT have the same meaning:
 
SQL> audit connect by system whenever not successful
  2  /
 
Audit succeeded.
 
SQL>
 
This will also be recorded in DBA_STMT_AUDIT_OPTS but the FAILURE column will be set to BY ACCESS to show that only failed connections are being audited:   
 
 
SQL> select user_name, audit_option, success, failure
  2  from dba_stmt_audit_opts
  3  /
 
USER_NAME  AUDIT_OPTION    SUCCESS    FAILURE
---------- --------------- ---------- ----------
SYSTEM     CREATE SESSION  NOT SET    BY ACCESS
 
SQL>
 
As before, stopping the auditing removes the entry from DBA_STMT_AUDIT_OPTS:
 
SQL> noaudit connect by system whenever not successful
  2  /
 
Noaudit succeeded.
 
SQL> select user_name, audit_option, success, failure
  2  from dba_stmt_audit_opts
  3  /
 
no rows selected
 
SQL>

A Difference Between SQL*Plus and SQL Developer

A third-party supplier delivered some SQL today but it did not work in SQL*Plus. We asked the supplier about this and it turned that the code had been tested in SQL Developer. The reason for the failure was as follows. If you end a line of SQL with a semi-colon then add a comment afterwards, SQL*Plus rejects it with an ORA-00911

SQL> @test1
SQL> set echo on
SQL> select 'Comment->' from dual; /*Andrew was here*/
  2  select 'More SQL' from dual;
select 'Comment->' from dual; /*Andrew was here*/
                            *
ERROR at line 1:
ORA-00911: invalid character
 
SQL>

To get the code to work, you need to include the comment before the semi-colon:

SQL> @test2
SQL> set echo on
SQL> select 'Comment->' from dual /*Andrew was here*/;
 
'COMMENT-
---------
Comment->
 
SQL> select 'More SQL' from dual;
 
'MORESQL
--------
More SQL
 
SQL>

However, if you try this in SQL Developer, both options work (as usual, click on the images to enlarge them and bring them into focus):

 
 

ORA-01114 Not Recorded Correctly in Alert Log

A user reported an ORA-01114 and an ORA-27069 in a 3rd party application running against an Oracle 11.1 database:

ERROR 28/01/2015 10:32:37  INF2 LKPDP_10:READER_1_1  RR_4035    SQL Error [
ORA-01114: IO error writing block to file 201 (block # 524367)
ORA-27069: attempt to do I/O beyond the range of the file
Additional information: 524367
Additional information: 7
Additional information: 524289
ORA-01114: IO error writing block to file 201 (block # 524367)
ORA-27069: attempt to do I/O beyond the range of the file
Additional information: 524367
Additional information: 7
Additional information: 524289

I searched the database’s alert log but could not find either of the errors. I asked the user when this had happened and looked at the corresponding place in the alert log. Then I noticed that the ORA-01114 had been recorded without the leading zero:

Wed Jan 28 10:32:51 2015
ORA-1114 : opiodr aborting process unknown ospid (23531_1)
 
… but there was still no sign of the ORA-27069.

ORA-00600: internal error code, arguments: [kzdlk_zt2 err]

This example is based on an error which a colleague showed me recently. I logged into an Oracle 11.1 database, reset its SYSTEM password and checked the encrypted value: 

Oracle11 > sqlplus / as sysdba
 
SQL*Plus: Release 11.1.0.6.0 - Production on Wed Feb 4 18:10:38 2015
 
Copyright (c) 1982, 2007, Oracle.  All rights reserved.
 
Connected to:
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
 
SQL> select name from v$database
  2  /
 
NAME
---------
PQEDPT1
 
SQL> alter user system
  2  identified by manager
  3  /
 
User altered.
 
SQL> select password from sys.user$
  2  where name = 'SYSTEM'
  3  /
 
PASSWORD
------------------------------
D4DF7931AB130E37
 
SQL>

I then logged into an Oracle 11.2 database, created a link to the database above, tried to use it and saw an error:

Oracle11 > sqlplus system/manager
 
SQL*Plus: Release 11.2.0.1.0 Production on Wed Feb 4 18:13:39 2015
 
Copyright (c) 1982, 2009, Oracle.  All rights reserved.
 
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
 
SQL> select name from v$database
  2  /
 
NAME
---------
BUSDPT1
 
SQL> create database link andrew
  2  connect to system
  3  identified by values 'D4DF7931AB130E37'
  4  using 'PQEDPT1'
  5  /
 
Database link created.
 
SQL> select * from dual@andrew
  2  /
select * from dual@andrew
                   *
ERROR at line 1:
ORA-00600: internal error code, arguments: [kzdlk_zt2 err],
[18446744073709551603], [], [], [], [], [], [], [], [], [], []
 
SQL>

I did some research and it seems that the identified by values syntax above is an undocumented feature. I altered the database link, supplying the unencrypted value, and it worked:

SQL> alter database link andrew
  2  connect to system
  3  identified by manager
  4  /
alter database link andrew
                    *
ERROR at line 1:
ORA-01031: insufficient privileges
 
SQL> grant alter database link to system
  2  /
 
Grant succeeded.
 
SQL> alter database link andrew
  2  connect to system
  3  identified by manager
  4  /
 
Database link altered.
 
SQL> select * from dual@andrew
  2  /
 
D
-
X
 
SQL>

So, if you are creating a new database link, you need to do it like this:

SQL> drop database link andrew
  2  /
 
Database link dropped.
 
SQL> create database link andrew
  2  connect to system
  3  identified by manager
  4  using 'PQEDPT1'
  5  /
 
Database link created.
 
SQL> select * from dual@andrew
  2  /
 
D
-
X

How Many Values Can You Have in an IN List?

I have often wondered how many values you could have following an IN and I have just found out. I loaded some new data into a name and address table in an Oracle 11 database over the weekend. On Monday, a user sent me an Excel spreadsheet containing a list of almost 18000 meter point references to search for in the table. I exported them into a file, copied it to the server and used vi to add a comma at the end of each line. Then I added a query at the start of the data:

select * from m_number_detail
where mpo_reference in (
0002538404,
0002538505,
0001312900,
0001313408,
0006175302,
Etc
Etc

When I tried to run it, I saw the following message at the end of the SPOOL file:

Etc
Etc
17906  8815785700,
17907  8817723609,
17908  0072096508,
17909  0072096710,
17910  0925482304)
17911  /
0005567909,
*
ERROR at line 1003:
ORA-01795: maximum number of expressions in a list is 1000
 
SQL>

Oracle "read by other session" Wait Event

When a session needs to read data from disk into the Oracle buffer cache, it may have to wait for another session to finish doing the same thing. Time spent doing this is recorded as a read by other session event. I decided to reproduce this in an Oracle 12 database. First, in session 1, in red, I set up a user called Fred to create a table:

SQL> conn / as sysdba
Connected.
SQL> create user fred
  2  identified by bloggs
  3  default tablespace users
  4  quota unlimited on users
  5  /
 
User created.
 
SQL> grant create session, create table,
  2  select any dictionary, alter system to fred
  3  /
 
Grant succeeded.
 
SQL>

Still in session 1, Fred then created a table, added some data and checked that all the rows were in the same block:

SQL> conn fred/bloggs
Connected.
SQL> create table t1 (c1 number)
  2  /
 
Table created.
 
SQL> begin
  2  for i in 1..200 loop
  3  insert into t1 values(1);
  4  end loop;
  5  end;
  6  /
 
PL/SQL procedure successfully completed.
 
SQL> select dbms_rowid.rowid_block_number(rowid), count(*)
  2  from t1
  3  group by dbms_rowid.rowid_block_number(rowid)
  4  order by 1
  5  /
 
DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)   COUNT(*)
------------------------------------ ----------
                                 223        200
 
SQL>
 
Then he started updating the data. After each update, he did a commit so that other sessions could see the changed data and a checkpoint to write the changes to disk:

SQL> begin
  2  while (1=1) loop
  3  update t1 set c1 = c1 + 1;
  4  commit;
  5  execute immediate 'alter system checkpoint';
  6  end loop;
  7  end;
  8  /
 
In 5 separate sessions in blue (sessions 2 through 6 inclusive), Fred repeatedly flushed the Oracle buffer cache and read the table into it: 

SQL> conn fred/bloggs
Connected.
SQL> declare
  2  grand_total number;
  3  begin
  4  while (1=1) loop
  5  execute immediate 'alter system flush buffer_cache';
  6  select sum(c1) into grand_total from t1;
  7  end loop;
  8  end;
  9  /

In a 7th session,in green, Fred read the table into the buffer cache 10000 times. While he was doing this he had to wait for sessions 2 through 6, which were doing the same thing:

SQL> conn fred/bloggs
Connected.
SQL> declare
  2  grand_total number;
  3  begin
  4  for i in 1..10000 loop
  5  execute immediate 'alter system flush buffer_cache';
  6  select sum(c1) into grand_total from t1;
  7  end loop;
  8  end;
  9  /
 
PL/SQL procedure successfully completed.
 
SQL>

So, when he finished, the time spent waiting on the read by other session event could clearly be seen: 

SQL> select event, time_waited/100
  2  from v$session_event
  3  where sid = (select distinct sid from v$mystat)
  4  /
 
EVENT                               TIME_WAITED/100
----------------------------------- ---------------
Disk file operations I/O                        .03
latch: cache buffers chains                     .01
buffer busy waits                                 0
read by other session                         33.36
db file sequential read                        7.35
db file scattered read                        22.07
db file parallel read                            .2
latch: In memory undo latch                       0
latch: row cache objects                          0
library cache: mutex X                            0
SQL*Net message to client                         0
SQL*Net message from client                     .02
events in waitclass Other                    405.87
 
13 rows selected.
 
SQL>

Bug 5497611

I used Oracle Enterprise Manager to look at the execution plan for some SQL in an Oracle 10.2.0.3 database. (The SQL shown is just an example done later for the purposes of this blog post. As usual, click on the image to enlarge it and bring it into focus if necessary.):


This produced the following ORA-00600 message several times in the alert log:
 
Wed Oct  1 18:13:21 2014
Errors in file /oracle/app/oracle/product/10.2.0/admin/mrmdpt1/udump/mrmdpt1_ora_15467.trc:
ORA-00600: internal error code, arguments: [qctVCO:csform], [0], [0], [0], [0], [112], [2], [224]
 
I looked in the trace file and saw the SQL which had caused the problem:
 
ORA-00600: internal error code, arguments: [qctVCO:csform], [0], [0], [0], [0], [112], [2], [224]
Current SQL statement for this session:
SELECT extractvalue(xmlval, '/*/info[@type = "sql_profile"]'), extractvalue(xmlval, '/*/info[@type = "outline"]'), extractvalue(xmlval, '/*/info[@type = "dyn
amic_sampling"]'), nvl(extractvalue(xmlval, '/*/info[@type = "index_size"]'), -1) from (select xmltype(other_xml) xmlval from v$sql_plan where sql_id = :1 an
d other_xml is not null and id = 1)

I extracted the SQL and ran it in a SQL*Plus session:

SQL> set head off
SQL> l
  1  SELECT
  2  extractvalue(xmlval, '/*/info[@type = "sql_profile"]'),
  3  extractvalue(xmlval, '/*/info[@type = "outline"]'),
  4  extractvalue(xmlval, '/*/info[@type = "dynamic_sampling"]'),
  5  nvl(extractvalue(xmlval, '/*/info[@type = "index_size"]'), -1)
  6  from
  7  (select xmltype(other_xml) xmlval
  8   from v$sql_plan
  9   where sql_id = 'cp5caasd2udnw'
 10   and other_xml is not null
 11*  and id = 1)
SQL> /
 
 
 
 
-1
 
 
 
 
-1
 
 
SQL>
 
Each time I did this, a similar ORA-00600 appeared in the alert log, proving that I had correctly diagnosed the cause of the problem. I looked the problem up in My Oracle Support and found that it was caused by bug 5497611.

VARCHAR and VARCHAR2

If you create a table with a VARCHAR column in Oracle 11.2, Oracle sets it up as a VARCHAR2:

SQL> l
  1  create table t1
  2  (c1 varchar(1),
  3*  c2 varchar2(1))
SQL> /
 
Table created.
 
SQL> desc t1
Name                       Null?    Type
-------------------------- -------- ------------------
C1                                  VARCHAR2(1)
C2                                  VARCHAR2(1)
 
SQL>

According to a book I am working through, this has been the case since Oracle 8. However, Oracle say you should not rely on this as, in a future release, the specifications for VARCHAR and VARCHAR2 may diverge.

Default Size of a CHAR Column

If you do not specify a size for a CHAR column, the default is 1. You can see what I mean in the example below, which I tested on Oracle 11.2:

SQL> create table t1
  2  (c1 char,
  3   c2 char(1))
  4  /
 
Table created.
 
SQL> desc t1
Name                       Null?    Type
-------------------------- -------- ------------------
C1                                  CHAR(1)
C2                                  CHAR(1)
 
SQL> 

However, if you rely on defaults like this and the software supplier changes them, you could be left with an application which does not work. 

ORA-02391

I tested this on an Oracle 11.1 database.
 
Oracle profiles control how certain database resources are allocated to a user session. They also define some security rules. When you create a user, it is assigned a profile and, if you do not specify it explicitly, the DEFAULT profile will be used:
 
SQL> grant create session to andrew
  2  identified by reid
  3  /
 
Grant succeeded.
 
SQL> select profile from dba_users
  2  where username = 'ANDREW'
  3  /
 
PROFILE
------------------------------
DEFAULT
 
SQL>
 
You can look at the limits defined in a profile by querying DBA_PROFILES:
 
SQL> select profile, resource_name, limit
  2  from dba_profiles
  3  where profile = 'DEFAULT'
  4  and resource_name = 'SESSIONS_PER_USER'
  5  /
 
PROFILE    RESOURCE_NAME        LIMIT
---------- -------------------- ----------
DEFAULT    SESSIONS_PER_USER    UNLIMITED
 
SQL>
 
The SESSIONS_PER_USER resource is set to UNLIMITED so a user with the DEFAULT profile can have as many simultaneous sessions as he wishes. What can you do if you do not like this? You can create a new profile with a different SESSIONS_PER_USER limit and assign it to the user. I will demonstrate this in a future post. Alternatively you can alter the DEFAULT profile, as shown below:
 
SQL> alter profile default
  2  limit sessions_per_user 3
  3  /
 
Profile altered.
 
SQL> select profile, resource_name, limit
  2  from dba_profiles
  3  where profile = 'DEFAULT'
  4  and resource_name = 'SESSIONS_PER_USER'
  5  /
 
PROFILE    RESOURCE_NAME        LIMIT
---------- -------------------- ----------
DEFAULT    SESSIONS_PER_USER    3
 
SQL>
 
You also need to set RESOURCE_LIMIT to TRUE otherwise Oracle does not check limits in a user's profile at all:
 
SQL> alter system set resource_limit = true
  2  /
 
System altered.
 
SQL>
 
The screen print below shows how this works (as usual, click on the image to enlarge it and bring it into focus if necessary).Three simultaneous connections have been made to the database. A fourth connection is then attempted but fails with an ORA-02391:



FULL and NO_INDEX Hints

I was reading about hints and decided to try out a couple on an Oracle 11.2 database. First I created a table, added some data and created an index:

SQL> create table t1 (c1 varchar2(30))
  2  /
 
Table created.
 
SQL> insert into t1 select table_name from dba_tables
  2  /
 
3159 rows created.
 
SQL> create index i1 on t1(c1)
  2  /
 
Index created.
 
SQL>

I ran a SELECT statement on the table. I thought it would use the index and it did:

SQL> set autotrace on explain
SQL> select count(*)
  2  from t1
  3  where c1 = 'T1'
  4  /
 
  COUNT(*)
----------
         1
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2349582935
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    17 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |      |     1 |    17 |            |          |
|*  2 |   INDEX RANGE SCAN| I1   |     1 |    17 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("C1"='T1')
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
SQL>

I then added a full hint to force the optimizer to choose a full table scan and this worked too:
 
SQL> select /*+ full(t1) */ count(*)
  2  from t1
  3  where c1 = 'T1'
  4  /
 
  COUNT(*)
----------
         1
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3693069535
 
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    17 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    17 |            |          |
|*  2 |   TABLE ACCESS FULL| T1   |     1 |    17 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("C1"='T1')
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
SQL>

I added an alias after the table name. The hint stopped working and the query used the index again:

SQL> select /*+ full(t1) */ count(*)
  2  from t1 my_alias
  3  where c1 = 'T1'
  4  /
 
  COUNT(*)
----------
         1
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 2349582935
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    17 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |      |     1 |    17 |            |          |
|*  2 |   INDEX RANGE SCAN| I1   |     1 |    17 |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("C1"='T1')
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
SQL>

That was because, if you use an alias, the hint must specify the alias, not the table name. I changed the hint to do this and the optimizer chose a full table scan again:

SQL> select /*+ full(my_alias) */ count(*)
  2  from t1 my_alias
  3  where c1 = 'T1'
  4  /
 
  COUNT(*)
----------
         1
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3693069535
 
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    17 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    17 |            |          |
|*  2 |   TABLE ACCESS FULL| T1   |     1 |    17 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("C1"='T1')
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
SQL>

It occurred to me that I might also be able to force a full table scan by telling Oracle not to use a particular index. I did this with a no_index hint and it worked as expected:

SQL> select /*+ no_index(t1 i1) */ count(*)
  2  from t1
  3  where c1 = 'T1'
  4  /
 
  COUNT(*)
----------
         1
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3693069535
 
---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    17 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |    17 |            |          |
|*  2 |   TABLE ACCESS FULL| T1   |     1 |    17 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter("C1"='T1')
 
Note
-----
   - dynamic sampling used for this statement (level=2)
 
SQL>

ORA-04000

I created a table in an Oracle 11.2 database. I did not specify pctfree or pctused so it was given the defaults of 10% and 40% respectively:

SQL> conn system/manager
Connected.
SQL> create table t1 (c1 number)
  2  /

Table created.

SQL> select pct_free, pct_used
  2  from user_tables
  3  where table_name = 'T1'
  4  /

  PCT_FREE   PCT_USED
---------- ----------
        10         40


SQL>

When Oracle inserts rows into a table, the pctfree specifies the percentage of space to leave free in each block for subsequent updates to the rows. This free space is used later if an extra column is added to the table or if a varchar2 column is updated to store a longer value than before. Once the pctfree in a given block falls below the specified value, no new rows can be inserted in that block so it is removed from the free list. It is then not allowed to accept new rows until the percentage of space used in the block falls below the pctused figure. When this happens, the block goes back onto the free list again. You can alter the pctfree and/or pctused settings like this:

SQL> alter table t1 pctfree 20
  2  /

Table altered.


SQL>

Going through some old notes from an Oracle 9 performance tuning course, I read that the sum of pctfree and pctused cannot be more than 100. This seemed reasonable. If you had a pctused of 40%, a pctfree of 70% and a block which was 35% full, Oracle would not know what to do with it. The pctused figure of 40% would tell Oracle to leave the block on the free list wheras the pctfree figure of 70% would tell Oracle to remove it (from the free list). In situations like this, Oracle usually has a special error message to display. In this case, it is ORA-04000, as you can see below:

SQL> alter table t1 pctfree 70
  2  /
alter table t1 pctfree 70
*
ERROR at line 1:
ORA-04000: the sum of PCTUSED and PCTFREE cannot
exceed 100

SQL>

Using NOT = in a WHERE Clause

I ran this test in Oracle 12.1. First I created a table, added some data to it and made sure that the bytes column was set to zero in every row:
 
SQL> conn system/manager
Connected.
SQL> alter session set optimizer_mode = first_rows
  2  /
 
Session altered.
 
SQL> create table t1
  2  tablespace users
  3  as select segment_name, bytes
  4  from dba_segments
  5  /
 
Table created.
 
SQL> update t1 set bytes = 0
  2  /
 
5757 rows updated.
 
SQL> begin
  2  for a in 1..4 loop
  3  insert into t1 select * from t1;
  4  end loop;
  5  end;
  6  /
 
PL/SQL procedure successfully completed.
 
SQL>
 
Then I added one row with a value greater than zero in the bytes column:
 
SQL> insert into t1 (segment_name, bytes)
  2  values ('ABC123',1000)
  3  /
 
1 row created.
 
SQL>
 
I created an index and gathered statistics on the table:
 
SQL> create index i1 on t1(bytes)
  2  tablespace users
  3  /
 
Index created.
 
SQL> exec sys.dbms_stats.gather_table_stats(-
> ownname=>'SYSTEM',tabname=>'T1');
 
PL/SQL procedure successfully completed.
 
SQL>
 
I tried to find the row with the non-zero value in the bytes column like this. The NOT = (!=) made Oracle choose to do a full table scan instead of using the index:
 
SQL> set autotrace on
SQL> select segment_name from t1 where bytes != 0
  2  /
 
SEGMENT_NAME
------------------------------
ABC123
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013
 
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 46057 |  1034K|   104   (1)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T1   | 46057 |  1034K|   104   (1)| 00:00:01 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("BYTES"<>0)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        374  consistent gets
        371  physical reads
          0  redo size
        550  bytes sent via SQL*Net to client
        543  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
SQL>

I wanted to make Oracle use the index. In situations like this it is sometimes possible to change a query but still get the same results. In this example, you would not expect to see a negative value in the bytes column. So you could write the SQL like this instead:

SQL> select segment_name from t1 where bytes > 0
  2  /
 
SEGMENT_NAME
------------------------------
ABC123
 
 
Execution Plan
----------------------------------------------------------
Plan hash value: 37133972
 
--------------------------------------------------------------------------------------------
| Id  | Operation                           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |      | 46057 |  1034K|   263   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T1   | 46057 |  1034K|   263   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                  | I1   | 46057 |       |    85   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - access("BYTES">0)
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          3  consistent gets
          3  physical reads
          0  redo size
        557  bytes sent via SQL*Net to client
        543  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
SQL>

This time, Oracle decided to use the index. The observant among you may be wondering why I set optimizer_mode to first_rows at the start. That was because the query refused to use the index at all when it was set to all_rows.

What is an INDEX SKIP SCAN?

I tested this on Oracle 11.2. First I created a table with two columns. The first column, OWNER, had low cardinality i.e. it had only two possible values, PUBLIC and SYS. The second column, OBJECT_NAME, had the names of all objects owned by PUBLIC and/or SYS so it had high cardinality i.e. it had many different values:

SQL> conn system/manager
Connected.
SQL> create table t1 as
  2  select owner, object_name
  3  from dba_objects
  4  where owner in ('SYS', 'PUBLIC')
  5  /
 
Table created.
 
SQL> select owner, count(*)
  2  from t1
  3  group by owner
  4  /
 
OWNER                            COUNT(*)
------------------------------ ----------
PUBLIC                              29041
SYS                                 31907
 
SQL>

I created an index on the table and gathered statistics:

SQL> create index i1 on t1(owner, object_name)
  2  /
 
Index created.
 
SQL> exec sys.dbms_stats.gather_table_stats(-
> ownname=>'SYSTEM',tabname=>'T1');
 
PL/SQL procedure successfully completed.
 
SQL>

Then I counted the number of rows with an OBJECT_NAME of DUAL. There were two, one owned by SYS and the other owned by PUBLIC. You might think that Oracle would not have been able to use the index, as OBJECT_NAME was not the leading column. However, as the leading column in the index, OWNER, had low cardinality, Oracle could SCAN the entries in the index for PUBLIC and see if any had an OBJECT_NAME of DUAL. It could then SKIP to the index entries for SYS and look for an OBJECT_NAME of DUAL there. This is why it is called an INDEX SKIP SCAN:

SQL> set autotrace on
SQL> select count(*) from t1
  2  where object_name = 'DUAL'
  3  /
 
  COUNT(*)
----------
         2
 
 
Execution Plan
----------------------------------------------------------
 
---------------------------------------------------------
| Id  | Operation        | Name | Rows  | Bytes | Cost  |
---------------------------------------------------------
|   0 | SELECT STATEMENT |      |     1 |    25 |     3 |
|   1 |  SORT AGGREGATE  |      |     1 |    25 |       |
|   2 |   INDEX SKIP SCAN| I1   |     2 |    50 |     3 |
---------------------------------------------------------
 
Note
-----
   - 'PLAN_TABLE' is old version
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        526  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
SQL>

I changed the test to include all the rows from DBA_OBJECTS, not just those owned by PUBLIC and/or SYS then reran it. Table T1 had 51 different values in the OWNER column so Oracle did a full table scan instead:

SQL> conn system/manager
Connected.
SQL> create table t1 as
  2  select owner, object_name
  3  from dba_objects
  4  /
 
Table created.
 
SQL> select count(distinct owner) from t1
  2  /
 
COUNT(DISTINCTOWNER)
--------------------
                  51
 
SQL> create index i1 on t1(owner, object_name)
  2  /
 
Index created.
 
SQL> exec sys.dbms_stats.gather_table_stats(-
> ownname=>'SYSTEM',tabname=>'T1');
 
PL/SQL procedure successfully completed.
 
SQL> set autotrace on
SQL> select count(*) from t1
  2  where object_name = 'DUAL'
  3  /
 
  COUNT(*)
----------
         2
 
 
Execution Plan
----------------------------------------------------------
 
-----------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost  |
-----------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |    24 |    31 |
|   1 |  SORT AGGREGATE    |      |     1 |    24 |       |
|   2 |   TABLE ACCESS FULL| T1   |     2 |    48 |    31 |
-----------------------------------------------------------
 
Note
-----
   - 'PLAN_TABLE' is old version
 
 
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        362  consistent gets
          0  physical reads
          0  redo size
        526  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
SQL>

How to See the Height of an Index

This example shows where to find the height of an index. I tested it on Oracle 11.2. First I deleted the index's statistics:

SQL> exec dbms_stats.delete_index_stats -
> ('uimsmgr','ubbchst_serv_index');
 
PL/SQL procedure successfully completed.
 
SQL>
 
Then I checked its BLEVEL was null:

SQL> select nvl(blevel,999)
  2  from dba_indexes
  3  where owner = 'UIMSMGR'
  4  and index_name = 'UBBCHST_SERV_INDEX'
  5  /
 
NVL(BLEVEL,999)
---------------
            999
 
SQL>
 
I recalculated the index's statistics:
 
SQL> exec dbms_stats.gather_index_stats -
> ('uimsmgr','ubbchst_serv_index');
 
PL/SQL procedure successfully completed.
 
SQL>
 
After that, the BLEVEL column in DBA_INDEXES showed the index’s height:

SQL> select nvl(blevel,999)
  2  from dba_indexes
  3  where owner = 'UIMSMGR'
  4  and index_name = 'UBBCHST_SERV_INDEX'
  5  /
 
NVL(BLEVEL,999)
---------------
              3
 
SQL>
 
I used to calculate an index's height by analyzing it then looking in INDEX_STATS. First I checked that INDEX_STATS was empty (you will see why I showed the username later):

SQL> select name, height from index_stats
  2  /
 
no rows selected
 
SQL> show user
USER is "ORACLE"
SQL>
 
Then I analyzed the index:

SQL> analyze index uimsmgr.ubbchst_serv_index
  2  validate structure
  3  /
 
Index analyzed.
 
SQL>
 
... and checked its height in INDEX_STATS again. This height comes out as BLEVEL + 1. I guess Oracle starts to count it from a different place as this happened in Oracle 9 too:
 
SQL> select name, height from index_stats
  2  /
 
NAME                               HEIGHT
------------------------------ ----------
UBBCHST_SERV_INDEX                      4
 
SQL>
 
INDEX_STATS is a public synonym for the SYS.INDEX_STATS view. It is far too complicated for me to follow but I know that it never has more than one row, which holds details for the index you analyzed most recently. It is also session specific so, if you start a new session, it will be empty again:
 
SQL> conn / as sysdba
Connected.
SQL> show user
USER is "SYS"
SQL> select name, height from index_stats
  2  /
 
no rows selected
 
SQL>
 
You can read more about this starting on page 69 of the book advertised below:


Viewing all 330 articles
Browse latest View live