PDA

Ver la Versión Completa : Referencias circulares aparentes con SPs


mlara
06-07-2003, 00:04:21
Hola de nuevo. Ahora entiendo bien lo del código BLR y el mensaje de error de IBConsole. Había comentado que tenía una solución, la puse en práctica y todo funcionó muy bien, pero siempre sucede algo nuevo, como lo que les voy a contar:

Supongamos que tengo el SP1 y el SP2.

1. El SP1 evalúa uno de sus parámetros (un código) y devuelve el valor calculado.

2. El SP2 evalúa una expresión matemática, pero en la cual algunos de los términos son códigos.

Ahora, veamos:

Ejecuto el SP1 pasándole como parámetro el código X, y él mismo determina que el código X que debe evaluar es de tipo fórmula, por lo cual llama a SP2.

El SP2 recorre los términos de la expresión correspondiente al código X. Luego, cuando encuentra un código Z en dicha expresión, llama al SP1 para que éste lo evalúe.

El SP1 evalúa el código Z, y es probable que éste sea nuevamente un código de tipo fórmula...

...

Es claro que esta es una referencia circular pero siempre y cuando se repita en alguna llamada uno de los códigos anteriores.

En mi caso, esto no sucede ya que una aplicación cliente verifica que no existan códigos repetidos, por lo cual nunca habrá un desbordamiento de pila o numérico.

El problema que tengo, es que, aunque esto no suceda siempre, sobre todo con expresiones cortas, InterBase no me deja ejecutar los SPs.

El error es el siguiente:

"arithmetic exception, numeric overflow, or string truncation on line 1 : SELECT out_result FROM "PayFormValue"('X', ...)

Pensé que de nuevo podría ser IBConsole, y lo probé desde Delphi 7. Generó de nuevo el mismo error.

Intenté usar un procedimiento intermediario, pero tampoco funcionó.

De nuevo, debo decir que estos procedimientos los estoy migrando de MS SQL Server a InterBase, y en SQL Server funcionaban bien.

Quisiera saber si alguien ha tenido algún problema similar y cómo lo ha podido resolver.

Muchas gracias por su colaboración.

mlara
06-07-2003, 09:23:21
Ha sido un poco difícil llegar a esto, pero tuve que quitar todo el código posible para saber dónde estaba el error. Si alguien está dispuesto, esta es una invitación para que creen la siguiente table y el siguiente procedimiento almacenado:


CREATE TABLE "Tempo"(
"Auto" INTEGER NOT NULL PRIMARY KEY,
"Value" VARCHAR(6)
);

CREATE GENERATOR "Tempo_Auto";
SET GENERATOR "Tempo_Auto" TO 0;

CREATE TRIGGER "TAutoInc_Tempo" FOR "Tempo"
BEFORE INSERT
POSITION 0
AS BEGIN
NEW."Auto" = GEN_ID("Tempo_Auto", 1);
END;



CREATE PROCEDURE "EvalExpr" (
in_value VARCHAR(6))
RETURNS(
out_result DECIMAL(18, 4))

AS

DECLARE VARIABLE expr VARCHAR(256);
DECLARE VARIABLE segexpr VARCHAR(32);
DECLARE VARIABLE autoinc INTEGER;

BEGIN

FOR
SELECT "Auto"
FROM "Tempo"
WHERE "Value" = :in_value
ORDER BY "Auto"
INTO :autoinc
DO BEGIN
segexpr = CAST(autoinc AS VARCHAR(32));
expr = STRCAT(expr, segexpr);
END

out_result = 1;

SUSPEND;
END


Ahora, hay que hacer algunos registros en la tabla "Tempo", digamos unos 30 o más (yo hice cerca de 200 registros).

Luego, los eliminamos:

DELETE FROM "Tempo";

Volvemos a registrar unos cuantos que tengan el mismo valor, por ejemplo '000000'. Así la tabla podría verse más o menos así:


Auto Value
---- ----
35 000000
36 000000
37 000000
38 000000
39 000000
40 000000
41 000000
42 000000
43 000000
44 000000
45 000000
46 000000
...


Ahora, se ejecuta el SP:

select out_result from "EvalExpr"('000000');

y voy a hablar de la línea:

expr = STRCAT(expr, segexpr);

Cuando hay menos de 27 registros en la tabla con el valor '000000' no hay problema. Si hay 27 registros o más...

Se causa el error del que pensaba era causado por una referencia circular.

Si se cambia la línea mencionada, digamos por:

expr = STRCAT(expr, '**');

El problema desaparece, pero debe ser claro que esta línea es sumamente importante, casi el objeto del procedimiento almacenado, ya que en ella se construye una expresión que luego debe ser evaluada (en el ejemplo no se ve esto con claridad).

