Archives For November 30, 1999

back from holidays and the first ouch on the first day. we tried to setup a clustered grid infrastructure on a solaris 10 t4-2 machine. business as usual, one might think. but when running the rootcrs.pl setup script asmca ( which in invoked from rootcrs.pl automatically ) reports:

...
main] [ 2012-11-02 12:58:59.282 MET ] [OracleHome.getVersion:1023] Current version from sqlplus: 11.2.0.3.0
[main] [ 2012-11-02 12:58:59.282 MET ] [UsmcaLogger.logInfo:143] Role SYSASM
[main] [ 2012-11-02 12:58:59.282 MET ] [UsmcaLogger.logInfo:143] OS Auth true
[main] [ 2012-11-02 12:59:21.347 MET ] [SQLEngine.done:2189] Done called
[main] [ 2012-11-02 12:59:21.349 MET ] [USMInstance.configureLocalASM:3033] ORA-04031: unable to allocate 32 bytes of shared memory ("shared pool","SELECT DECODE(null,'','Total...","SQLA","tmp")
...

hm. there is a support note about this behaviour ( 1416083.1 ) but there is no way to give asm parameters to rootcrs.pl or the crsconfig_params files. what we needed to do:
disable some CPUs ( so there are less than 64 available ), re-configure the cluster, adjust the asm memory parameters and then enable the cpus again. this seems to be the only workaround at the moment ( 11.2.0.4 and 12.1 should contain a fix for this ).

while learning more about postgresql I came across the default case in postgresql ( it is lower case ). so, when querying the dictionary/catalog you’ll have to provide the lower case names to get any results:

postgres=# create table test1 ( a numeric );
CREATE TABLE
postgres=# select relname from pg_class where relname = 'TEST1';
 relname 
---------
(0 rows)
postgres=# select relname from pg_class where relname = 'test1';
 relname 
---------
 test1
(1 row)

in oracle you’ll need to use upper case by default:

SQL> create table test1 ( a number );
Table created.
SQL> select table_name from dba_tables where table_name = 'TEST1';
TABLE_NAME
------------------------------
TEST1
SQL> select table_name from dba_tables where table_name = 'test1';
no rows selected
SQL> 

if you want postgresql to respect the case when creating objects you’ll need to put double quotes around the names:

postgres=# create table "TEST2" ( a numeric );
CREATE TABLE
postgres=# select relname from pg_class where relname = 'TEST2';
 relname 
---------
 TEST2
(1 row)
postgres=# select relname from pg_class where relname = 'test22';
 relname 
---------
(0 rows)

same in oracle:

SQL> create table "test2" ( a number );
Table created.
SQL> select table_name from dba_tables where table_name = 'test2';
TABLE_NAME
------------------------------
test2
SQL> select table_name from dba_tables where table_name = 'TEST2';
no rows selected

knowing this, is it possible to create identical tables which just differ in the case of their name ? :

postgresql:

postgres=# create table test3 ( a numeric );
CREATE TABLE
postgres=# create table "Test3" ( a numeric );
CREATE TABLE
postgres=# create table "TesT3" ( a numeric );
CREATE TABLE
postgres=# select relname from pg_class where upper(relname) like 'TEST3%';
 relname 
---------
 Test3
 TesT3
 test3
(3 rows)

not an issue with postgresql. what about oracle ?

SQL> create table test3 ( a number );
Table created.
SQL> create table "Test3" ( a number );
Table created.
SQL> create table "TesT3" ( a number );
Table created.
SQL> select table_name from dba_tables where upper(table_name) like 'TEST3%';
TABLE_NAME
------------------------------
TesT3
Test3
TEST3

same behaviour. If someone had asked me if this is possible in oracle before, I would have said: no, definitely not. lessons learned ? :)

going further: what about constraint names ?
in postgresql:

postgres=# alter table test3 add constraint c1 check ( a is not null );
ALTER TABLE
postgres=# alter table test3 add constraint "C1" check ( a > 5 );
ALTER TABLE
postgres=# select conname,consrc from pg_constraint where upper(conname) = 'C1';
 conname |       consrc       
