Archivos de la categoría ORA-?????

En esta categoría se clasificarán las entradas relacionadas con los errores ORA- de Oracle. Es la manera más rápida de acceder al error detectado.
Introduce el código de error en el buscador en el formato “ORA-XXXXX” y te aparecerá la información del error buscado si es que la hay.

Utilidad oerr de ORACLE

OERR es una utilidad de Oracle que extrae los mensajes de error informándote de la posible causa y recomendando acciones que se sugieren a partir de los archivos de mensajes estándar de Oracle. Esta utilidad es muy útil ya que puede extraer los errores específicos de OS que no están en los mensajes de error y códigos de Manual genéricos.

Oerr sólo funciona en sistemas Unix/Linux y se instala junto con el software de Base de datos en el directorio $ORACLE_HOME/bin.

Para saber que acciones podemos realizar cuando se produce un error Oracle que muestro un mensaje ORA- podemos ejecutar la siguiente instrucción:

$ cd $ORACLE_HOME/bin (sino lo tenemos en el PATH)
$ oerr ora 600       (Aquí poner el error ORA-NNNNN que nos haya dado)
ora-00600: internal error code, arguments: [%s], [%s], [%s], [%s], [%s], [%s], [%s], [%s]
*Cause:  This is the generic internal error number for Oracle program exceptions.  This indicates that a process has encountered an exceptional condition.
*Action: Report as a bug - the first argument is the internal error number

ORA-01536: space quota exceeded for tablespace …

Este error se produce cuando se ha llegado al máximo de quota que tiene asignado el usuario sobre ese tablespace. La solución es ampliar esa quota, o bien asignarle una ilimitada.

Conectado con el usuario con  permisos de DBA o system:

SQL>  ALTER USER <usuario_afectado> QUOTA UNLIMITED ON  <tablespace_afectado>;

Si vemos que haciendo lo anterior sigue dando error, ejecutar la siguiente instrucción:

SQL> GRANT UNLIMITED TABLESPACE ON  <tablesapace_afectado>  TO <usuario_afectado>;

ORA-00054: recurso ocupado y obtenido con NOWAIT especificado o timeout vencido || Bloqueos

Este error suele aparecer cuando existen bloqueos esperando a que otro usuario termine una operación, para poder realizar la suya. Uno de los más comunes que suelen suceder  es cuando se hacen “truncate” o “drop” de tablas y no nos deja hacerlo porque las tabla/s están bloqueada/s por otros procesos de ese mismo u otros usuarios.

En el ejemplo que explico seguidamente el problema se produjo porque al llenarse un tablespace (datafile al 100%) realizando unos “inserts” en una tabla, el proceso se quedó bloqueado hasta poder hacer la inserción. En  mi caso, como podía volver a lanzar el proceso sin problema, y no quería ampliar más la ocupación del tablespace afectado. Decidí matar los procesos que estaban bloqueados y volver a lanzar la ejecución de los inserts que provocaron por error que se llenara el tablespace.

En este ejemplo sólo se va a tratar el problema del bloqueo, no se va a tener en cuenta el problema de la falta de espacio del tablespace, porque eso fue debido a otra causa, pero que fue, en parte el causante del error que os voy a explicar y cuya solución veréis en esta entrada de blog.

Entro en detalle:

Al intentar hacer un truncate de una tabla salta el error:

ORA-00054: recurso ocupado y obtenido con NOWAIT especificado o timeout vencido

SQL> truncate table <mi_tabla> ;
truncate table <mitabla>
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified

Esto ocurre porque se ha bloqueado uno o varios registros mediante setencias SQL. Select´s especificados como “NO WAIT” o “FOR UPDATE NOWAIT” o por una operación DDL que fue bloqueada. La solución podía pasar por hacer el commit o rollback. El commit no funcionó porque no hay espacio en el tablespace. El rollback no se probó, porque preferí matar la sesión que estaba provocando el error tal y como explico seguidamente.

En Oracle hay una vista llamada v$lock que nos indica los objetos que se encuentran en bloqueo, el identificador de usuario,  sesion y el tipo de bloqueo. Una join con la tabla dba_objects nos proporcionará ademas el nombre y tipo de los objetos bloqueados. La consulta seria como sigue:

