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

ORA-01031 Creating a View on a Table Accessed via a Role

$
0
0
This example shows that you cannot create a view on a table which you access via a role. It was tested on Oracle 11. First I created a role:

SQL> create role andrews_role
  2  /

Role created. 

SQL>

Then I created a user called ANDREW1, who will create a table:

SQL> create user andrew1
  2  identified by reid1
  3  default tablespace users
  4  quota unlimited on users
  5  /

User created.

SQL> grant create session, create table
  2  to andrew1
  3  /

Grant succeeded. 

SQL> 

Next I created a user called ANDREW2, who will try to read that table directly and via a view:

SQL> create user andrew2
  2  identified by reid2
  3  /

User created.

SQL> grant create session, create view,
  2  andrews_role to andrew2
  3  /

Grant succeeded.

SQL>

I connected as ANDREW1, created a table and granted SELECT access on it to ANDREWS_ROLE:

SQL> connect andrew1/reid1
Connected.
SQL> create table numbers
  2  as select '1234' col1 from dual
  3  /
 
Table created.
 
SQL> grant select on numbers to andrews_role
  2  /
 
Grant succeeded.

SQL>

At this point, ANDREW2 could see the NUMBERS table via ANDREWS_ROLE: 

SQL> conn andrew2/reid2
Connected.

SQL> select * from andrew1.numbers
  2  /
 
COL1
----
1234
 
SQL>

... but he could not create a view to look at ANDREW1.NUMBERS:

SQL> create view numbers
  2  as select * from andrew1.numbers
  3  /
as select * from andrew1.numbers
                         *
ERROR at line 2:
ORA-01031: insufficient privileges
SQL> 

ANDREW1 then granted the SELECT privilege directly to ANDREW2:

SQL> conn andrew1/reid1
Connected.
SQL> grant select on numbers to andrew2
  2  /
 
Grant succeeded.

SQL>

This allowed ANDREW2 to create a view to look at ANDREW1.NUMBERS:

SQL> conn andrew2/reid2
Connected.
SQL> create view numbers
  2  as select * from andrew1.numbers
  3  /

View created.

SQL> select * from numbers
  2  /

COL1
----
1234

SQL>


AFTER DELETE Triggers do not Run After a Truncate

$
0
0
In this example, I want to show that Oracle does not execute an AFTER DELETE trigger after doing a TRUNCATE. I tested it on Oracle 11.2. First I created a table:

SQL> create table tab1 (my_name varchar2(10))
  2  /

Table created. 

SQL>

Then I inserted a row into it: 

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

1 row created.

SQL> commit
  2  /

Commit complete.

SQL> select * from tab1
  2  /

MY_NAME
----------
Andrew

SQL>

Next I created a second table:

SQL> create table tab2 (my_name varchar2(10))
  2  /

Table created.

SQL>

I created a trigger to insert rows into TAB2 after deleting them from TAB1:

SQL> create or replace trigger trig1
  2  after delete on tab1
  3  for each row
  4  begin
  5  insert into tab2 (my_name) values (:old.my_name);
  6  end;
  7  /

Trigger created.


SQL>

To test the trigger, I ran a DELETE to remove the row from TAB1 and it appeared in TAB2:

SQL> delete tab1
  2  /

1 row deleted.

SQL> select * from tab1
  2  /

no rows selected

SQL> select * from tab2
  2  /

MY_NAME
----------
Andrew


SQL>

I did a ROLLBACK and the row returned from TAB2 to TAB1:

SQL> rollback
  2  /

Rollback complete.

SQL> select * from tab1
  2  /

MY_NAME
----------
Andrew

SQL> select * from tab2
  2  /

no rows selected


SQL>

I repeated the test with TRUNCATE:

SQL> truncate table tab1
  2  /

Table truncated.

SQL> select * from tab1
  2  /

no rows selected


SQL>

... but Oracle did not execute the trigger and the row did not appear in TAB2:

SQL> select * from tab2
  2  /

no rows selected

SQL>

... and after doing a ROLLBACK, the row did not return to TAB1 because TRUNCATE is DDL and includes a COMMIT:

SQL> rollback
  2  /

Rollback complete.

SQL> select * from tab1
  2  /

no rows selected

SQL> select * from tab2
  2  /

no rows selected

SQL>



_single_process Initialization Parameter

$
0
0
The _single_process initialization parameter allows you to start an instance in single process mode. As it begins with an underscore, you should only use it when Oracle tell you to. When I tried it on Oracle 10 and Oracle 11 test databases, they promptly fell over. However, I was able to get it to work on Oracle 9, although it produced an ORA-00600. I then left the SYSDBA session open: 

Oracle 9: sqlplus '/ as sysdba'
 
SQL*Plus: Release 9.2.0.7.0 - Production on Mon Nov 11 18:49:19 2013
 
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
 
Connected to an idle instance.
 
SQL> startup
ORACLE instance started.
 
Total System Global Area  106139816 bytes
Fixed Size                   733352 bytes
Variable Size              75497472 bytes
Database Buffers           29360128 bytes
Redo Buffers                 548864 bytes
ORA-00600: internal error code, arguments: [ksfdgfnm1], [0x40228B858], [], [],
[], [], [], []
 
SQL> col value format a30
SQL> select value from v$parameter
  2  where name = '_single_process'
  3  /
 
VALUE
------------------------------
TRUE
 
SQL> 

… and a subsequent login attempt from another server session failed with an ORA-00024: 

Oracle 9: sqlplus /
 
