sql >> Base de Datos >  >> RDS >> Oracle

Abordar el error de la columna desplegable en Oracle 18c y 19c

El camino del progreso puede ser difícil a veces. Las versiones 18 y 19 de Oracle no son una excepción. Hasta la versión 18.x, Oracle no tuvo problemas para marcar columnas como no utilizadas y, finalmente, descartarlas. Dadas algunas circunstancias interesantes, las dos últimas versiones de Oracle pueden arrojar errores ORA-00600 cuando las columnas se configuran como no utilizadas y luego se eliminan. Las condiciones que provocan este error pueden no ser comunes, pero hay una gran cantidad de instalaciones de Oracle en todo el mundo y es muy probable que alguien, en algún lugar, encuentre este error.

El cuento comienza con dos mesas y un disparador:

create table trg_tst1 (c0 varchar2(30), c1 varchar2(30), c2 varchar2(30), c3 varchar2(30), c4 varchar2(30));
create table trg_tst2 (c_log varchar2(30));

create or replace trigger trg_tst1_cpy_val
after insert or update on trg_tst1
for each row
begin
        IF :new.c3 is not null then
                insert into trg_tst2 values (:new.c3);
        end if;
end;
/

Los datos se insertan en la tabla TRG_TST1 y, siempre que se cumplan las condiciones, los datos se replican en la tabla TRG_TST2. Se insertan dos filas en TRG_TST1 para que solo una de las filas insertadas se copie en TRG_TST2. Después de que se consulte cada tabla de inserción TRG_TST2 y se muestren los resultados:

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c3) values ('Inserting c3 - should log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 

Ahora comienza la 'diversión':dos columnas en TST_TRG1 están marcadas como 'sin usar' y luego se eliminan, y la tabla TST_TRG2 se trunca. Se vuelven a ejecutar las inserciones en TST_TRG1, pero esta vez se producen los temidos errores ORA-00600. Para ver por qué ocurren estos errores, el estado del activador se informa desde USER_OBJECTS:

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > --  ===================================
SMERBLE @ gwunkus > --  Drop some columns in two steps then
SMERBLE @ gwunkus > --  truncate trg_tst2 and repeat the test
SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > --  ORA-00600 errors are raised
SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > --  The trigger is not invalidated and
SMERBLE @ gwunkus > --  thus is not recompiled.
SMERBLE @ gwunkus > --  ===================================
SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > alter table trg_tst1 set unused (c1, c2);

Table altered.

SMERBLE @ gwunkus > alter table trg_tst1 drop unused columns;

Table altered.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > select object_name, status from user_objects where object_name in (select trigger_name from user_triggers);


OBJECT_NAME                         STATUS
----------------------------------- -------
TRG_TST1_CPY_VAL                    VALID

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > truncate table trg_tst2;

Table truncated.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c3) values ('Inserting c3 - should log');

insert into trg_tst1(c3) values ('Inserting c3 - should log')
            *
ERROR at line 1:
ORA-00600: internal error code, arguments: [insChkBuffering_1], [4], [4], [], [], [], [], [], [], [], [], []


SMERBLE @ gwunkus > select * from trg_tst2;

no rows selected

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log');

insert into trg_tst1(c4) values ('Inserting c4 - should not log')
            *
ERROR at line 1:
ORA-00600: internal error code, arguments: [insChkBuffering_1], [4], [4], [], [], [], [], [], [], [], [], []


SMERBLE @ gwunkus > select * from trg_tst2;

no rows selected

SMERBLE @ gwunkus > 

El problema es que, en Oracle 18c y 19c, la acción de "eliminar columnas no utilizadas" NO invalida el activador, dejándolo en un estado "VÁLIDO" y configurando las próximas transacciones para que fallen. Dado que el activador no se volvió a compilar en la siguiente llamada, el entorno de compilación original todavía está en vigor, un entorno que incluye las columnas ahora descartadas. Oracle no puede encontrar las columnas C1 y C2, pero el disparador aún espera que existan, por lo tanto, el error ORA-00600. My Oracle Support informa esto como un error:

Bug 30404639 : TRIGGER DOES NOT WORK CORRECTLY AFTER ALTER TABLE DROP UNUSED COLUMN.

e informa que la causa es, de hecho, la falla al invalidar el disparador con la caída de columna diferida.

Entonces, ¿cómo solucionar este problema? Una forma es compilar explícitamente el activador después de eliminar las columnas no utilizadas:

SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > -- Compile the trigger after column drops
SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > alter trigger trg_tst1_cpy_val compile;

Trigger altered.

SMERBLE @ gwunkus > 

