Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   Continuar Procedure a pesar del salto de excepción (https://www.clubdelphi.com/foros/showthread.php?t=56961)

santiaguinillo 02-06-2008 12:28:27

Continuar Procedure a pesar del salto de excepción
 
Hola a tod@s,

tengo un problema con un procedure.
BBDD: firebird 1.5

El procedure realiza una serie de inserts según unas condiciones. En el último insert de este procedure y que inserta en una determinada tabla X, salta una excepción. Pusimos una excepción en un trigger before insert que si no se cumple otra condición no se realice el insert en la tabla X i salte esta excepción para capturarla y mostrarla por pantalla en la aplicación delphi.

El problema viene que al saltar la excepción en esta última tabla X, el resto de inserts anteriores no se guardan. Hace rollback cuando salta la excepción.

Quisiera saber si existe la posibilidad de que aunque salte la excepción del procedure firebird, continúe con el procedure y haga finalmente commit del mismo.

Un saludo y gracias por adelantado.

duilioisola 02-06-2008 13:04:23

A pesar de que lo mejor sería verificar si se va a dar la excepción y actuar en consecuencia, existe una posibilidad:

WHEN XXX DO

Código SQL [-]
create procedure insertar_aglo (VALOR varchar(3))
as
begin
  for select familia from art_familias
    where(empresa=1) into :familia do
    begin
      insert into propaga(familia)
        values(:familia) ;
        when SQLCODE -803 do exit ;
    end 
end

Código SQL [-]
create procedure trunca100 (CADENA1 varchar(200))
returns (CADENA2 varchar(100))
as
begin
  /* Este procedimiento trunca la cadena a 100 caracteres */
  cadena2 ='INICIALIZA';
  cadena2 = cadena1;
  when any do suspend;
end

santiaguinillo 02-06-2008 14:39:07

Continuar Procedure a pesar del salto de excepción
 
Hola duilioisola,

he probado de poner el código que me comentas. En concreto he probado en mi procedure:
  • when SQLCODE -803 do exit ;
  • when EXCEPTION SUPERA_IMPORTE DO exit;
(SUPERA_IMPORTE es el nombre de la excepción que salta)

Ambas opciones han salido con rollback del procedure
así que todavía no me queda resuelto el problema.

No sé como hacer para que aun saltando una excepción en un insert, el resto de inserts ejecutados anteriormente hagan commit.

RolphyReyes 02-06-2008 15:02:10

Saludos.

Puedes ir haciendo commit por cada inserción es inefectivo pero es una solución.

También puedes poner el código que tienes y cuando salte la excepción "forza" el commit.

santiaguinillo 02-06-2008 15:12:52

Hacer commit
 
Hola RolphyReyes,

el problema es que al poner commit en el procedure me da error al compilar. Es decir, no puedo poner commit dentro del procedure.


Cita:

Empezado por RolphyReyes (Mensaje 290702)
Saludos.

Puedes ir haciendo commit por cada inserción es inefectivo pero es una solución.

También puedes poner el código que tienes y cuando salte la excepción "forza" el commit.


duilioisola 02-06-2008 15:15:06

Prueba con
* when SQLCODE -803 do SUSPEND ;
* when EXCEPTION SUPERA_IMPORTE DO SUSPEND ;

Cada vez que un procedimiento encuentra un suspend, devuelve los valores que tenga en el RETURNS() y espera a que le pidan el siguiente.

Aunque no devuelvas nada, el SUSPEND hará que devuelva el control a tu programa y puedas hacer un commit o rollback, según te convenga.

santiaguinillo 02-06-2008 15:52:40

Pruebas con do suspend KO
 
Hola duilioisola,

¿pero el commit o rollback quieres decir que lo ponga en el código delphi? Porque he hecho lo siguiente y sigue sin hacerme commit (en el procedure he puesto when EXCEPTION SUPERA_IMPORTE DO SUSPEND):

Código Delphi [-]
      sentencia := 'EXECUTE PROCEDURE PROCESA_VISITA (variables)';
      try
        Q1.Close;
        Q1.SQL.Clear;
        Q1.SQL.Add(sentencia);
        Q1.Prepare;
        Q1.ExecQuery;   //AQUI DA LA EXCEPCIÓN
        Q1.Transaction.CommitRetaining;
        Q1.FreeHandle;
      except
        Q1.Transaction.CommitRetaining; //ESTO LO HE PUESTO AHORA Y NO HACE COMMIT
        Q1.FreeHandle;
        raise;
      end;



Cita:

Empezado por duilioisola (Mensaje 290706)
Prueba con
* when SQLCODE -803 do SUSPEND ;
* when EXCEPTION SUPERA_IMPORTE DO SUSPEND ;

Cada vez que un procedimiento encuentra un suspend, devuelve los valores que tenga en el RETURNS() y espera a que le pidan el siguiente.

Aunque no devuelvas nada, el SUSPEND hará que devuelva el control a tu programa y puedas hacer un commit o rollback, según te convenga.


duilioisola 02-06-2008 17:49:59

Supongo que si no sigue es porque la excepción que salta no es la que esperas en el WHEN EXCEPCION DO...
Prueba a poner WHEN ANY DO...

O prueba a ver cuál es la excepción que llega con un ShowMessage.
Código Delphi [-]
sentencia := 'EXECUTE PROCEDURE PROCESA_VISITA (variables)';
      try
        Q1.Close;
        Q1.SQL.Clear;
        Q1.SQL.Add(sentencia);
        Q1.Prepare;
        Q1.ExecQuery;   //AQUI DA LA EXCEPCIÓN
        Q1.Transaction.CommitRetaining;
        Q1.FreeHandle;
      except
         on e:exception do 
         begin
            Showmessage(e.message); //Esto te mostrará el texto de la excepcion
           Q1.Transaction.Rollback;
           Q1.FreeHandle;
           raise;
         end;
      end;

Por más que le digas que haga un commit, si ha saltado una excepción no grabará los datos.
Si quieres envíanos el código de tu procedimiento, para que le hechemos una mirada.
Pero como dije anteriormente, quizás sea mejor filtrar los casos para los que se dá la excepción.

lbuelvas 02-06-2008 19:10:05

Un procedimento almacenado funciona como una unidad en donde todas las operaciones son grabadas (commit) si y solamente si no se presenta alguna violacion de integridad (llaves foraneas, primarias, campos nulos, etc). Al presentarse alguna violacion los datos de la base de datos quedan en el estado en que se encontraban justo antes de lanzar el procedimiento almacenado.

Realmente y en lo personal evito que queden datos pendientes por actualizar cuando se lanza un procedimiento almacenado, sin embargo, puedes colocar una sentencia de codigo y que te escriba en otra tabla, no que te lance una expecion porque las excepciones provocan que pierdas las actualzaciones alcanzadas hasta ese punto.

Código SQL [-]
          when any do
          begin
            INSERT INTO TS_TRAZO
            (DESCRIPCION)
            VALUES
            (texto con la observacion);
          END
        end

La tabla TS_TRAZO la puedes consultar despues para saber que paso.

santiaguinillo 03-06-2008 12:50:37

Medio solucionado
 
Hola a todos,

pues resulta que no cogía la excepción correcta. Con el WHEN ANY DO SUSPEND; me ha funcionado correctamente y ha hecho commit de los inserts anteriores.

El "problema" es que ahora se traga la excepción y no muestra el mensaje de error ya que el código delphi no detecta excepción.

Una pregunta, ¿el insert devuelve alguna variable si se ha insertado correctamente (en el procedure)?
Si fuese así, podría devolver en el procedure una variable que diga si se ha insertado bien el último insert que es el único que provoca la excepción.

RolphyReyes 03-06-2008 13:31:06

Saludos.

En Firebird 2.xx tienes la clausula RETURNING pero tienes la v1.5.

No se cual es tu caso pero analiza si puedes migrar a la nueva versión que tiene más mejoras y estabilidad.

Hasta luego.

duilioisola 03-06-2008 13:49:26

Si quitas la sentencia WHEN ANY DO SUSPEND de tu procedimiento, la parte donde tratas las excepciones te dirá cuál es la excepción que se ha generado.

Código Delphi [-]
   except
      on e:exception do 
      begin
         Showmessage(e.message); //Esto te mostrará el texto de la excepcion
         Q1.Transaction.Rollback;
         Q1.FreeHandle;
         raise;
      end;
   end;

De todos modos, la filosofía de las excepciones es diferente a lo que parece que haces tu:
- Las excepciones hay que tratar de no utilizarlas.
- Consumen muchos recursos.
- Si salta una excepción hay que volver atrás todo lo que se ha hecho hasta entonces.
- Normalmente se puede evitar la excepción.

Por ejemplo:
Si tienes un procedimiento que divide dos valores, tienes dos opciones:
  • Verificas con un if si el divisor es distinto de 0 antes de realizar la división y si es 0 muestras un mensaje de error.
  • Realizas la división. Si salta la excepción la capturas y muestras un mensaje de error.

Supongo que lo mejor en tu caso (creo que estás insertando registros) es verificar si existe y sobrepasa un valor dado.

Código SQL [-]
   /* Sumo importe */
   select sum(importe) from tabla where condiciones
   into :importe;

   /* Verifico que no me paso del valor maximo */
   if (importe<=VALOR_MAXIMO) then
   begin
      /* Verifico que no exista para poder insertarlo */
      if (not exists(select importe from tabla where condiciones)) then
          insert into tabla (campos) values (valores);  
 
      else
         /* Si existe lo meto en tabla de errores que mostraré al usuario */
         insert into tabla_errores (detalles) values ('YA EXISTE EL VALOR A INSERTAR');
   end
   else
        /* Si sobrepasa el importe maximo lo meto en tabla de errores que mostraré al usuario */
       insert into tabla_errores (detalles) values ('SE SUPERA EL IMPORTE');

santiaguinillo 03-06-2008 14:37:39

Resuelto
 
Gracias a todos por vuestras respuestas.

Doy el problema por resuelto. Finalmente con el when any do suspend; ha sido suficiente y aunque ya no muestra el mensaje de la excepción no es realmente grave.

Seria interesante haber tenido lo que comentaba RolphyReyes sobre Returning y de esta forma podría haber devuelto alguna variable para informar de que no se ha introducido el último registro.

Es para esto que teníamos las excepciones, para informar de ello a la aplicación delphi. Lo que no queríamos es que nos hiciera rollback de las anteriores operaciones en la bbdd del procedimiento.

En mi caso no podría hacerlo de otra forma, ni de la forma que comenta duilioisola en el código sql ya que el último insert del procedure se efectua o no dependiendo de unas condiciones que hay en el trigger before insert de la tabla que intenta insertar. Por lo tanto, o bien ponemos el código del trigger en el mismo procedure o bien necesitaríamos el returning que ya tiene el firebird 2.0

De nuevo, gracias a todos por vuestro aporte.
Santi.

rastafarey 11-06-2008 14:33:36

Resp
 
Dentro de un disparador no vas a poder decir que no inserte o que no haga lo que uba ahacer en su tabla. Por exactamente la accion de insertar es la que dispara el disparador. Lo que si puedes hacer desde el disparador verifcar al condicion que te hace saltar la exception antes de manadar a insertar.


La franja horaria es GMT +2. Ahora son las 05:37:04.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi