martes, 17 de diciembre de 2013

ORA-04068, ORA-04061, ORA-04065, ORA-06508 y ORA-06512

Probé este ejemplo en Oracle 11.2. Creé un paquete con una variable global, variable1, en la sesión roja abajo:
 
SQL> create or replace package package1
  2  is
  3  variable1 number := 1;
  4  procedure procedure1;
  5  end;
  6  /
 
Paquete creado.
 
SQL> create or replace package body package1
  2  is
  3  procedure procedure1
  4  is
  5  begin
  6  variable1 := variable1 + 1;
  7  dbms_output.put_line('Variable1 = '||variable1);
  8  end;
  9  end;
10  /
 
Cuerpo del paquete creado.
 
SQL>
 
Lo ejecuté en otra sesión azul, que se puede ver abajo. Oracle guardó el valor de la variable global entre cada ejecución:
 
SQL> set serveroutput on
SQL> exec package1.procedure1;
Variable1 = 2
 
Procedimiento PL/SQL terminado correctamente.
 
SQL> exec package1.procedure1;
Variable1 = 3
 
Procedimiento PL/SQL terminado correctamente.
 
SQL> exec package1.procedure1;
Variable1 = 4
 
Procedimiento PL/SQL terminado correctamente.
 
SQL>
 
Regresé a la primera sesión, añadí otra variable global, variable2, y recompilé el paquete y su cuerpo:
 
SQL> create or replace package package1
  2  is
  3  variable1 number := 1;
  4  variable2 number;
  5  procedure procedure1;
  6  end;
  7  /
 
Paquete creado.
 
SQL> create or replace package body package1
  2  is
  3  procedure procedure1
  4  is
  5  begin
  6  variable1 := variable1 + 1;
  7  dbms_output.put_line('Variable1 = '||variable1);
  8  end;
  9  end;
10  /
 
Cuerpo del paquete creado.
 
SQL>
 
Luego , cuando volví a la segunda sesión y intenté a ejecutar el paquete, Oracle sabía que no podía creer el valor de la variable global porque una nueva variable global había sido añadida:
 
SQL> set serveroutput on
SQL> exec package1.procedure1;
BEGIN package1.procedure1; END;
 
*
ERROR en linea 1:
ORA-04068: se ha anulado el estado existente de los paquetes
ORA-04061: el estado existente de package "OPS$ORACLE.PACKAGE1" ha sido
invalidado
ORA-04065: package "OPS$ORACLE.PACKAGE1" no se ha ejecutado porque se ha
modificado o borrado
ORA-06508: PL/SQL: no se ha encontrado la unidad de programa llamada :
"OPS$ORACLE.PACKAGE1"
ORA-06512: en linea 1
 
SQL>
 
Entonces, cuando lo ejecuté por segunda vez, Oracle empezó a contar del principio otra vez:
 
SQL> set serveroutput on
SQL> exec package1.procedure1;
Variable1 = 2
 
Procedimiento PL/SQL terminado correctamente.
 
SQL> exec package1.procedure1;
Variable1 = 3
 
Procedimiento PL/SQL terminado correctamente.
 
SQL> exec package1.procedure1;
Variable1 = 4
 
Procedimiento PL/SQL terminado correctamente.
 
SQL>
 
No es buena idea emplear variables globales si quieres evitar el error ORA-04068.

miércoles, 30 de octubre de 2013

ORA-02396

Se puede poner un límite en el tiempo inactivo y continuo de una sesión de la manera siguiente. Antes de empezar, es necesario cambiar resource_limit a true. Si no se hace esto, los límites no se hacen cumplir: 

SQL> alter system set resource_limit = true
  2  /
 
Sistema modificado.
 
SQL> 

Luego debes crear un perfil con un límite en el tiempo de inactividad que se llama idle_time. El valor que empleas es en minutos: 

SQL> create profile idle_time_profile
  2  limit idle_time 1
  3  /
 
Perfil creado.
 
SQL> 

Entonces necesitas un usuario para probar el perfil. Se puede crearlo así: 

SQL> create user idle_time_user
  2  identified by idle_time_user
  3  profile idle_time_profile
  4  /
 
Usuario creado.
 
SQL> grant create session to idle_time_user
  2  /
 
Concesion terminada correctamente.
 
SQL>
 
Para probar el perfil, el usuario nuevo se conecta a la base de datos. Lo hago en rojo porque desde ahora tendré que trabajar en dos sesiónes distintas: 

SQL> conn idle_time_user/idle_time_user
Conectado.
SQL>
 
Luego el usuario sys se conecta a la base de datos para monitorizar la sesión del usuario idle_time_user. Por eso, lo hago en azul en una sesión distinta. El usuario sys sabe que la sesión de idle_time_user no hace nada porque tiene un status de INACTIVE:
 
SQL> conn / as sysdba
Conectado.
SQL> select status from v$session
  2  where username = 'IDLE_TIME_USER'
  3  /
 
STATUS
--------
INACTIVE
 
SQL> exec dbms_lock.sleep(60);
 
Procedimiento PL/SQL terminado correctamente.
 