---------+--------------------
 c1      | (a IS NOT NULL)
 C1      | (a > (5)::numeric)

ok, this is consistent. what about oracle ? :

SQL> alter table test3 add constraint c1 check ( a is not null );
Table altered.
SQL> alter table test3 add constraint "C1" check ( a > 5 );
alter table test3 add constraint "C1" check ( a > 5 )
                                *
ERROR at line 1:
ORA-02264: name already used by an existing constraint

what about indexes ? postgresql:

postgres=# create index i1 on test3(a);
CREATE INDEX
postgres=# create index "i1" on test3(a);
ERROR:  relation "i1" already exists
postgres=# create index "I1" on test3(a);
CREATE INDEX
postgres=# select indexname,indexdef from pg_indexes where upper(indexname) = 'I1';
 indexname |                  indexdef                  
-----------+--------------------------------------------
 i1        | CREATE INDEX i1 ON test3 USING btree (a)
 I1        | CREATE INDEX "I1" ON test3 USING btree (a)
(2 rows)

oracle:

SQL> create index i1 on test3 ( a );
Index created.
SQL> create index "i1" on test3 ( a );
create index "i1" on test3 ( a )
                            *
ERROR at line 1:
ORA-01408: such column list already indexed

as oracle checks if an index is defined on the same column(s) this is not possible. slightly modified test:

SQL> alter table test3 add ( b number );
Table altered.
SQL> create index "I1" on test3 ( b );
create index "I1" on test3 ( b )
            *
ERROR at line 1:
ORA-00955: name is already used by an existing object

still not possible.

I did not check all the objects but it seems that oracle is not as consistent as postgresql in this case.

if you see errors like this in the alert-log of your standby database:

MRP0: Background Media Recovery terminated with error 1274
Errors in file /oradata/dbs100/admin/diag/rdbms/dbs100dg2/dbs100dg2/trace/dbs100dg2_mrp0_4371.trc:
ORA-01274: cannot add datafile '+DBS100_DATA_DG/dbs100dg1/datafile/tbs1.276.793899763' - file could not be created
Managed Standby Recovery not using Real Time Apply
Recovery interrupted!
Recovery stopped due to failure in applying recovery marker (opcode 17.30).
Datafiles are recovered to a consistent state at change 10967593 but controlfile could be ahead of datafiles.
MRP0: Background Media Recovery process shutdown (dbs100dg2)

or this:

Errors in file /oradata/dbs100/admin/diag/rdbms/dbs100dg2/dbs100dg2/trace/dbs100dg2_mrp0_4981.trc:
ORA-01111: name for data file 6 is unknown - rename to correct file
ORA-01110: data file 6: '/opt/oracle/product/base/11.2.0.3.1/dbs/UNNAMED00006'
ORA-01157: cannot identify/lock data file 6 - see DBWR trace file
ORA-01111: name for data file 6 is unknown - rename to correct file
ORA-01110: data file 6: '/opt/oracle/product/base/11.2.0.3.1/dbs/UNNAMED00006'

… don’t panic. it is easy to fix.

first of all this happens if the STANDBY_FILE_MANAGEMENT parameter is set to “MANUAL” on the standby database. but how do you fix it ? the first option would be to re-create the standby database, but this would be a lot of work and depending on the size of the database could require a huge amount of time.

the easy way is to do it like this ( on the standby database, of course ):

SQL> ALTER DATABASE CREATE DATAFILE '/opt/oracle/product/base/11.2.0.3.1/dbs/UNNAMED00006' AS '+DBS100_DATA_DG';
Database altered.
SQL> alter database recover managed standby database disconnect from session;
Database altered.
SQL> alter system set standby_file_management='AUTO' scope=both;
System altered.

don’t forget to set the STANDBY_FILE_MANAGEMENT to “AUTO” after you created the file so that you don’t have to worry about such things in the future…

this is work in progress, but shall show the similarities and differences between postgresql and oracle in regards to implementing schemas and code. I will try to add more and more things in the future and update this post and the samples accordingly. documentation in the scripts is not very well at the moment but it should be enough to start.