SELECT
     decode(L.TYPE,'TM','TABLE','TX','Record(s)') TYPE_LOCK,
     decode(L.REQUEST,0,'NO','YES') WAIT,
     S.OSUSER OSUSER_LOCKER,
     S.PROCESS PROCESS_LOCKER,
     S.USERNAME DBUSER_LOCKER,
     O.OBJECT_NAME OBJECT_NAME,
     O.OBJECT_TYPE OBJECT_TYPE,
     CONCAT(' ',s.PROGRAM) PROGRAM,
     O.OWNER OWNER
 FROM v$lock l,dba_objects o,v$session s
 WHERE l.ID1 = o.OBJECT_ID AND s.SID =l.SID AND l.TYPE in ('TM','TX','UL');

Existen varios tipos de bloqueo (TM,TX,UL):

TM – DML enqueue. Bloqueos a nivel de tabla. Los bloqueos a nivel de tabla son creados cuando se ejecuta una sentencia DML del tipo: update, insert, delete, select ..for update sobre la tabla entera.

Por ejemplo:

DELETE from <mi_tabla>;
TRUNCATE <mi_tabla>;
UPDATE <mi_tabla> SET campo1 = valor;

haciendo un “delete” fue el error en este ejemplo. Por tanto tenía un bloqueo a nivel de tabla.

TX – Transaction enqueue. Bloqueos a nivel de fila. Los bloqueos a nivel de fila se crean cuando se ejecutan senencias DML contra un conjunto de registros específicos.

UL – User supplied.  Bloqueos a nivel de usuario.

Para solucionar el error:

Nos conectaremos como system a Oracle y ejecutaremos la siguiente consulta para ver si existe algún bloqueo:

 SQL> show user
 USER is "SYSTEM"
 SQL> select * from v$lock where request!=0;

Si hacemos una join con v$open_cursor podremos ver que consulta es la que se encuentra parada a la espera de que se produzca el desbloqueo para poder ejecutarse.

En la consulta siguiente podemos ver las sentencias paradas esperando a que termine un bloqueo, la sentencia que quieren ejecutar y el id de proceso que las está bloqueando:

select /*+ ordered
    no_merge(L_WAITER)
    no_merge(L_LOCKER) use_hash(L_LOCKER)
    no_merge(S_WAITER) use_hash(S_WAITER)
    no_merge(S_LOCKER) use_hash(S_LOCKER)
    use_nl(O)
    use_nl(U)
    */
    /* first the table-level locks (TM) and mixed TM/TX TX/TM */
    S_LOCKER.OSUSER OS_LOCKER,
    S_LOCKER.USERNAME LOCKER_SCHEMA,
    S_LOCKER.PROCESS LOCKER_PID,
    S_WAITER.OSUSER OS_WAITER,
    S_WAITER.USERNAME WAITER_SCHEMA,
    S_WAITER.PROCESS WAITER_PID,
    'Table lock (TM): '||U.NAME||'.'||O.NAME||
    ' - Mode held: '||
    decode(L_LOCKER.LMODE,
    0, 'None', /* same as Monitor */
    1, 'Null', /* N */
    2, 'Row-S (SS)', /* L */
    3, 'Row-X (SX)', /* R */
    4, 'Share', /* S */
    5, 'S/Row-X (SSX)', /* C */
    6, 'Exclusive', /* X */
    '???: '||to_char(L_LOCKER.LMODE))||
    ' / Mode requested: '||
    decode(L_WAITER.REQUEST,
    0, 'None', /* same as Monitor */
    1, 'Null', /* N */
    2, 'Row-S (SS)', /* L */
    3, 'Row-X (SX)', /* R */
    4, 'Share', /* S */
    5, 'S/Row-X (SSX)', /* C */
    6, 'Exclusive', /* X */
    '???: '||to_char(L_WAITER.REQUEST))
    SQL_TEXT_WAITER
from
    V$LOCK L_WAITER,
    V$LOCK L_LOCKER,
    V$SESSION S_WAITER,
    V$SESSION S_LOCKER,
    sys.OBJ$ O,
    sys.USER$ U
where S_WAITER.SID = L_WAITER.SID
    and L_WAITER.TYPE IN ('TM')
    and S_LOCKER.sid = L_LOCKER.sid
    and L_LOCKER.ID1 = L_WAITER.ID1
    and L_WAITER.REQUEST > 0
    and L_LOCKER.LMODE > 0
    and L_WAITER.ADDR != L_LOCKER.ADDR
    and L_WAITER.ID1 = O.OBJ#
    and U.USER# = O.OWNER#