Ahora que el disparador usa el entorno actual y la configuración de la tabla, las inserciones funcionan correctamente y el disparador se activa como se esperaba:

SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > -- Attempt inserts again
SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > insert into trg_tst1(c3) values ('Inserting c3 - should log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 

Existe otra forma de solucionar este problema; No marque las columnas como no utilizadas y simplemente suéltelas de la tabla. Descartar las tablas originales, volver a crearlas y ejecutar este ejemplo con una caída de columna directa no muestra ningún signo de ORA-00600, y el estado de activación después de la caída de la columna demuestra que no se producirán tales errores:

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > drop table trg_tst1 purge;

Table dropped.

SMERBLE @ gwunkus > drop table trg_tst2 purge;

Table dropped.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > --  ===================================
SMERBLE @ gwunkus > --  Re-run the example without marking
SMERBLE @ gwunkus > --  columns as 'unused'
SMERBLE @ gwunkus > --  ===================================
SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > create table trg_tst1 (c0 varchar2(30), c1 varchar2(30), c2 varchar2(30), c3 varchar2(30), c4 varchar2(30));

Table created.

SMERBLE @ gwunkus > create table trg_tst2 (c_log varchar2(30));

Table created.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > create or replace trigger trg_tst1_cpy_val
  2  after insert or update on trg_tst1
  3  for each row
  4  begin
  5  	     IF :new.c3 is not null then
  6  		     insert into trg_tst2 values (:new.c3);
  7  	     end if;
  8  end;
  9  /

Trigger created.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c3) values ('Inserting c3 - should log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > --  ===================================
SMERBLE @ gwunkus > --  Drop some columns,
SMERBLE @ gwunkus > --  truncate trg_tst2 and repeat the test
SMERBLE @ gwunkus > --
SMERBLE @ gwunkus > --  No ORA-00600 errors are raised as
SMERBLE @ gwunkus > --  the trigger is invalidated by the
SMERBLE @ gwunkus > --  DDL.  Oracle then recompiles the
SMERBLE @ gwunkus > --  invalid trigger.
SMERBLE @ gwunkus > --  ===================================
SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > alter table trg_tst1 drop (c1,c2);

Table altered.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > select object_name, status from user_objects where object_name in (select trigger_name from user_triggers);

OBJECT_NAME                         STATUS
----------------------------------- -------
TRG_TST1_CPY_VAL                    INVALID

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > truncate table trg_tst2;

Table truncated.

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c3) values ('Inserting c3 - should log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 
SMERBLE @ gwunkus > insert into trg_tst1(c4) values ('Inserting c4 - should not log');

1 row created.

SMERBLE @ gwunkus > select * from trg_tst2;

C_LOG
------------------------------
Inserting c3 - should log

SMERBLE @ gwunkus > 

Las versiones de Oracle anteriores a la 18c se comportan como se esperaba, con la caída de la columna diferida configurando correctamente el estado de activación en 'INVÁLIDO':

SMARBLE @ gwankus > select banner from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
CORE	12.1.0.2.0	Production
TNS for Linux: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production

SMARBLE @ gwankus >
SMARBLE @ gwankus > alter table trg_tst1 set unused (c1, c2);

Table altered.

SMARBLE @ gwankus > alter table trg_tst1 drop unused columns;

Table altered.

SMARBLE @ gwankus >
SMARBLE @ gwankus > select object_name, status from user_objects where object_name in (select trigger_name from user_triggers);

OBJECT_NAME			    STATUS
----------------------------------- -------
TRG_TST1_CPY_VAL		    INVALID

SMARBLE @ gwankus >

La forma en que se colocan las columnas en versiones anteriores a la 18c no hace ninguna diferencia, ya que cualquier activador en la tabla afectada se invalidará. La próxima llamada a cualquier disparador en esa tabla dará como resultado una recompilación "automática", configurando el entorno de ejecución correctamente (lo que significa que las columnas que faltan en la tabla afectada no permanecerán en el contexto de ejecución).

No es probable que una base de datos de producción sufra caídas de columnas sin realizar primero dichos cambios en una base de datos DEV o TST. Desafortunadamente, probar las inserciones después de eliminar las columnas puede no ser una prueba que se ejecute después de realizar dichos cambios y antes de que el código se promueva a PRD. Tener a más de una persona probando los efectos secundarios de dejar caer columnas parecería una excelente idea, ya que, como lo atestigua el viejo adagio, "Dos cabezas piensan mejor que una". de posible falla puede ser presentado y ejecutado. El tiempo adicional necesario para probar un cambio más a fondo significa una probabilidad menor de errores imprevistos que afecten seriamente o detengan la producción.

###

Ver artículos de David Fitzjarrell