SQL*Plus: Release 9.2.0.7.0 - Production on Mon Nov 11 18:36:55 2013
 
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
 
ERROR:
ORA-00024: logins from more than one process not allowed in single-process mode
 
Enter user-name:

ORA-01511, ORA-01512 and ORA-27037

$
0
0
This was run on an Oracle 11 database. I used dbca to create a database recently and, by mistake, I put redo03a.log in the wrong directory (ent_red01 with a zero instead of ent_redo1 with an o). dbca had no way of knowing this was wrong and simply created the directory name I specified in the GUI. You can see what I mean in the output below:

In the database: 

  1  select group#, member from v$logfile
  2* order by 1,2
SQL> /
 
    GROUP# MEMBER
---------- ----------------------------------------
         1 /database/ENTARCH/ent_redo1/redo01a.log
         1 /database/ENTARCH/ent_redo2/redo01b.log
         2 /database/ENTARCH/ent_redo1/redo02a.log
         2 /database/ENTARCH/ent_redo2/redo02b.log
         3 /database/ENTARCH/ent_red01/redo03a.log
         3 /database/ENTARCH/ent_redo2/redo03b.log
 
6 rows selected.
 
SQL>
 
... and in UNIX:
 
ENTARCH /database/ENTARCH > ls -R ent_red*
ent_red01:
redo03a.log
 
ent_redo1:
redo01a.log  redo02a.log
 
ent_redo2:
redo01b.log  redo02b.log  redo03b.log
ENTARCH /database/ENTARCH >
 
I closed and mounted the database:
 
SQL> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup mount
ORACLE instance started.
 
Total System Global Area  522092544 bytes
Fixed Size                  2149672 bytes
Variable Size             335549144 bytes
Database Buffers          176160768 bytes
Redo Buffers                8232960 bytes
Database mounted.
SQL>
 
Then I tried to rename the logfile:
 
SQL> l
  1  alter database rename file
  2  '/database/ENTARCH/ent_red01/redo03a.log' to
  3* '/database/ENTARCH/ent_redo1/redo03a.log'
SQL> /
alter database rename file
*
ERROR at line 1:
ORA-01511: error in renaming log/data files
ORA-01512: error renaming log file
/database/ENTARCH/ent_red01/redo03a.log - new file
/database/ENTARCH/ent_redo1/redo03a.log not found
ORA-27037: unable to obtain file status
SVR4 Error: 2: No such file or directory
Additional information: 3
 
SQL>
 
This failed because you need to move or copy the logfile at the OS level first. As I had only just created this database I decided it was OK to use mv:
 
ENTARCH /database/ENTARCH/ent_red01 > ls
redo03a.log
ENTARCH /database/ENTARCH/ent_red01 > mv * ../ent_redo1
ENTARCH /database/ENTARCH/ent_red01 >
 
Then I was able to rename the file and open the database again:
 
SQL> l
  1  alter database rename file
  2  '/database/ENTARCH/ent_red01/redo03a.log' to
  3* '/database/ENTARCH/ent_redo1/redo03a.log'
SQL> /
 
Database altered.
 
SQL> alter database open
  2  /
 
Database altered.
 
SQL>
 
Finally, I checked that the redo log member looked OK in the database:
 
SQL> l
  1  select group#, member from v$logfile
  2* order by 1,2
SQL> /
 
    GROUP# MEMBER
---------- ----------------------------------------
         1 /database/ENTARCH/ent_redo1/redo01a.log
         1 /database/ENTARCH/ent_redo2/redo01b.log
         2 /database/ENTARCH/ent_redo1/redo02a.log
         2 /database/ENTARCH/ent_redo2/redo02b.log
         3 /database/ENTARCH/ent_redo1/redo03a.log
         3 /database/ENTARCH/ent_redo2/redo03b.log
 
6 rows selected.
 
SQL>
 
... and removed the empty directory:
 
ENTARCH /database/ENTARCH > rmdir ent_red01
ENTARCH /database/ENTARCH >

TRUNCATE TABLE SYS.AUD$ Gives ORA-00942

$
0
0
I saw that the SYSTEM tablespace in an Oracle 11.2 database was getting quite big so I checked the size of SYS.AUD$:
 
C:\Users\AJ0294094>sqlplus / as sysdba
 
SQL*Plus: Release 11.2.0.1.0 Production on Fri May 16 13:20:54 2014
 
Copyright (c) 1982, 2010, 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 count(*) from sys.aud$;
 
  COUNT(*)
----------
  35483888
 
SQL>
 
… but when I tried to TRUNCATE it, Oracle returned an ORA-00942:
 
SQL> truncate table sys.aud$
  2  /
truncate table sys.aud$
                   *
ERROR at line 1:
ORA-00942: table or view does not exist
 
SQL>
 
I took a closer look and saw that the AUD$ table had been moved to the SYSTEM schema and SYS.AUD$ was just a synonym pointing to it. This was a while later, by which time, several more rows had been added to it:
 
SQL> select owner, object_type
  2  from dba_objects where object_name = 'AUD$';
 
OWNER                          OBJECT_TYPE
------------------------------ -------------------
SYS                            SYNONYM
SYSTEM                         TABLE
 
SQL> select table_owner, table_name
  2  from dba_synonyms
  3  where owner = 'SYS'
  4  and synonym_name = 'AUD$'
  5  /
 
TABLE_OWNER          TABLE_NAME
-------------------- --------------------
SYSTEM               AUD$
 
SQL> select count(*) from system.aud$
  2  /
 
  COUNT(*)