SQL>

Después de un minuto de inactividad, Oracle podría cerrar la sesión de idle_time_user pero no suele hacerlo inmediatamente:
 
SQL> select status from v$session
  2  where username = 'IDLE_TIME_USER'
  3  /
 
STATUS
--------
INACTIVE
 
SQL> exec dbms_lock.sleep(60);
 
Procedimiento PL/SQL terminado correctamente.
 
SQL>
 
Tras dos minutos, Oracle todavía no se da cuenta de la inactividad de idle_time_user

SQL> select status from v$session
  2  where username = 'IDLE_TIME_USER'
  3  /
 
STATUS
--------
INACTIVE
 
SQL> exec dbms_lock.sleep(60);
 
 
Procedimiento PL/SQL terminado correctamente.
 
SQL>
 
Pero tras 3 minutos, Oracle se da cuenta y cambia el status de idle_time_user a SNIPED: 

SQL> select status from v$session
  2  where username = 'IDLE_TIME_USER'
  3  /
 
STATUS
--------
SNIPED
 
SQL>
 
Y cuando idle_time_user vuelve a su sesión, Oracle le da un error ORA-02396 y le dice de conectarse otra vez a la base de datos antes de continuar: 

SQL> select sysdate from dual;
select sysdate from dual
*
ERROR en linea 1:
ORA-02396: ha excedido el tiempo maximo de
inactividad, vuelva a conectarse
 
SQL>

viernes, 18 de octubre de 2013

Como se Puede Emplear un Usuario sin Conocer su Contraseña en Oracle 11


En Oracle versión 11, llegaron contraseñas sensibles al uso de mayúsculas y minúsculas:

SQL> conn / as sysdba
Conectado.
SQL> grant create session to andrew
  2  identified by secret_password
  3  /
 
Concesion terminada correctamente.
 
SQL> conn andrew/secret_password
Conectado.
SQL> conn andrew/SECRET_PASSWORD
ERROR:
ORA-01017: nombre de usuario/contraseña no validos; conexion
denegada
 
Advertencia: !Ya no esta conectado a ORACLE!
SQL> 

Si quieres emplear un usuario en una base de datos sin conocer su contraseña normal, debes mirar su contraseña encriptada de la manera siguiente: 

SQL> conn / as sysdba
Conectado.
SQL> select spare4 from sys.user$
  2  where name = 'ANDREW'
  3  /
 
SPARE4
-----------------------------------------------------------------
S:CD4E8CCD97532CE9EBB35A8D5B910CF9F554711216EFDDECECD32DC4943A
 
SQL> 

Luego se puede cambiar la contraseña normal y emplear el usuario. 

SQL> alter user andrew identified by reid
  2  /
 
Usuario modificado.
 
SQL> conn andrew/reid
Conectado.
SQL> select sysdate from dual
  2  /
 
SYSDATE
---------
18-OCT-13
 
SQL> 

Después de terminar la sesión, debes restaurar la contraseña encriptada: 

SQL> alter user andrew identified by values
  2  'S:CD4E8CCD97532CE9EBB35A8D5B910CF9F554711216EFDDECECD32DC4943A'
  3  /
 
Usuario modificado.
 
SQL> 

Entonces el dueño usual del usuario podrá utilizarlo con la contraseña normal que será todavía sensible al uso de mayúsculas y minúsculas:

SQL> conn andrew/secret_password
Conectado.
SQL> conn andrew/SECRET_PASSWORD
ERROR:
ORA-01017: nombre de usuario/contraseña no validos; conexion
denegada
 
Advertencia: !Ya no esta conectado a ORACLE!
SQL>

domingo, 13 de octubre de 2013

La Columna Table_Lock y el Error ORA-00069

Probé este ejemplo en Oracle 11.2.0.2.7. Un colega quería saber cual era el  propósito de la columna table_lock en la vista dba_tables. Yo no sabía y por lo tanto decidí investigar. Para empezar, miré los valores en la columna table_lock en una base de datos de prueba: 

SQL> select table_lock, count(*)
  2  from dba_tables
  3  group by table_lock
  4  / 

TABLE_LOCK   COUNT(*)
---------- ----------
ENABLED          2760

SQL>

Esto me hizo pensar que el valor predeterminado fuese ENABLED. Para confirmar esta teoría, creé una tabla de prueba: 

SQL> create table andrew (col1 number)
  2  /
 
Tabla creada.
 
SQL>

Como se esperaba, la columna table_lock tenía un valor de ENABLED para esta tabla nueva: 

SQL> l
  1  select table_lock from user_tables
  2* where table_name = 'ANDREW'
SQL> /
 
TABLE_LOCK
----------
ENABLED

SQL>

Se puede cambiar el valor de table_lock de la manera siguiente:

SQL> alter table andrew disable table lock
  2  /
 
Tabla modificada.

SQL>

Tras hacer esto, el valor de table_lock cambia a DISABLED: 

SQL> select table_lock from dba_tables
  2  where table_name = 'ANDREW'
  3  /
 
TABLE_LOCK
----------
DISABLED

SQL>