union
    select /*+ ordered
        no_merge(L_WAITER)
        no_merge(L_LOCKER) use_hash(L_LOCKER)
        no_merge(S_WAITER) use_hash(S_WAITER)
        no_merge(S_LOCKER) use_hash(S_LOCKER)
        no_merge(L1_WAITER) use_hash(L1_WAITER)
        no_merge(O) use_hash(O)
        */
        /* now the (usual) row-locks TX */
        S_LOCKER.OSUSER OS_LOCKER,
        S_LOCKER.USERNAME LOCKER_SCHEMA,
        S_LOCKER.PROCESS LOCK_PID,
        S_WAITER.OSUSER OS_WAITER,
        S_WAITER.USERNAME WAITER_SCHEMA,
        S_WAITER.PROCESS WAITER_PID,
        'TX: '||O.SQL_TEXT SQL_TEXT_WAITER
    from
        V$LOCK L_WAITER,
        V$LOCK L_LOCKER,
        V$SESSION S_WAITER,
        V$SESSION S_LOCKER,
        V$_LOCK L1_WAITER,
        V$OPEN_CURSOR O
    where S_WAITER.SID = L_WAITER.SID
        and L_WAITER.TYPE IN ('TX')
        and S_LOCKER.sid = L_LOCKER.sid
        and L_LOCKER.ID1 = L_WAITER.ID1
        and L_WAITER.REQUEST > 0
        and L_LOCKER.LMODE > 0
        and L_WAITER.ADDR != L_LOCKER.ADDR
        and L1_WAITER.LADDR = L_WAITER.ADDR
        and L1_WAITER.KADDR = L_WAITER.KADDR
        and L1_WAITER.SADDR = O.SADDR
        and O.HASH_VALUE = S_WAITER.SQL_HASH_VALUE
/

Pero sino devuelve nada, que era lo que me pasaba a mi, y como el error me lo generaba un delete o un truncate, tuve que ejecutar la siguiente consulta para ver que era lo que tenía que “matar”.

SQL> SELECT mode_held FROM dba_dml_locks where OWNER='<mi_usuario>';
MODE_HELD
-------------
Row-X (SX)
Row-X (SX)
Row-X (SX)

Seguidamente podía listar los bloqueos que ocurrían en ese momento en la base de datos.Y  por medio de los campos LMODE y REQUEST saber de que tipo son :

none
null (NULL)
row-S (SS)
row-X (SX)
share (S)
S/Row-X (SSX)
exclusive (X)

SELECT oracle_username || ' (' || s.osuser || ')' username
    ,  s.sid || ',' || s.serial# sess_id
    ,  owner || '.' || object_name object
    ,  object_type
    ,  decode( l.block
       ,       0, 'Not Blocking'
       ,       1, 'Blocking'
       ,       2, 'Global') status
    ,  decode(v.locked_mode
      ,       0, 'None'
      ,       1, 'Null'
      ,       2, 'Row-S (SS)'
      ,       3, 'Row-X (SX)'
      ,       4, 'Share'
      ,       5, 'S/Row-X (SSX)'
      ,       6, 'Exclusive', TO_CHAR(lmode)) mode_held
FROM v$locked_object v,dba_objects d,v$lock l,v$session s
WHERE v.object_id = d.object_id
   and v.object_id = l.id1
   and v.session_id = s.sid
ORDER BY oracle_username,session_id
/

Sale algo como …

Intento matarlo …

SQL> alter system kill session '215,2774';
alter system kill session '215,2774'
*
ERROR at line 1:
ORA-00031: session marked for kill

Pruebo entonces con immediate y si me funciona.

alter system kill session '215,2774' immediate;

Pero si lo anterior fallase buscaría por medio de esta SQL el  SPID que es el número de proceso de Linux/unix que usaría para matar el proceso a nivel de S.O.

SELECT s.sid, p.spid, s.osuser, s.program
FROM   v$process p, v$session s
WHERE  p.addr = s.paddr;

La consulta anterior me muestra todos los procesos, buscaría el SID del proceso que quiero matar y me anotaría el campo SPID que es el que definitivamente usaré para matar el proceso. Pero si quiero sólo buscar el SID de la que quiero matar, ejecutaría:

SELECT s.sid, p.spid, s.osuser, s.program
FROM   v$process p, v$session s
WHERE  p.addr = s.paddr and s.sid = '215'; --> SID basado en mi ejemplo