----------
  35484319
 
SQL>
 
… so I did the TRUNCATE on SYSTEM.AUD$ instead:
 
SQL> truncate table system.aud$
  2  /
 
Table truncated.
 
SQL>
 
… then SYS.AUD$ appeared empty afterwards:
 
SQL> select count(*) from sys.aud$
  2  /
 
  COUNT(*)
----------
         0
 
SQL>
 
… and the SYSTEM tablespace had plenty of free space:
 
SQL> select sum(bytes) from dba_free_space
  2  where tablespace_name = 'SYSTEM'
  3  /
 
SUM(BYTES)
----------
8996651008
 
SQL>
 
This happened because you cannot TRUNCATE a table through a synonym. This applies, incidentally, even if you own both the table and the synonym, as you can see in the example below: 

SQL> create table tab1 (col1 number);
 
Table created.
 
SQL> truncate table tab1;
 
Table truncated.
 
SQL> create synonym tab2 for tab1;
 
Synonym created.
 
SQL> desc tab2
Name                       Null?    Type
-------------------------- -------- ------------------
COL1                                NUMBER
 
SQL> truncate table tab2;
truncate table tab2
               *
ERROR at line 1:
ORA-00942: table or view does not exist
 
SQL>

Global_Names and ORA-02085

$
0
0
This was tested on Oracle 9. I created 2 database links to the same target database:
 
SQL> conn / as sysdba
Connected.
SQL> create database link any_name.world
  2  connect to link_schema
  3  identified by link_schema
  4  using 'REMOTEDB.WORLD'
  5  /
 
Database link created.
 
SQL> create database link remotedb.world
  2  connect to link_schema
  3  identified by link_schema
  4  using 'REMOTEDB.WORLD'
  5  /
 
Database link created.
 
SQL>
 
I checked the value of the source database’s global_names parameter:
 
SQL> select value from v$parameter
  2  where name = 'global_names'
  3  /
 
VALUE
----------
FALSE
 
SQL>
 
I tested both links and they worked OK:
 
SQL> select sysdate from dual@any_name.world
  2  /
 
SYSDATE
---------
26-APR-12
 
SQL> select sysdate from dual@remotedb.world
  2  /
 
SYSDATE
---------
26-APR-12
 
SQL>
 
I changed global_names to true in the source database:
 
SQL> alter system set global_names = true
  2  /
 
System altered.
 
SQL> select value from v$parameter
  2  where name = 'global_names'
  3  /
 
VALUE
----------
TRUE
 
SQL>
 
This stopped the any_name.world link working as the link name in the source database and the global_name of the target database must match if global_names is set to true in the source database:
 
SQL> select sysdate from dual@any_name.world
  2  /
select sysdate from dual@any_name.world
                         *
ERROR at line 1:
ORA-02085: database link ANY_NAME.WORLD connects to
REMOTEDB.WORLD
 
SQL>
 
But the remotedb.world link still worked:
 
SQL> select sysdate from dual@remotedb.world
  2  /
 
SYSDATE
---------
26-APR-12
 
SQL>
 
How do I know that the check is made against the target database’s global name? I connected to the target database and changed its global name to any_name.world:
 
SQL> conn /@remotedb.world
Connected.
SQL> alter database rename global_name
  2  to any_name.world
  3  /
 
Database altered.
 
I reconnected to the source database: 

SQL> conn / as sysdba
Connected.
SQL>
 
Then the any_name.world link worked:
 
SQL> select sysdate from dual@any_name.world
  2  /
 
SYSDATE
---------
26-APR-12
 
SQL>
 
But the remotedb.world link didn’t:
 
SQL> select sysdate from dual@remotedb.world
  2  /
select sysdate from dual@remotedb.world
                         *
ERROR at line 1:
ORA-02085: database link REMOTEDB.WORLD connects to
ANY_NAME.WORLD
 
SQL>

PRAGMA AUTONOMOUS_TRANSACTION

$
0
0
I tested this on Oracle 11.2. If you update a table in SQL*Plus then update it again with a different value in a PL/SQL block, the second update replaces the first:
 
SQL> create table tab1 as
  2  select 1 col1 from dual
  3  /
 
Table created.
 
SQL> update tab1 set col1 = 2
  2  /
 
1 row updated.
 
SQL> begin
  2   update tab1 set col1 = 3;
  3   commit;
  4  end;
  5  /
 
PL/SQL procedure successfully completed.
 
SQL> select col1 from tab1
  2  /
 
      COL1
----------
         3
 
SQL>
 
… but, if you DECLARE the PL/SQL block as a PRAGMA AUTONOMOUS_TRANSACTION, Oracle runs it independently from the underlying session. The PL/SQL block then cannot complete until the underlying SQL has finished. However, the underlying SQL cannot finish until the PL/SQL block has finished. This sets up a deadlock situation, which Oracle resolves after a few seconds. The PL/SQL block then fails with an ORA-00060 and only the first update remains:
 
SQL> create table tab1 as
  2  select 1 col1 from dual
  3  /
 
Table created.
 
SQL> update tab1 set col1 = 2
  2  /
 
1 row updated.
 
SQL> declare
  2   pragma autonomous_transaction;
  3  begin
  4   update tab1 set col1 = 3;
  5   commit;
  6  end;
  7  /
declare
*
ERROR at line 1:
ORA-00060: deadlock detected while waiting for
resource
ORA-06512: at line 4
 
SQL> select col1 from tab1
  2  /
 
      COL1
----------
         2
 