for now the samples include:

  • tables: standard columns and arrays
  • constraints: primary keys, foreign keys, check constraints
  • triggers
  • sequences
  • indexes
  • views
  • loading blobs / clobs
  • plsql packages -> pgsql
  • materialzed views
  • partitioning
  • anonymous plsql / pgplsql blocks

did you know ( I didn’t ) that you can provide a star (*) when defining a number column ?

SQL> create table t1 ( a number(*), b number(*,10) );
Table created.
SQL> desc t1;
 Name	     Null?    Type
 ----------- -------- ----------------------------
 A                    NUMBER
 B                    NUMBER(38,10)

just noticed that there is a template script in the GI_HOME to enable RAC:

cat $GI_HOME/crs/config/relink_rac_on
#!/bin/sh
#
MAKE=/usr/bin/make
ECHO=/bin/echo
if [ $# -lt 1 ]; then
  $ECHO "Usage: relink_rac_on "
  exit 1
fi
ORACLE_HOME=$1
if [ $# -gt 1 ]; then
  MAKE=$2
else
  $ECHO "Make path has not been passed from command line."
fi
$ECHO "Checking for writable permission on $ORACLE_HOME/lib and $ORACLE_HOME/bin."
if [ -w $ORACLE_HOME/bin -a -w $ORACLE_HOME/lib ]; then
  $ECHO "running rac_on make target.."
  $MAKE -f $ORACLE_HOME/rdbms/lib/ins_rdbms.mk ORACLE_HOME=$ORACLE_HOME rac_on ioracle
  exit $?
else
  $ECHO "$ORACLE_HOME/lib and $ORACLE_HOME/bin directories are not writable, hence skipping relink operation."
fi
exit 0

this can be useful if you installed the ORACLE_HOME without enabling RAC and want to turn it on afterwards …

because various people come to this blog when they search for “acfs-9459: advm/acfs is not supported on this os version” I want to give some information on how you may tweak this ( if you really want to test ACFS/ADVM on an OS that is _not_ supported and there really is no need for this as oracle linux is free for testing ).

but before you really do this, keep in mind:

  • this is just for educational purposes
  • this is in no way supported by oracle
  • don’t do this on a production system
  • there is no guarantee that this works for you os

before executing the “roothas.pl” or “rootcrs.pl” scripts check the following file in your GI_HOME:

$GI_HOME/lib/osds_acfslib.pm

scroll down to where the various checks are performed. the lines should look similar to this:

# Testing for "oraclelinux_release", etc prevents us from dropping
# into the code below from other RH based distros like CentOS, for example.
  if ((defined($release)) &&                     # Redhat or OEL if defined
      (($release =~ /^redhat-release/) ||        # straight RH
       ($release =~ /^enterprise-release/) ||    # Oracle Enterprise Linux
       ($release =~ /^oraclelinux-release/)))    # Oracle Linux
  {
    my ($major, $minor, $patch, $vendor, $variation) = split /\./, $kver;
    my ($micro_number, $patch_level) = split /-/, $patch;    # e.g., 100 and 32
    if ($release =~ /AXS/)                 # Asianux Not supported
    {
      $vers = "ASIANUX";
      $supported = 0;
...

here you can tweak the code so that your os reports to be supported and you should be fine …

if you work with perl and want to connect to oracle you might want to DBD::Oracle. this is a oracle database driver for the perl DBI module.

there is no need to install this on a server which hosts an oracle instance. all you need from an oracle perspective is the oracle instant client.

to prepare the installation download the following files ( for your architecture ) from the above link:

  • instantclient-basic-linux.x64-11.2.0.3.0.zip
  • instantclient-sqlplus-linux.x64-11.2.0.3.0.zip
  • instantclient-sdk-linux.x64-11.2.0.3.0.zip

as I did a default mint install I additionally had to install the “Linux kernel AIO access library” package which is required for the instant client to work:

sudo apt-get install libaio1

all you need to do for getting the instant client to work is to unzip the downloaded files and setup your environment. in may case I unzipped the files to my home directory in a folder called oracle:

ls -la ~/oracle/instantclient_11_2/
total 185012
drwxr-xr-x 3 daniel daniel      4096 Aug 31 06:57 .
drwxr-xr-x 3 daniel daniel      4096 Aug 31 06:51 ..
-rwxrwxr-x 1 daniel daniel     25308 Sep 17  2011 adrci
-rw-rw-r-- 1 daniel daniel       437 Sep 17  2011 BASIC_README
-rwxrwxr-x 1 daniel daniel     46228 Sep 17  2011 genezi
-r--r--r-- 1 daniel daniel       368 Sep 17  2011 glogin.sql
lrwxrwxrwx 1 daniel daniel        56 Aug 31 06:57 libclntsh.so -> /home/daniel/oracle/instantclient_11_2/libclntsh.so.11.1
-rwxrwxr-x 1 daniel daniel  52761218 Sep 17  2011 libclntsh.so.11.1
-r-xr-xr-x 1 daniel daniel   7955322 Sep 17  2011 libnnz11.so
-rwxrwxr-x 1 daniel daniel   1971762 Sep 17  2011 libocci.so.11.1
-rwxrwxr-x 1 daniel daniel 118408281 Sep 17  2011 libociei.so
-r-xr-xr-x 1 daniel daniel    164836 Sep 17  2011 libocijdbc11.so
-r-xr-xr-x 1 daniel daniel   1503303 Sep 17  2011 libsqlplusic.so
-r-xr-xr-x 1 daniel daniel   1477446 Sep 17  2011 libsqlplus.so
-r--r--r-- 1 daniel daniel   2095661 Sep 17  2011 ojdbc5.jar
-r--r--r-- 1 daniel daniel   2714016 Sep 17  2011 ojdbc6.jar
drwxrwxr-x 4 daniel daniel      4096 Sep 17  2011 sdk
-r-xr-xr-x 1 daniel daniel      9352 Sep 17  2011 sqlplus
-rw-rw-r-- 1 daniel daniel       441 Sep 17  2011 SQLPLUS_README
-rwxrwxr-x 1 daniel daniel    191237 Sep 17  2011 uidrvci
-rw-rw-r-- 1 daniel daniel     66779 Sep 17  2011 xstreams.jar

for the environment I created a little file and sourced it:

echo"ORACLE_HOME=/home/daniel/oracle/instantclient_11_2
LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH
PATH=$ORACLE_HOME:$PATH
TNS_ADMIN=$ORACLE_HOME/network/admin
export ORACLE_HOME LD_LIBRARY_PATH PATH TNS_ADMIN" > ~/ora_env.sh 
mkdir -p ~/oracle/instantclient_11_2/network/admin
. ./ora_env.sh

before going on you should test if you can execute sqlplus ( if this does not work something is wrong with your environment ):

sqlplus
SQL*Plus: Release 11.2.0.3.0 Production on Fri Aug 31 07:26:46 2012
Copyright (c) 1982, 2011, Oracle.  All rights reserved.
Enter user-name: 

as a final test create a tnsnames.ora file with the connection parameters for your database and check if you can establish a connection:

echo "my_oracle_db=
  (DESCRIPTION=
    (ADDRESS=
      (PROTOCOL=TCP)
      (HOST=192.168.10.39)
      (PORT=1530)
    )
    (CONNECT_DATA=
      (GLOBAL_NAME=dbs300.local)
      (SID=dbs300)
    )
  )" >> ~/oracle/instantclient_11_2/network/admin/tnsnames.ora

let’s see if it works:

sqlplus my_oracle_usr/oracle@my_oracle_db
SQL*Plus: Release 11.2.0.3.0 Production on Fri Aug 31 07:53:44 2012
Copyright (c) 1982, 2011, Oracle.  All rights reserved.
|-----------------------------------------------------------------|
| This system is for the use of authorized users only.		  |
| Individuals using this computer system without authority, or in |
| excess of their authority, are subject to having all of their   |
| activities on this system monitored and recorded by system	  |
| personnel.							  |
|-----------------------------------------------------------------|
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
With the Partitioning, Automatic Storage Management, OLAP, Data Mining
and Real Application Testing options

as expected.

so far for the oracle part. because DBD::Oracle is a driver for the DBI package you probably need to install it first. you should have cpan installed along with your perl distribution.

which cpan
/usr/local/bin/cpan

if it’s not there search with your package manager and install it. for ubuntu/debian based distributions:

apt search cpan

for rpm based distributions:

yum search cpan

cpan requires an initial setup, but as this is straight forward I’ll just mention the important questions/answers:

cpan
Would you like to configure as much as possible automatically? [yes] yes
What approach do you want?  (Choose 'local::lib', 'sudo' or 'manual')
 [local::lib] sudo

that’s it. now we are ready to install the DBI module:

cpan
install DBI
quit

for the DBD::Oracle module I decided to just download the source file and to do a manual compilation/installation:

cpan
get DBD::Oracle
quit
cd ~/.cpan/build/DBD-Oracle-1.50*
perl Makefile.PL
make
sudo make install

easy, isn’t it ?

create a little perl script and see if you can execute a test-statement:

echo "#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my @results;
my $db = DBI->connect ( 'dbi:Oracle:host=192.168.10.39;sid=dbs300;port=1530', 'my_oracle_usr/oracle', '' )
         or die "Connection failed: ". DBI->errstr;
my $sql = $db->prepare('SELECT \'connected\' FROM DUAL' )
         or die "Statement preparation failed: ". $db->errstr;
$sql->execute()
      or die "Statement execution failed:".$sql->errstr;
while ( @results = $sql->fetchrow_array() ) {
   print "$results[0] \n";
}
1;" >> oracle_test.pl
chmod u+x oracle_test.pl
./oracle_test.pl 
connected 

time to have fun with perl and oracle :)

ever wanted to pass a value of type interval to a procedure ? not a big a deal you might think:

create or replace procedure p1 ( a interval day to second ) is
begin
  null;
end;
/
exec p1 ( a => interval '10 00:00:00' day to second );

works as expected. but what if you want to pass an interval with a precision of more than two ?

exec p1 ( a => interval '101 00:00:00' day to second );
BEGIN p1 ( a => interval '101 00:00:00' day to second ); END;
*
ERROR at line 1:
ORA-01873: the leading precision of the interval is too small
ORA-06512: at line 1

should work, shouldn’t it ?
let’s try to specify the precision in the procedure’s declaration:

create or replace procedure p1 ( a interval day(3) to second ) is
begin
  null;
end;
  5  /
Warning: Procedure created with compilation errors.
SQL> show errors;
Errors for PROCEDURE P1:
LINE/COL ERROR
-------- -----------------------------------------------------------------
1/30	 PLS-00103: Encountered the symbol "(" when expecting one of the
	 following:
	 to

ok, it is not possible to specify this in the declaration. can we pass the precision to the procedure along with the parameter ?

exec p1 ( a => interval '101 00:00:00' day(3) to second );
BEGIN p1 ( a => interval '101 00:00:00' day(3) to second ); END;
                                           *
ERROR at line 1:
ORA-06550: line 1, column 44:
PLS-00103: Encountered the symbol "(" when expecting one of the following:
) , * & = - +  at in is mod remainder not rem
  or != or ~= >= <=  and or like like2
like4 likec between to || multiset member submultiset

syntax error, again. what’s wrong ? the documentation tells that the default precision is 2, but can be of any value between 0 and 9. so this must be possible somehow.

the solution is either to move the procedure to a package and to declare a subtype:

create or replace package ptest
is
   subtype my_interval is interval day(3) to second;
   procedure p1 ( v_interval IN my_interval );
end;
/
create or replace package body ptest
is
   procedure p1 ( v_interval IN my_interval ) 
   is
   begin
     null;
   end p1;
end;
/
SQL> exec ptest.p1 ( interval '230 23:0:0.1' day to second );
PL/SQL procedure successfully completed.
SQL>  exec ptest.p1 ( interval '23 23:0:0.1' day to second );
PL/SQL procedure successfully completed.

… or to reference the column of a table:

SQL> create table t1 ( a interval day(3) to second );
Table created.
SQL> create or replace procedure p1 ( a t1.a%type ) is begin null; end;
  2  /
Procedure created.
SQL> exec p1 ( a => interval '101 00:00:00' day to second);
PL/SQL procedure successfully completed.
SQL> exec p1 ( a => interval '10 00:00:00' day to second);
PL/SQL procedure successfully completed.
SQL> 

recently a customer had the request to export a column to a flat file. nothing special, but the request was to encrypt the column as the data needs to be delivered to a third site. as you might know oracle provides the dbms_obfuscation_toolkit package which might be used for requests like this. so, here is an example ( as wordpress does not allow to upload scripts, sorry for the long pasting :) ):

/**
Export a column for a given table and schema in encrypted format to csv
PARAMETERS: OWNER
            TABLE
            COLUMN
            ENRYPTION-KEY
**/
SET LINES 164 VERIFY OFF PAGES 999 SERVEROUT ON FEEDBACK OFF
VARIABLE owner VARCHAR2(30)
VARIABLE table_name VARCHAR2(30)
VARIABLE column_name VARCHAR2(30)
VARIABLE encryption_key VARCHAR2(100)
VARIABLE continue VARCHAR2(1);
EXEC dbms_output.put_line ( chr(13) );
EXEC dbms_output.put_line ( '------------------------------------------------------' );
EXEC dbms_output.put_line ( '-- INFO: Starting encrypted column export  ');
EXEC dbms_output.put_line ( chr(13) );
/**
do some sanity checks
**/
DECLARE
  lv_owner all_users.username%TYPE;
  lv_directory all_directories.directory_path%TYPE;
  FUNCTION schema_exists ( pv_schema IN all_users.username%TYPE )
                         RETURN BOOLEAN
  IS
    CURSOR cur_schema
    IS SELECT 'ok'
         FROM all_users
        WHERE username = pv_schema
    ;
    lv_cursor_result VARCHAR2(2);
  BEGIN
    OPEN cur_schema;
      FETCH cur_schema INTO lv_cursor_result;
    CLOSE cur_schema;
    RETURN NVL ( lv_cursor_result, 'xx' ) = 'ok';
  END schema_exists;
  FUNCTION table_exists ( pv_schema IN all_users.username%TYPE
                        , pv_table IN all_tables.table_name%TYPE
                        ) RETURN BOOLEAN
  IS
    CURSOR cur_table
    IS SELECT 'ok'
         FROM all_tables
        WHERE owner = pv_schema
          AND table_name = pv_table
    ;
    lv_cursor_result VARCHAR2(2);
  BEGIN
    OPEN cur_table;
      FETCH cur_table INTO lv_cursor_result;
    CLOSE cur_table;
    RETURN NVL ( lv_cursor_result, 'xx' ) = 'ok';
  END table_exists;
  FUNCTION column_exists ( pv_schema IN all_users.username%TYPE
                         , pv_table IN all_tables.table_name%TYPE
                         , pv_column IN all_tab_columns.column_name%TYPE
                         ) RETURN BOOLEAN
  IS
    CURSOR cur_column
    IS SELECT 'ok'
         FROM all_tab_columns
        WHERE owner = pv_schema
          AND table_name = pv_table
          AND column_name = pv_column
    ;
    lv_cursor_result VARCHAR2(2);
  BEGIN
    OPEN cur_column;
      FETCH cur_column INTO lv_cursor_result;
    CLOSE cur_column;
    RETURN NVL ( lv_cursor_result, 'xx' ) = 'ok';
  END column_exists;
  FUNCTION exp_directory_exists RETURN BOOLEAN
  IS
    CURSOR cur_directory
    IS SELECT directory_path
         FROM all_directories
        WHERE directory_name = 'DATA_PUMP_DIR'
    ;
  BEGIN
    OPEN cur_directory;
      FETCH cur_directory INTO lv_directory;
    CLOSE cur_directory;
    RETURN lv_directory IS NOT NULL;
  END exp_directory_exists;
BEGIN
   :owner := UPPER('&1');
   :table_name := UPPER('&2'); 
   :column_name := UPPER('&3');
   :encryption_key := '&4';
   IF :encryption_key IS NULL
      OR
      -- encryption key must be at least 80 characters
      length ( :encryption_key ) < 80
   THEN
     raise_application_error ( -20001
                             , '--- CHECK INPUT: The encryption key "'||:encryption_key||'" is too short or empty ! ');
     :continue := 'N';
   END IF;
   IF NOT ( schema_exists ( pv_schema => :owner ) )
   THEN
     raise_application_error ( -20001
                             , '--- CHECK INPUT: The schema '||:owner||' does not exist in this database ! ');
     :continue := 'N';
   END IF;
   IF NOT ( table_exists ( pv_schema => :owner
                         , pv_table => :table_name
                         ) )
   THEN
     raise_application_error ( -20001
                             , '--- CHECK INPUT: The table '||:table_name||' does not exist for the schema '||:owner||' ! ');
     :continue := 'N';
   END IF;
   IF NOT ( column_exists ( pv_schema => :owner
                          , pv_table => :table_name
                          , pv_column => :column_name
                          ) )
   THEN
     raise_application_error ( -20001
                             , '--- CHECK INPUT: The column '||:column_name||' does not exist for the table '||:table_name||' in schema '||:owner||'! ');
     :continue := 'N';
   END IF;
   IF NOT ( exp_directory_exists )
   THEN
     raise_application_error ( -20001
                             , '--- CHECK DIRECTORY: Can not find directory DATA_PUMP_DIR ! ');
     :continue := 'N';
   ELSE
     dbms_output.put_line (' --- INFO: File will be located here : '||lv_directory );
   END IF;
   :continue := 'Y';
END;
/
DECLARE
  lv_owner all_users.username%TYPE := UPPER(:owner);
  lv_table all_tables.table_name%TYPE := UPPER(:table_name);
  lv_column all_tab_columns.column_name%TYPE := UPPER(:column_name);
  lt_file_type utl_file.file_type;
  lv_statement VARCHAR2(4000);
  ln_cursor NUMBER;
  ln_amount_of_rows INTEGER := 0;
  lv_value VARCHAR2(4000);
  lv_seed VARCHAR2(4000) := :encryption_key;
BEGIN
  IF ( :continue = 'Y' ) 
  THEN
    dbms_output.put_line (' --- INFO: selected schema is : '||lv_owner );
    dbms_output.put_line (' --- INFO: selected table is  : '||lv_table );
    dbms_output.put_line (' --- INFO: selected column is : '||lv_column );
    dbms_output.put_line (' --- INFO: exporting to       : DATA_PUMP_DIR ');
    -- open file for writing
    lt_file_type := utl_file.fopen ( location => 'DATA_PUMP_DIR'
                                   , filename => 'exp_'||lv_owner||'_'||lv_table||'_'||lv_column||'_'
                                                  ||TO_CHAR(sysdate,'DD.MM.YYYY HH24:MI:SS')||'.csv'
                                   , open_mode => 'w'
                                   );
    lv_statement := 'SELECT '||lv_column||' FROM '||lv_owner||'.'||lv_table;
    dbms_output.put_line (' --- INFO: statement is: '||lv_statement );
    -- prepare statement
    ln_cursor := dbms_sql.open_cursor;
    dbms_sql.parse ( c => ln_cursor
                   , statement => lv_statement
                   , language_flag => dbms_sql.v7 
                   );
    dbms_sql.define_column ( c => ln_cursor
                           , position => 1
                           , column => lv_column
                           , column_size => 4000
                           );
    ln_amount_of_rows := dbms_sql.execute ( c => ln_cursor ); 
    LOOP
      -- fetch all the rows
      IF dbms_sql.fetch_rows ( c => ln_cursor ) = 0
      THEN
        EXIT;
      ELSE
        ln_amount_of_rows := ln_amount_of_rows + 1;
        -- get the column value
        dbms_sql.column_value ( c => ln_cursor
                              , position => 1
                              , value => lv_value 
                              ); 
        -- input must be a multiple of 8 bytes, so rpadding with blanks
        dbms_obfuscation_toolkit.desencrypt ( input_string => rpad ( lv_value
                                                                   , ( trunc ( length ( lv_value ) / 8 ) + 1 ) * 8
                                                                   , chr(0)
                                                                   )
                                            , key_string => lv_seed 
                                            , encrypted_string  => lv_value
                                            );
        -- write to output file
        utl_file.put_line ( file => lt_file_type 
                          , buffer => utl_raw.cast_to_raw ( lv_value )
                          );
      END IF;
    END LOOP;
    -- close cursor
    dbms_sql.close_cursor ( c => ln_cursor );
    -- close file handle
    utl_file.fclose ( file => lt_file_type );
    dbms_output.put_line (' --- Exported '||ln_amount_of_rows||' rows !!! ');
  END IF;
END;
/

for decrypting one could use this snippet:

/**
Decrypt the encrypted data in the exported csv files
**/
SET LINES 164 VERIFY OFF PAGES 999 SERVEROUT ON FEEDBACK OFF
VARIABLE filename VARCHAR2(4000)
VARIABLE encryption_key VARCHAR2(100)
VARIABLE continue VARCHAR2(1);
EXEC dbms_output.put_line ( chr(13) );
EXEC dbms_output.put_line ( '------------------------------------------------------' );
EXEC dbms_output.put_line ( '-- INFO: Starting decryption  ');
EXEC dbms_output.put_line ( chr(13) );
/**
do some sanity checks
**/
DECLARE
  lv_directory all_directories.directory_path%TYPE;
  FUNCTION exp_directory_exists RETURN BOOLEAN
  IS
    CURSOR cur_directory
    IS SELECT directory_path
         FROM all_directories
        WHERE directory_name = 'DATA_PUMP_DIR'
    ;
  BEGIN
    OPEN cur_directory;
      FETCH cur_directory INTO lv_directory;
    CLOSE cur_directory;
    RETURN lv_directory IS NOT NULL;
  END exp_directory_exists;
BEGIN
  :filename := '&1';
  :encryption_key := '&2';
  IF ( length ( :encryption_key ) < 80 ) 
  THEN
     raise_application_error ( -20001
                             , '--- CHECK INPUT: The encryption key "'||:encryption_key||'" is too short or empty ! ');
     :continue := 'N';
  END IF;
  IF NOT ( exp_directory_exists )
  THEN
    raise_application_error ( -20001
                            , '--- CHECK DIRECTORY: Can not find directory DATA_PUMP_DIR ! ');
    :continue := 'N';
  ELSE
    dbms_output.put_line (' --- INFO: File to be loaded : '||lv_directory );
  END IF;
  :continue := 'Y';
END;
/
DECLARE
  lt_file_type utl_file.file_type;
  lv_buffer VARCHAR2(4000);
  lv_buffer_raw RAW(4000);
  lraw_decrypted_value RAW(4000);
  lv_value RAW(32767);
BEGIN
  IF ( :continue = 'Y' ) 
  THEN
    -- open file handle
    lt_file_type := utl_file.fopen ( location => 'DATA_PUMP_DIR'
                                   , filename => :filename
                                   , open_mode => 'r'
                          --         , max_linesize => 4000
                                   );
    -- read through the file line by line
    LOOP
      BEGIN
        utl_file.get_line ( file => lt_file_type
                         ,  buffer => lv_buffer
                         );
        lv_buffer_raw := lv_buffer;
        dbms_obfuscation_toolkit.desdecrypt ( input => lv_buffer_raw
                                            , key => utl_raw.cast_to_raw ( :encryption_key )
                                            , decrypted_data => lraw_decrypted_value
                                            );
        dbms_output.put_line ( 'encrypted value: '||utl_raw.cast_to_varchar2(lraw_decrypted_value) );
      EXCEPTION
        WHEN no_data_found THEN EXIT;
      END;
    END LOOP;
    -- close file handle
    utl_file.fclose ( file => lt_file_type );
  ELSE
    NULL;
  END IF;
END;
/

for encrypting and decrypting two simple calls now do the trick:

# to encrypt:
@encrypted_column_export.sql USER TABLE_NAME COLUMN_NAME ENCRYPTION_KEY
# to decrypt
@decrypt_exported_csv.sql FILE_NAME ENCRYPTION_KEY