Para saber cual es el SPID busco con el campo SID que me salia en la primera consulta. Es el primer valor antes de la “,” que uso en el” alter kill session …”

Para matar el proceso a nivel de S.O en mi caso un Linux. Se hace así:

Conectado como root al linux …

# ps -ef |grep  <SPID> --> Donde SPID es el número de proceso
# kill -9 <SPID>

ORA-01552: no se puede usar segm. rollback SYSTEM para tablespace ”

En esta entrada de blog voy a intentar dar alguna solución a este error sobretodo para versiones antiguas de ORACLE. En versiones a partir de la 10G la solución estaría más basada en los tablespace de UNDO que en los segmentos de rollback propiamente dichos.

El error que suele aparecer es el siguiente:

Error de Java Gateway: JDBC Gateway SP execute(0) error 0:
 ORA-01552: no se puede usar segm. rollback SYSTEM para tablespace '<tablespace_afectado>' NO-SYSTEM

Donde … tablespace_afectado corresponderá al tablespace específico de tu BBDD.

Lo primero que tenemos que hacer es consultar el fichero de alertas del servidor Oracle. Normalmente ubicado en $ORACLE_HOME/oracle/admin/<SID>/bdump. El fichero de alertas suele llamarse alert_<SID>.log y almacena todos los movimientos y errores de la BBDD. Donde <SID> es el nombre de nuestra instancia.

Normalmente en esta ruta, se almacenan también los ficheros de traza que tienen la extensión.trc y almacenarán el error concreto de manera más específica que el fichero alert_<SID>.log.

En el caso que nos ocupa, el error aparecerá en el fichero de alertas, pero muy probablemente en el mismo fichero de alerta se nos indicará el nombre del fichero de traza que almacenará el error “origen” que ha provocado el error que finalmente vemos en el fichero de traza.

Por tanto, miraremos el fichero de alerta para encontrar el fichero de traza que miraremos posteriormente para averiguar “el nuevo error” o el “error origen” para ponerle remedio.

En el ejemplo siguiente vemos un posible  caso de lo que puede aparecer en el fichero de alerta:

FULL status of rollback segment 19 set.
ORA-1650: unable to extend rollback segment RBS9 by 128 intablespace RBS

Failure to extend rollback segment 10 because of 1650 condition
FULL status of rollback segment 10 set.
Thu Aug 22 14:04:52 2013
Errors in file /oracle/admin/<SID>/bdump/snp3_3448_p103.trc:
ORA-12012: error on auto execute of job 121
ORA-01552: cannot use system rollback segment for non-system tablespace '<tablespace_afectado>'

Lo siguiente sería consultar el fichero de traza que hemos visto en el de alerta….

Si nos fijamos en el ejemplo anterior, en negrita, tenemos el nombre del fichero de traza que nos detalla de manera más precisa el error que ha provocado la falta de espacio en los rollback segments: (snp3_3448_p103.trc).

Si miramos su contenido veremos algo como:

Unix process pid: 3448, image: oracle@h0514 (SNP3)
*** SESSION ID:(511.55794) 2013-08-12 14:25:26.821
*** 2013-08-12 14:25:26.821
ORA-12012: error on auto execute of job 101
ORA-01555: snapshot too old: rollback segment number 6 with name "RBS5" too small
ORA-06512: at "OPS$USUARIO.PROCEDIMIENTO", line 109

Este fichero nos informa del pid del proceso Unix por si tenemos que cancelarlo, el segmento de rollback afectado y el procedimiento, acción, función, etc… que se estaba ejecutando y que ha provocado el error. Esto lo usaremos para informar al usuario que lanza dicho proceso o a los desarrolladores de que es lo que está provocando la carga excesiva, por si es necesario, crear algún segmento de rollback especial para este proceso y especificarlo en la aplicación por medio del comando:

SQL> set transaction use rollback segment <nombre_tablespace_rollback>;

Pero la solución para evitar el error ORA_01552 sería ampliar con un datafile <tablespace_afectado> y revisar la contención, que los datafiles del tablespace del segmento de rollback tengan espacio y el parámetro OPTIMAL de los segmentos de rollback.

Para modificar el OPTIMAL de un segmento de rollback se hace:

ALTER ROLLBACK SEGMENT "RBS5" STORAGE ( OPTIMAL 4096K);

ORA-28002: The password expire within 7 days … como solucionarlo

Es posible que en alguna ocasión que como DBA te llame un usuario y te diga que al intentar acceder con su usuario a su cuenta le salga el mensaje de error:

ORA-28002: the password will expire within 7 days

Tú le cambias la contraseña, vuelves a probar, y desgraciadamente sigue saliendo el mismo error. Incluso, el usuario puede tener ya la cuenta bloqueada después de varios intentos fallidos de conexión. Visitar este enlace para ver como desbloquear la cuenta: Bloquear/Desbloquear una cuenta ORACLE

El problema es de fácil solución. Todo es debido al perfil (profile) que tenga asociado dicho usuario. Normalmente, el perfil asociado a un usuario en el proceso de creación es DEFAULT. Y dentro de ese perfil, se definen valores como la caducidad y otros parámetros que determinan la caducidad de la contraseña. Para evitar este error, revisaremos dichos parámetros del perfil.

Los podemos consultar via SQLPLUS o bien con OEM. En el ejemplo veremos las dos maneras.

Debemos tener en consideración que …

- Es recomendable que el password expire aunque sea en un periodo prolongado de tiempo.
- El perfil se define en la sentencia de creación del usuario “CREATE USER …”
- Podemos modificar los perfiles una vez creado dicho usuario e incluso cambiar el perfil del usuario.

Empecemos…..

Desde SQLPLUS

Miramos que perfil tiene el usuario asignado:

SQL>  select username, profile from dba_users
where username = 'USUARIO';

 

USERNAME                       PROFILE
—————————— ——————————
USUARIO                          DEFAULT

vemos que tiene asignado el perfil DEFAULT, será el que editaremos para cambiar los valores de expiración de la contraseña. Estos valores de perfil son PASSWORD_LIFE_TIME y basados en la tabla de atributos de usuario ASTATUS y EXPTIME.

sacamos los valores …

SQL> select username, account_status, expiry_date from dba_users;

USERNAME                       ACCOUNT_STATUS                   EXPIRY_DATE
—————————— ——————————– ————
USUARIO                           EXPIRED & LOCKED                 15-AUG-12

en este caso el usuario le expiró el 15 de Agosto de 2012 y además, tiene la cuenta bloqueada. Por tanto, primero desbloquearemos la cuenta tal y como explicamos en la entrada de blog del enlace anterior. Y procedermos a cambiar el límite de caducidad en el profile.

SQL> ALTER PROFILE "DEFAULT" LIMIT PASSWORD_LIFE_TIME UNLIMITED;

Teniendo en cuenta que no le estamos poniendo fecha de “expiración” y siempre es aconsejable hacerlo.  Esto por si sólo no solventa el error, hasta que las tareas de cambio de password, desbloqueo pendientes estén realizadas. Una vez hechas, el usuario al conectarse cogerá los nuevos valores definidos en el perfil.

Recordad que para cambiar la clave del usuario haremos:

SQL>  ALTER USER usuario IDENTIFIED BY VALUES 'clave';

CONCLUSIÓN:
La caducidad de la contraseña está controlada por el límite PASSWORD_LIFE_TIME definido en el perfil (DEFAULT por defecto) y se activa al iniciar la sesión Oracle, pero depende de la combinación de los parámetros ASTATUS y EXPTIME y se reestablece por un cambio de contraseña (alter user … identified by …). El cambio de perfil NO cambiará ASTATUS. En el cambio de contraseña se restablece ASTATUS y PTIME, pero no EXPTIME. Por tanto, los usuarios que estén bloqueados, se tendrán que desbloquear y los que esten en “EXPIRED” se les tendrá que cambiar la contraseña.

Ahora vamos a ver lo mismo desde OEM …

iremos a la pestaña “Servidor” y en el apartado Seguridad pulsaremos en el enlace “Perfiles”.

Seguidamente en el perfil (normalmente suele ser el perfil DEFAULT) que corresponda al usuario afectado, pulsaremos en el perfil y luego lo editaremos …

Por último, editaremos el perfil y cambiaremos sus valores, pulsaremos en “Aplicar” para aplicar los cambios realizados.

Pues esto es todo …
Sencillito!!! ;-)

 

ORA-01031: privilegios insuficientes … al hacer un GRANT

Al hacer un GRANT conectado tanto como el usuario SYSTEM y/o SYS en una versión Oracle 7.3.4 para dar permisos de un package de un usuario a otro me aparece el error Oracle:

ORA-01031: privilegios insuficientes

La solución más rápida a este error es conectarse como el usuario propietario del Package (u del objeto que nos dé problemas) y ejecutar el comando GRANT.