SQL>
 

ORA-00065

$
0
0
For those of you still  on Oracle 9, here is a summary of bug 3368245. It caught me out on a few occasions until I looked it up on Metalink. If you are using a server parameter file, setting fixed_date to none causes an ORA-00065 the next time you open the database:

TEST9 > sqlplus '/ as sysdba'

SQL*Plus: Release 9.2.0.7.0 - Production on Fri Feb 11 08:40:19 2011

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.

Connected to:
Oracle9i Enterprise Edition Release 9.2.0.7.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.7.0 - Production

SQL> alter system set fixed_date = none scope = both;

System altered.

SQL> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORA-00065: initialization of FIXED_DATE failed
SQL>

The workaround is to use a pfile or use SCOPE=MEMORY to set FIXED_DATE.

The bug was fixed in Oracle 10:

TEST10 > sqlplus / as sysdba

SQL*Plus: Release 10.2.0.3.0 - Production on Fri Feb 11 08:57:35 2011

Copyright (c) 1982, 2006, Oracle.  All Rights Reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options

SQL> alter system set fixed_date = none scope = both;

System altered.

SQL> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.

Total System Global Area  155189248 bytes
Fixed Size                  2028560 bytes
Variable Size              79694832 bytes
Database Buffers           71303168 bytes
Redo Buffers                2162688 bytes
Database mounted.
Database opened.
SQL>


Soundex

$
0
0
I found a booklet called Introduction to SQL Version 1.0, printed in 1985 and 1989. It was written by a certain Lawrence Ellison and described a function called SOUNDEX, which I had never heard of before so I tried it out on an Oracle 9 database.

SOUNDEX represents the sound of a word by a 4 character alphanumeric code. Words which sound alike should have the same code. This allows you to select rows containing data with a particular sound. Sometimes it works:
 
SQL> select soundex('STEWART') from dual;
 
SOUN
----
S363
 
SQL> select soundex('STUART') from dual;
 
SOUN
----
S363
 
SQL>
 
SQL> select soundex('BORED') from dual;
 
SOUN
----
B630
 
SQL> select soundex('BOARD') from dual;
 
SOUN
----
B630
 
SQL>
 
But sometimes it gives words, which sound the same, different codes:
 
SQL> select soundex('WITCH') from dual;
 
SOUN
----
W320
 
SQL> select soundex('WHICH') from dual;
 
SOUN
----
W200
 
SQL>
 
And sometimes it gives words, which sound different, the same code:
 
SQL> select soundex('FLOOR') from dual;
 
SOUN
----
F460
 
SQL> select soundex('FLOUR') from dual;
 
SOUN
----
F460
 
SQL>

sqlplus -prelim

$
0
0
If all the available processes in a database are used up, you will get an ORA-00020 when you try to login. The best way round this is to get some people to log out. However, this may not be possible e.g. if people have logged in from some application then killed it, leaving their sessions alive in the database but with no way to get back to them and end them tidily.
 
Oracle 11: sqlplus / as sysdba
 
SQL*Plus: Release 11.2.0.1.0 Production on Wed Nov 13 15:56:40 2013
 
Copyright (c) 1982, 2009, Oracle.  All rights reserved.
 
ERROR:
ORA-00020: maximum number of processes (100) exceeded
 
Enter user-name: ^C
Oracle 11:
 
An alternative is to login with the –prelim option. This does not allow you to run any SQL but it does let you close the database:
 
Oracle 11: sqlplus -prelim / as sysdba
 
SQL*Plus: Release 11.2.0.1.0 Production on Wed Nov 13 15:56:59 2013
 
Copyright (c) 1982, 2009, Oracle.  All rights reserved.
 
SQL> select sysdate from dual;
select sysdate from dual
*
ERROR at line 1:
ORA-01012: not logged on
Process ID: 0
Session ID: 0 Serial number: 0
 
SQL> shutdown abort
ORACLE instance shut down.
SQL>

Before opening the database again, you might consider increasing the processes parameter to reduce the chance of this happening again.



How I Copied Triggers from one Database to Another

$
0
0
(This happened on Oracle 11.2.) In an earlier example, I started to look at doing a full expdp and impdp. I'm not sure why, but the Data Pump import produced a number of errors. There were well over 200 like this: 

ORA-39112: Dependent object type TRIGGER:"SRCE"."ADDRESS_BIU" skipped, base object type TABLE:"SRCE"."ADDRESS" creation failed 

This message suggested that the SRCE.ADDRESS table had not been created in the output database but, in fact, it had: 

SQL> desc srce.address
Name                       Null?    Type
-------------------------- -------- ------------------
UPDATE_COUNT               NOT NULL NUMBER
ADDRESS_ID                 NOT NULL NUMBER
SUB_PREMISES                        VARCHAR2(30)
PREMISES_NAME                       VARCHAR2(30)
PO_BOX                              VARCHAR2(30)
PREMISES_NUMBER                     VARCHAR2(10)
DEP_THOROUGHFARE                    VARCHAR2(30)
THOROUGHFARE                        VARCHAR2(30)
DOUB_DEP_LOCALITY                   VARCHAR2(30)
DEP_LOCALITY                        VARCHAR2(30)
POST_TOWN                           VARCHAR2(30)
COUNTY                              VARCHAR2(30)
POSTCODE                            VARCHAR2(8)
USES                                NUMBER
LAST_UPD_BY                         VARCHAR2(8)
LAST_UPD_TIME                       DATE
COUNTRY                             VARCHAR2(30)
BUILDING_GROUP_NAME                 VARCHAR2(30)
TCN                                 NUMBER 