Luego no es posible tener un bloqueo en la tabla:

SQL> l
  1* lock table andrew in share mode
SQL> /
lock table andrew in share mode
           *
ERROR en linea 1:
ORA-00069: no se puede obtener el bloqueo -- bloqueos
de tabla desactivados para ANDREW

SQL>

Tampoco es posible cambiar la tabla de cualquier manera. Por ejemplo, no se puede renombrarla: 

SQL> rename andrew to fred
  2  /
rename andrew to fred
*
ERROR en linea 1:
ORA-00069: no se puede obtener el bloqueo -- bloqueos
de tabla desactivados para ANDREW
 
SQL>

Si quieres hacer estas cosas, es necesario cambiar table_lock a ENABLED: 

SQL> alter table andrew enable table lock
  2  /
 
Tabla modificada.
 
SQL>

Entonces se puede cambiar la tabla otra vez:

SQL> rename andrew to fred
  2  /
 
Nombre de tabla cambiado.
 
SQL>

Así se puede ver que cuando table_lock sea ENABLED, Oracle te permite tener bloqueos en la tabla y cambiar su definición.

En inglés / in English

jueves, 10 de octubre de 2013

Característica Nueva de Flashback en Oracle 11.2

Hallé esta información aquí. En Oracle 10, era necesario cerrar una base de datos y abrirla con STARTUP MOUNT antes de cambiarla a FLASHBACK ON. En Oracle 11.2, se puede hacerlo en una base de datos abierta. Aquí está un ejemplo, que probé en Oracle 11.2.0.2.7: 

SQL> select open_mode, flashback_on from v$database
  2  /
 
OPEN_MODE            FLASHBACK_ON
-------------------- ------------------
READ WRITE           NO
 
SQL> alter database flashback on
  2  /
 
Base de datos modificada.
 
SQL> select flashback_on from v$database
  2  /
 
FLASHBACK_ON
------------------
YES
 
SQL> alter database flashback off
  2  /
 
Base de datos modificada.
 
SQL> select flashback_on from v$database
  2  /
 
FLASHBACK_ON
------------------
NO
 
SQL>

viernes, 27 de septiembre de 2013

Ejemplos Sencillos de PL/SQL: El Bucle WHILE

Se puede repetir un comando PL/SQL en un bucle WHILE. Aquí está un ejemplo que probé en Oracle 11.2.0.2.7:

SQL> l
  1  DECLARE
  2   M NUMBER := 1;
  3  BEGIN
  4   WHILE M < 6 LOOP
  5    DBMS_OUTPUT.PUT_LINE('M = '||M);
  6    M := M + 1;
  7   END LOOP;
  8* END;
SQL> /
M = 1
M = 2
M = 3
M = 4
M = 5
 
Procedimiento PL/SQL terminado correctamente.
 
SQL>

jueves, 26 de septiembre de 2013

ORA-01502

En este ejemplo, miro una razón por tener el error ORA-01502. Lo probé en Oracle 11.2.0.2.7. Creé una tabla en el tablespace SYSTEM por error: 

SQL> create table andrews_table
  2  tablespace system
  3  as select * from dba_tables
  4  /
 
Tabla creada.
 
SQL>

Creé un índice en la tabla y verifiqué que era válido:

SQL> create index andrews_index
  2  on andrews_table(table_name)
  3  /
 
Indice creado.
 
SQL> select status from user_indexes
  2  where index_name = 'ANDREWS_INDEX'
  3  /
 
STATUS
--------
VALID
 
SQL>

Le dije a Oracle de informarme sobre índices inutilizables:

SQL> alter session
  2  set skip_unusable_indexes = false
  3  /
 
Sesion modificada.
 
SQL>

Ejecuté una consulta para emplear el índice sin problemas: 

SQL> select count(*) from andrews_table
  2  where table_name = 'BLAH'
  3  /
 
  COUNT(*)
----------
         0
 
SQL>

Luego me di cuenta que la tabla estaba en el tablespace SYSTEM y la puse en el tablespace USERS, lo que invalidó el índice:

SQL> alter table andrews_table
  2  move tablespace users
  3  /
 
Tabla modificada.

SQL> select status from user_indexes
  2  where index_name = 'ANDREWS_INDEX'
  3  /
 
STATUS
--------
UNUSABLE
 
SQL>

Así cuando intenté a emplear el índice, tenía un error: 

SQL> select count(*) from andrews_table
  2  where table_name = 'BLAH'
  3  /
select count(*) from andrews_table
*
ERROR en linea 1:
ORA-01502: el indice 'OPS$ORACLE.ANDREWS_INDEX' o una
particion del mismo estan en estado inutilizable
 
SQL>

Después de reconstruir el índice, la consulta funcionó otra vez: 

SQL> alter index andrews_index rebuild
  2  /
 
Indice modificado.
 
SQL> select status from user_indexes
  2  where index_name = 'ANDREWS_INDEX'
  3  /
 
STATUS
--------
VALID
 
SQL> select count(*) from andrews_table
  2  where table_name = 'BLAH'
  3  /
 
  COUNT(*)
----------
         0