¿ Lo que estoy haciendo es muy importante... y me encontré con esto!

¿ ES UN BUG DE INTERBASE ?

He hecho muchas pruebas, de muchas formas, y la verdad lo único que me falta es probar con otra función para concatenar. La función STRCAT que estoy usando es la proporcionada junto a InterBase, librería udflib.dll.

Hubiese probado otra función pero no la he encontrado. Si alguien me pudiera ayudar agradecería mucho su colaboración.

por si acaso, la función externa está declarada tal y como se especifica en la documentación, así:

DECLARE EXTERNAL FUNCTION STRCAT
VARCHAR(255) CHARACTER SET NONE, VARCHAR(255) CHARACTER SET NONE
RETURNS CSTRING(80) CHARACTER SET NONE FREE_IT
ENTRY_POINT 'fn_strcat' MODULE_NAME 'udflib';

kinobi
06-07-2003, 11:00:31
Hola,

Posteado originalmente por mlara
He hecho muchas pruebas, de muchas formas, y la verdad lo único que me falta es probar con otra función para concatenar.

¿Has probado con el operador de concatenación, || (dos caracteres ASCII 124 seguidos)?.

Posteado originalmente por mlara
La función STRCAT que estoy usando es la proporcionada junto a InterBase, librería udflib.dll.
Si el problema está en una UDF, se podría descartar casi con toda seguirdad un bug del servidor.

Nota para investigar: algunos problemas del uso de tipos cadena (CHAR y VARCHAR) provienen del juego de caracteres utilizado (en la base de datos y en las columnas implicadas).

Saludos.

mlara
06-07-2003, 15:14:07
Claro, no sé si es porque hace algún tiempo no trabaja en InterBase, o porque de trabajar en varios lenguajes se le confunden las cosas a uno en la cabeza, o porque cuando uno busca la sección operadores que toda referencia del lenguaje tiene sencillamente en la de InterBase no la encuentra. De cualquier forma todo esto suena como a disculpa... entonces prefiero pensar que tal vez todo se debió a la falta de sueño en los últimos días!

En la primera prueba que hice con el operador todo funcionó bien. Voy a probar con el conjunto de SPs auténticos del sistema.

Gracias.

kinobi
06-07-2003, 15:57:55
Hola,

Posteado originalmente por mlara
o porque cuando uno busca la sección operadores que toda referencia del lenguaje tiene sencillamente en la de InterBase no la encuentra.

es cierto que en el volumen "Language Reference" de la documentación no aparece la referencia al operador de concatenación (algo que debería ser corregido), pero también es cierto que sí viene en la documentación:

"Concatenation operator ||, used to combine character strings."

Volumen "Embedded SQL Guide", capítulo "Working with Data", "Understanding SQL expressions"

De todas formas, coincido contigo, es algo que debería ir en la guía de referencia del lenguaje y no en un apartado escondido de la guía de SQL embebido.

Saludos.

mlara
06-07-2003, 16:18:41
No era necesaria la tabla. Veamos el siguiente SP:


ALTER PROCEDURE "EvalExpr"
RETURNS(
out_expr VARCHAR(256))

AS

DECLARE VARIABLE i DECIMAL(18, 4);
DECLARE VARIABLE expr VARCHAR(256);
DECLARE VARIABLE segexpr VARCHAR(32);

BEGIN

expr = '';

i = 0;
WHILE (i < 34) DO BEGIN

segexpr = CAST(i AS VARCHAR(32));
expr = expr || segexpr;

segexpr = '+';
expr = expr || segexpr;

i = i+1;
END

out_expr = expr;

SUSPEND;
END


En este procedimiento se produce el mismo error cuando se cuenta hasta i < 34. Si se suenta hasta i < 33 no hay error.

Claro, la variable i es decimal(18, 4) y tiene como mínimo 6 caracteres al ser convertida a varchar, así si i = 0 tenemos que cast(i as varchar(32)) = '0.0000'. cuando se cuenta hasta i < 33 se tienen exactamente 254 caracteres, valor justo por debajo del
tamaño de la variable expr.

Sucede que cuando un procedimiento tiene mucho más código, como InterBase no retorna el punto exacto donde está el error, pues la depuración es ardua y engorrosa, pero ahí vamos.

mlara
06-07-2003, 16:29:19
Con respecto a tu último comentario, has citado la referencia:

Volumen "Embedded SQL Guide", capítulo "Working with Data", "Understanding SQL expressions"

Eso lo entiendo, y aunque hace más o menos unos un año y medio trabajé con SQL embebido, pues no lo recordaba. Además precisamente ahora no estoy trabajando con SQL embebido, por lo que no he considerado necesario tener siquiera abierta la referencia citada.

Definitivamente el error debe ser corregido.

Gracias.