However, none of the triggers had been created so my output database was over 200 triggers short, which, as you might expect, stopped it working properly.
 
I tried to extract DDL from the input database to create the triggers as follows but this failed too:

SQL> EXECUTE DBMS_METADATA.SET_TRANSFORM_PARAM -
> (DBMS_METADATA.SESSION_TRANSFORM,'PRETTY',true);
 
PL/SQL procedure successfully completed.
 
SQL> EXECUTE DBMS_METADATA.SET_TRANSFORM_PARAM -
> (DBMS_METADATA.SESSION_TRANSFORM,'SQLTERMINATOR',true);
 
PL/SQL procedure successfully completed.
 
SQL> select dbms_metadata.get_ddl
  2  ('TRIGGER','ADDRESS_BIU','SRCE') from dual
  3  /
ERROR:
ORA-03113: end-of-file on communication channel
Process ID: 13899
Session ID: 95 Serial number: 10069
 
no rows selected
 
SQL> conn / as sysdba
Connected.
SQL> select open_mode from v$database
  2  /
 
OPEN_MODE
--------------------
READ WRITE
 
1 row selected.
 
SQL>
 
I wrote the following script and ran it against the input database:
 
col trigger_body newline
col slash newline
set feedback off
set pages 0
set lines 200
set long 100000
set longchunksize 200
set trimspool on
spool create_triggers.sql
select 'create or replace trigger '||
description,
trigger_body,
'/' slash
from dba_triggers
where owner = 'SRCE'
/
spool off
 
This is part of the SPOOL file:
 
create or replace trigger srce.ptr_product_pk_bi
  BEFORE INSERT ON ptr_product
  FOR EACH ROW
DECLARE
  -- local variables here
BEGIN
  IF :new.product_id IS NULL THEN
    SELECT ptr_product_id_seq.nextval
    INTO   :new.product_id
    FROM   DUAL;
  END IF;
END ptr_product;
/
 
… and when I ran it against the output database, it worked like this:
 
SQL> l
  1  create or replace trigger srce.ptr_product_pk_bi
  2    BEFORE INSERT ON ptr_product
  3    FOR EACH ROW
  4  DECLARE
  5    -- local variables here
  6  BEGIN
  7    IF :new.product_id IS NULL THEN
  8      SELECT ptr_product_id_seq.nextval
  9      INTO   :new.product_id
10      FROM   DUAL;
11    END IF;
12* END ptr_product;
SQL> /
 
Trigger created.
 
SQL>

The SQL in the SPOOL file created the other 200+ missing triggers in the same way. When I get a moment, I will try to understand what caused the problems I had with impdp and dbms_metadata.get_ddl.

The book advertised below is apparently free if you have a connected Android device:


ORA-00361

$
0
0
This was tested on an Oracle 9 database. 

One way to relocate redo log groups is to drop members in one location and recreate them in another. However, if your redo log group only has one member and you want to relocate it, you need to create the new member first. If you don’t do this, you get an ORA-00361 as a redo log group must always have at least one member:
 
SQL> select member from v$logfile
  2  where group# = 4
  3  /
 
MEMBER
----------------------------------------
/cisdpt/misdpt1/mis_redo1/redo4_1.dbf
 
SQL> alter database drop logfile member
  2  '/cisdpt/misdpt1/mis_redo1/redo4_1.dbf'
  3  /
alter database drop logfile member
*
ERROR at line 1:
ORA-00361: cannot remove last log member
/cisdpt/misdpt1/mis_redo1/redo4_1.dbf for group 4
 
SQL>

GROUP BY and ORDER BY

$
0
0
In 1997, I was sent on a 1 day Introduction to Oracle course. The lecturer said that if you used a GROUP BY, you could not assume that the results would be returned in order. If you wanted to make sure, you should include an ORDER BY as well. From a theoretical point of view, he was correct as an RDBMS is not obliged to return results in any particular order unless you tell it to do so. But from a practical point of view, a simple way to group data is to sort it first. Up to and including version 9, Oracle seems to have used this method:
 
TEST9 > sqlplus /
 
SQL*Plus: Release 9.2.0.7.0 - Production on Fri Sep 2 18:04:16 2011
 
Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.
 
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.7.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.7.0 - Production
group by, Oracle 10, Oracle 9, order by,
 
SQL> @group_by
SQL> set pages 100
SQL> col first_letter format a12
SQL> select substr(segment_name,1,1) first_letter,
  2  count(*)
  3  from dba_segments
  4  group by substr(segment_name,1,1)
  5  /
 
FIRST_LETTER   COUNT(*)
------------ ----------
1                     1
A                   118
B                    92
C                    98
D                    43
E                    17
F                    51
G                    22
H                    39
I                   293
J                     5
K                     7
L                   115
M                   116
N                     2
O                    29
P                    52
Q                    49
R                   170
S                   319
T                   121
U                    54
V                    16
W                    11
X                    14
_                   119
 
26 rows selected.
 
SQL>
 
This is no longer the case and, starting with Oracle 10, if you want to see the output of a GROUP BY in order, you have to include an ORDER BY as well:
 
TEST10 > sqlplus /
 
SQL*Plus: Release 10.2.0.3.0 - Production on Fri Sep 2 18:15:01 2011
 
Copyright (c) 1982, 2006, Oracle.  All Rights Reserved.
 
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options
 