Ej.

$ sqlplus system/password_system
SQL> connect usuario/password_usuario
SQL> show user
el usuario es “usuario”
SQL>  GRANT DELETE, INSERT, SELECT, UPDATE ON OPS$USUARIO.PACKAGE_EJEMPLO TO OPS$USUARIO_DESTINO;

Pero en mi caso, no tenía el password del usuario en concreto y tampoco me dejaban cambiarlo por si afectaba a alguna aplicación. ¿ Que se puede hacer en este caso ?

Como sí que tenía el password de root a nivel de sistema operativo (Unix en este caso) hice lo siguiente:

# su – usuario
$ whoami
usuario
$ sqlplus /

Ahora ya estaba conectado como el usuario “usuario” y ya podía ejecutar el comando GRANT.

SQL> show user
el usuario es “usuario”
SQL>  GRANT DELETE, INSERT, SELECT, UPDATE ON OPS$USUARIO.PACKAGE_EJEMPLO TO OPS$USUARIO_DESTINO;

Fácil  ¿verdad?

SHLIB_PATH y LD_LIBRARY_PATH y su relación con el error ORA-12547: TNS:lost contact

Al intentar conectar a Oracle versión 8.1.7.0 en un entorno HP-UX me encontré con el siguiente error:

# sqlplus scott/tiger

SQL*Plus: Release 8.1.7.0.0 – Production on Mon May 19 14:47:42 2003
(c) Copyright 2000 Oracle Corporation.  All rights reserved.
/usr/lib/pa20_64/dld.sl: Unable to find library ‘libjox8.sl’.
ERROR:
ORA-12547: TNS:lost contact
Enter user-name:

 ¿ Que puede estar sucediendo ?

Basicamente es que Oracle no puede encontrar las librerias dinámicas compartidas. Por tanto, la solución parece, a priori, ser bastante sencilla. Y de hecho lo es.

Las librerias o bibliotecas compartidas son librerias que cargan los programas cuando se inician. Cuando una biblioteca compartida se instala correctamente, todos los programas que se inician automáticamente usan esta biblioteca compartida. Es más, en Linux estas bibliotecas se pueden actualizar, se pueden anular íncluso funciones específicas de una determinada biblioteca, y todo esto mientras los programas están en ejecución.

Para poder realizar estas tareas, se pueden configurar variables de entorno que nos permitan controlar estos procesos y normalmente se suelen usar para sustituir una biblioteca por una diferente para una ejecución particular, como por ejemplo un entorno ORACLE determinado.

Es aquí donde prestan protagonismo las variables de entorno que hago referencia en esta entrada de blog.

LD _LIBRARY_PATH y SHLIB_PATH es un conjunto de directorios separados por “:” en donde se especifica o fija la ruta donde primero se tiene que buscar para encontrar las librerias necesarias para ejecutar cualquier programa, como por ejemplo sqlplus.

¿ Pero que diferencia hay o porqué se usa una u otra ?

LD_LIBRARY_PATH funciona en muchos sistemas Unix como Sun  y Linux, SHLIB_PATH sólo funciona en HP-UX y su equivalencia en AIX es la variable de entorno LIBPATH con la misma sintaxis, lista separada por “:”.

Lo que quiere decir que para sistemas HP-UX usaremos las dos variables de entorno (SHLIB_PATH y LD_LIBRARY_PATH) para Linux o Sun Solaris sólo LD_LIBRARY_PATH y para AIX LIBPATH.

Ahora, volvamos al error que nos ha dado ORACLE y vamos a solucionarlo ….

Simplemente editaremos el fichero donde tengamos definidas las variables del entorno Oracle (normalmente el .profile del usuario Oracle) y añadiremos las rutas que nos muestra el error Oracle (/usr/lib) a las variables de entorno que toque en función del sistema Unix/Linux en el que estemos trabajando. En mi caso era HP-UX y tuve que configurar el entorno como sigue:

export ORACLE_SID=ORCL
export ORACLE_HOME=/u/oracle
export PATH=$PATH:$ORACLE_HOME/bin:$ORACLE_HOME/lib:$ORACLE_HOME/lib32
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$ORACLE_HOME/lib32:/usr/lib
export SHLIB_PATH=/usr/lib:/psg_pr103/oracle/lib:/psg_pr103/oracle/lib64

Ahora ya podríamos ejecutar sqlplus sin problemas.