SQL> @group_by
SQL> set pages 100
SQL> col first_letter format a12
SQL> select substr(segment_name,1,1) first_letter,
  2  count(*)
  3  from dba_segments
  4  group by substr(segment_name,1,1)
  5  /
 
FIRST_LETTER   COUNT(*)
------------ ----------
P                   161
O                    37
V                    83
D                   157
_                    68
M                   448
N                    98
W                   779
Q                    40
Y                     3
C                   135
I                   550
B                   120
F                    55
U                    20
S                   717
T                   250
A                   316
J                    21
E                   143
L                   178
R                   303
H                   662
K                     9
b                     1
G                     7
X                    23
 
27 rows selected.
 
SQL> select substr(segment_name,1,1) first_letter,
  2  count(*)
  3  from dba_segments
  4  group by substr(segment_name,1,1)
  5  order by 1
  6  /
 
FIRST_LETTER   COUNT(*)
------------ ----------
A                   316
B                   120
C                   135
D                   157
E                   143
F                    55
G                     7
H                   662
I                   550
J                    21
K                     9
L                   178
M                   448
N                    98
O                    37
P                   161
Q                    40
R                   303
S                   717
T                   250
U                    20
V                    83
W                   779
X                    23
Y                     3
_                    68
b                     1
 
27 rows selected.
 
SQL>


FAST Refresh of Materialized View Returns ORA-12004

$
0
0
I tried to do a FAST refresh of a materialized view in Oracle 11.2 but it failed with an ORA-12004:
 
SQL> begin
  2  dbms_mview.refresh('ebase.m_gridpoint2',method=>'F');
  3  end;
  4  /
begin
*
ERROR at line 1:
ORA-12004: REFRESH FAST cannot be used for materialized view
"EBASE"."M_GRIDPOINT2"
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2558
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2771
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2740
ORA-06512: at line 2
 
SQL>
 
I checked the documentation provided by the software supplier. It said I could not do this as the materialized view in question contained a NOT EXISTS clause. I looked at the source of the materialized view and saw that it did:
 
SQL> l
  1  select query
  2  from dba_mviews
  3  where owner = 'EBASE'
  4* and mview_name = 'M_GRIDPOINT2'
SQL> /
 
QUERY
-------------------------------------------------------
SELECT GPT.NR GRIDPOINTNR,
       DECODE (GPT.PRODUCTTYPE,  0, 'E',  1, 'G',  'U')
PRODUCTTYPE,
       GPT.EANCODE EANCODE,
       NAR.EANCODE NETAREAEAN,
       MROL.EANCODE GRIDOPERATOREAN,
       GPT.ROWID GPTROWID,
       NAR.ROWID NARROWID,
       MROL.ROWID MROLROWID
  FROM D_GRIDPOINT GPT,
       D_NETAREA NAR,
       D_MARKETROLE MROL
WHERE GPT.GRIDOPERNR = MROL.NR(+)
AND   GPT.NETAREANR = NAR.NR(+)
AND NOT EXISTS (SELECT 1
                    FROM   D_GRIDPOINTCONNECTION GPC
                    WHERE  GPT.NR = GPC.GRIDPOINTNR)
 
SQL>
 
… so I tried a COMPLETE refresh and it worked:
 
SQL> begin
  2  dbms_mview.refresh('ebase.m_gridpoint2',method=>'C');
  3  end;
  4  /
 
PL/SQL procedure successfully completed.
 
SQL>

ORA-00031, ORA-03113 and ORA-03114

$
0
0
This was tested on Oracle 11.2. I had a session in the middle of a long-running operation: 

SQL> conn user1/user1
Connected.
SQL> exec dbms_lock.sleep(3600);
 
I tried to kill it like this but Oracle gave me an ORA-00031 instead:
 
SQL> select sid, serial# from v$session
  2  where username = 'USER1'
  3  /
 
       SID    SERIAL#
---------- ----------
         7         62
 
SQL> alter system kill session '7,62'
  2  /
alter system kill session '7,62'
*
ERROR at line 1:
ORA-00031: session marked for kill
 
SQL>
 
… and the session carried on regardless:
 
SQL> conn user1/user1
Connected.
SQL> exec dbms_lock.sleep(3600);
 
I really wanted to kill it straight away so I did it like this:
 
SQL> alter system disconnect session '7,62'
  2  immediate
  3  /
 
System altered.
 
SQL>
 
… and the session died there and then:
 
SQL> exec dbms_lock.sleep(3600);
BEGIN dbms_lock.sleep(3600); END;
 
*
ERROR at line 1:
ORA-03113: end-of-file on communication channel
Process ID: 15294
Session ID: 7 Serial number: 62
 
SQL>
 
When I tried to carry on using it, Oracle told me I had been disconnected:
 
SQL> select sysdate from dual
  2  /
ERROR:
ORA-03114: not connected to ORACLE
 
SQL>


The USER Keyword

$
0
0
Here are a couple of examples with the USER keyword, which I tested on Oracle 11.2. You can use it after the SHOW command or in a SELECT statement as follows. It returns the name of the user running the current session: 

SQL> conn fred/bloggs
Connected.
SQL> show user
USER is "FRED"
SQL> select user from dual
  2  /
 
USER
------------------------------
FRED

SQL>

You can also include it in a WHERE clause to return rows which match the current username. To illustrate this, I created an employee table:

SQL> create table emp_table
  2  (emp_name varchar2(10),
  3   dept     varchar2(10),
  4   salary   number)
  5  /
 
Table created.

SQL>

... then I added 2 rows for the IT department and 2 for Sales:

SQL> insert into emp_table
  2  values('ANDREW','IT',10000)
  3  /
 
1 row created.
 
SQL> insert into emp_table
  2  values('BRIAN','SALES',20000)
  3  /
 
1 row created.
 
SQL> insert into emp_table
  2  values('COLIN','IT',30000)
  3  /
 
1 row created.
 
SQL> insert into emp_table
  2  values('DAVID','SALES',40000)
  3  /
 
1 row created.

SQL>

I created a view to return rows from the employee table in the same department as the user running the current session:

SQL> create view emp_view
  2  as select * from emp_table
  3  where dept =
  4  (select dept from emp_table
  5   where emp_name = user)
  6  /
 
View created.
 
SQL> grant select on emp_view to andrew, brian
  2  /
 
Grant succeeded.

SQL>

... so when Andrew used it, he only saw rows from the IT department where he worked:

SQL> conn andrew/andrew
Connected.
SQL> select * from fred.emp_view
  2  /
 
EMP_NAME   DEPT           SALARY
---------- ---------- ----------
ANDREW     IT              10000
COLIN      IT              30000

SQL>

... and when Brian used it, he only saw data for Sales:

SQL> conn brian/brian
Connected.
SQL> select * from fred.emp_view
  2  /
 
EMP_NAME   DEPT           SALARY
---------- ---------- ----------
BRIAN      SALES           20000
DAVID      SALES           40000
 
SQL>

Wrap Utility

$
0
0
This is an example using the wrap utility, which allows you to hide stored code. Software suppliers can use it to prevent customers stealing their PL/SQL. First I created a table:
 
SQL> create table name_list
  2  as select 'ANDREW' name from dual
  3  /
 
Table created.
 
SQL> select * from name_list
  2  /
 
NAME
------
ANDREW
 
SQL>
 
Then I created a procedure to add names to the table:
 
SQL> create or replace procedure add_name
  2   (new_name char) as
  3  begin
  4   insert into name_list values(new_name);
  5  end;
  6  /
 
Procedure created.
 
SQL>
 
... and I tested it as follows:
 
SQL> execute add_name('BRIAN');
 
PL/SQL procedure successfully completed.
 
SQL> select * from name_list
  2  /
 
NAME
------
ANDREW
BRIAN
 
SQL>
 
The procedure was easy to see in the database so anybody could have stolen it:
 
SQL> l
  1  select text from dba_source
  2  where name = 'ADD_NAME'
  3* order by line
SQL> /
 
TEXT
-------------------------------------------------------
procedure add_name
(new_name char) as
begin
insert into name_list values(new_name);
end;
 
SQL>
 
The wrap utility was stored here:
 
ORACLE11 > which wrap
/oracle/app/oracle/product/11.2.0/bin/wrap
ORACLE11 >
 
I ran it on the source code. To do this you supply the name of the file containing the code to be wrapped as the iname parameter and the name of the file to store the wrapped code as the oname parameter:
 
ORACLE11 > cat procedure.sql
create or replace procedure add_name
(new_name char) as
begin
insert into name_list values(new_name);
end;
/
ORACLE11 > wrap iname=procedure.sql \
> oname=wrapped_procedure.sql
 
PL/SQL Wrapper: Release 11.2.0.1.0- 64bit Production on Thu Nov 03 13:07:53 2011
 
Copyright (c) 1993, 2009, Oracle.  All rights reserved.
 
Processing procedure.sql to wrapped_procedure.sql
ORACLE11 >
 
This encrypted the source code as follows:
 
ORACLE11 > cat wrapped_procedure.sql
create or replace procedure add_name wrapped
a000000
1
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
7
5b 92
d6K98qP2M1tFArx52RBb3rXsyPgwg5nnm7+fMr2ywFxaWaH0cgxHLkMJUKXHstD+x9IyXLgz
uHQlw7h0i8DAMv7Shgml0pmfsp77Caj5nspECCJBr6ieOEoia+KvuEzsPHHiP9E8dKYY7AS4
 
/
ORACLE11 >
 
I compiled the wrapped source code and checked that it was still encrypted in the database:
 
SQL> @wrapped_procedure
 
Procedure created.
 
SQL> select text from dba_source
  2  where name = 'ADD_NAME'
  3  order by line
  4  /
 
TEXT
-------------------------------------------------------
procedure add_name wrapped
a000000
1
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
7
5b 92
d6K98qP2M1tFArx52RBb3rXsyPgwg5nnm7+fMr2ywFxaWaH0cgxHLkM
JUKXHstD+x9IyXLgz
uHQlw7h0i8DAMv7Shgml0pmfsp77Caj5nspECCJBr6ieOEoia+KvuEz
sPHHiP9E8dKYY7AS4
 
SQL>
 
Finally, I checked that the procedure still worked:
 
SQL> execute add_name('COLIN');
 
PL/SQL procedure successfully completed.
 
SQL> select * from name_list
  2  /
 
NAME
------
ANDREW
BRIAN
COLIN
 
SQL>
 
Looking elsewhere on the Internet there seem to be a few points to bear in mind when using wrap. I have not checked any of these myself:
  1. It will increase the size of your source code.
  2. If you have strict security on $ORACLE_HOME/bin, you may wish to install a copy of wrap in a shared area for your developers.
  3. You should use the correct version of wrap for your database. Otherwise, if Oracle has introduced some new feature, an out of date version of wrap will not recognise it.
  4. You should only wrap package bodies. You should leave the package headers alone as they can provide useful documentation (I'm not sure if I agree with this one).

SHUTDOWN TRANSACTIONAL

$
0
0
If you run this command, Oracle waits until all users have committed or rolled back any outstanding transactions before closing the database. To test this out in Oracle 12.1, I tried to close a database from a session with an outstanding transaction. This failed with an ORA-01097. Once I had committed the transaction, I was able to close the database successfully:

SQL> show user
USER is "SYS"
SQL> create table tab1(col1 number)
  2  /
 
Table created.
 
SQL> insert into tab1 values(1)
  2  /
 
1 row created.
 
SQL> shutdown transactional
ORA-01097: cannot shutdown while in a transaction - commit or rollback first
SQL> commit
  2  /
 
Commit complete.
 
SQL> shutdown transactional
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
 
Total System Global Area 1720328192 bytes
Fixed Size                  2403496 bytes
Variable Size            1056965464 bytes
Database Buffers          654311424 bytes
Redo Buffers                6647808 bytes
Database mounted.
Database opened.
SQL>
 
For the next example, I logged in as user ANDREW, created a table, inserted a row but did not commit it:
 
SQL> show user
USER is "ANDREW"
SQL> create table tab1(col1 number)
  2  /
 
Table created.
 
SQL> insert into tab1 values(1)
  2  /
 
1 row created.
 
SQL>
 
When user SYS tried to close the database, nothing happened:
 
SQL> show user
USER is "SYS"
SQL> shutdown transactional
 
After a while, user ANDREW rolled back his transaction:
 
SQL> show user
USER is "ANDREW"
SQL> create table tab1(col1 number)
  2  /
 
Table created.
 
SQL> insert into tab1 values(1)
  2  /
 
1 row created.
 
SQL> rollback
  2  /
 
Rollback complete.
 
SQL>
 
… then the database closed down a few seconds later:
 
SQL> show user
USER is "SYS"
SQL> shutdown transactional
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL>


Taking a Datafile Offline in NOARCHIVELOG Mode

$
0
0
I was reading a colleague’s copy of Oracle RMAN 11g Backup and Recovery by Robert Freeman. It said that you could not take a datafile offline if the database was in NOARCHIVELOG mode. I don’t know much about taking datafiles offline but this seemed reasonable so I gave it a try in Oracle 12.1. First I checked the database’s LOG_MODE: 

SQL> select log_mode from v$database
  2  /
 
LOG_MODE
------------
NOARCHIVELOG
 
SQL>

Then I tried to take a datafile offline but this failed, as the book had said it would: 

SQL> alter database datafile
  2  'C:\APP\ADMINISTRATOR\ORADATA\ORCL1\DATAFILE\USERS.DBF'
  3  offline
  4  /
alter database datafile
*
ERROR at line 1:
ORA-01145: offline immediate disallowed unless media
recovery enabled
 
SQL>
 
I read somewhere in the Oracle documentation that you could do this if the tablespace was READ ONLY. This also seemed reasonable so I tried it too: 

SQL> alter tablespace users read only
  2  /
 
Tablespace altered.
 
SQL> alter database datafile
  2  'C:\APP\ADMINISTRATOR\ORADATA\ORCL1\DATAFILE\USERS.DBF'
  3  offline
  4  /
 
Database altered.
 
SQL> 

Of course, once I had done this, I could not change the tablespace to READ WRITE mode again: 

SQL> l
  1* alter tablespace users read write
SQL> /
alter tablespace users read write
*
ERROR at line 1:
ORA-01135: file 6 accessed for DML/query is offline
ORA-01110: data file 6:
'C:\APP\ADMINISTRATOR\ORADATA\ORCL1\DATAFILE\USERS.DBF'
 
SQL> 

… until I had brought the datafile ONLINE: 

SQL> alter database datafile
  2  'C:\APP\ADMINISTRATOR\ORADATA\ORCL1\DATAFILE\USERS.DBF'
  3  online
  4  /
 
Database altered.
 
SQL> alter tablespace users read write
  2  /
 
Tablespace altered.
 
SQL>

V$RECOVERY_FILE_DEST

$
0
0
The V$RECOVERY_FILE_DEST view shows details about the flash recovery area. In the example below, it is queried to see the maximum size. This is then increased from 4 gigabytes to 5 gigabytes and the view is queried again to see the change: 

ORCL /export/home/oracle > sqlplus / as sysdba
 
SQL*Plus: Release 11.2.0.2.0 Production on Tue Jun 18 16:08:48 2013
 
Copyright (c) 1982, 2010, Oracle.  All rights reserved.
 
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
 
SQL> desc v$recovery_file_dest
Name                       Null?    Type
-------------------------- -------- ------------------
NAME                                VARCHAR2(513)
SPACE_LIMIT                         NUMBER
SPACE_USED                          NUMBER
SPACE_RECLAIMABLE                   NUMBER
NUMBER_OF_FILES                     NUMBER
 
SQL> select space_limit
  2  from v$recovery_file_dest
  3  /
 
SPACE_LIMIT
-----------
4294967296
 
SQL> alter system set db_recovery_file_dest_size = 5g
  2  /
 
System altered.
 
SQL> select space_limit
  2  from v$recovery_file_dest
  3  /
 
SPACE_LIMIT
-----------
5368709120
 
SQL>

If you click on the two images below to enlarge them and bring them into focus, you will see that in version 11.1, Oracle called it the flash recovery area:



... whereas in Oracle 11.2, it was called the fast recovery area:


Viewing all 330 articles
Browse latest View live