Firebird constante UDF
-
25-10-2019 - |
Pregunta
¿Es posible realizar una función externa como constante o inmutable, para que Firebird sepa que no debe recomputarla durante el curso de una declaración SQL?
En el ejemplo a continuación (Firebird 2.1), me gustaría que GetTeofday () se comporte como Current_Timestamp, pero en cambio se evalúa dos veces:
SQL> DECLARE EXTERNAL FUNCTION gettimeofday -- gettimeofday(2) wrapper
CON> RETURNS DOUBLE PRECISION BY VALUE
CON> ENTRY_POINT 'UDF_gettimeofday' MODULE_NAME 'udf_gettimeofday';
SQL> SELECT CURRENT_TIMESTAMP AS ts,
CON> CAST(GETTIMEOFDAY() AS INTEGER) AS time_t,
CON> FB_SLEEP(2) AS zzz
CON> FROM rdb$database
CON> CROSS JOIN (SELECT 1 AS foo
CON> FROM rdb$database
CON> UNION ALL
CON> SELECT 2
CON> FROM rdb$database) d;
TS TIME_T ZZZ
========================= ============ ============
2011-03-15 20:57:46.0390 1300244268 0
2011-03-15 20:57:46.0390 1300244270 0
Como puede ver, el valor de "TS" permanece constante, pero mi "Time_T" avanza a través de las llamadas FB_Sleep (). (FB_Sleep es una función de conveniencia para detenerse para el número dado de segundos).
¿Es posible lo que quiero? Sé que PostgreSQL permite precisamente esto con su concepto de ESTABLE Funciones.
Solución 2
En breve
La respuesta corta es "no", y la guía adjunta es "siempre ejecutar su servidor en UTC".
Soluciones
Caso más simple: marcas de tiempo UTC estables
(Este era mi objetivo original). Si Current_Timestamp es lo suficientemente preciso, simplemente ejecute su servidor en UTC. No se requiere UDF.Precomputado explícitamente UDF
No hay una forma directamente compatible de "estabilizar" un UDF. Por lo tanto, la mayoría de los clientes están mejor simplemente que precomputan el valor de retorno de la UDF, lo que hace que ese valor literal esté disponible como un parámetro suplicado por el cliente, en un GTT, etc.
Kludge
Current_Transaction y Current_Timestamp en conjunto identifican efectivamente una consulta individual, al menos a la precisión de Current_Timestamp. (Nuevamente suponiendo que el reloj esté en UTC, para que no se repita el tiempo durante un cambio de ahorro para el día).
Eso en mente, un procedimiento almacenado seleccionable podría almacenar en caché el valor de devolución de la UDF Como una cadena Usando RDB $ set_context y rdb $ get_context, almacenando en el contexto user_transaction y la eliminación de Current_Timestamp. Agregue un poco de lógica adicional para podar el número de entradas almacenadas también en User_Transaction. Yuck.
Otros consejos
AFAIK, no puede marcar un UDF como constante o inmutable en Firebird, pero como solución puede confiar en una vista en línea (también conocida como Tabla derivada) para lograr lo que desea: seleccione el valor solo una vez y usarlo como constante en tus resultados. No tengo UDF a mano para hacer una prueba, así que tal vez un error de sintaxis, pero espero que tome la idea detrás de esto:
SELECT CURRENT_TIMESTAMP AS ts,
q1.time_t,
FB_SLEEP(2) AS zzz
FROM rdb$database
CROSS JOIN (select CAST(GETTIMEOFDAY() AS INTEGER) AS time_t from rdb$database)
CROSS JOIN (SELECT 1 AS foo
FROM rdb$database
UNION ALL
SELECT 2
FROM rdb$database) d;
También puede confiar en un procedimiento almacenado seleccionable para ejecutar el UDF una vez y agregar la columna a los resultados de una consulta dada
Editar Según lo solicitado, incluyo el procedimiento almacenado:
SET TERM ^ ;
CREATE PROCEDURE ExampleSP
RETURNS
(
ts timestamp
, time_t integer
, zzz integer
)
as
BEGIN
SELECT CAST(GetTimeOfDay() AS Integer)
FROM rdb$database
INTO :time_t;
for SELECT Current_Timestamp AS ts,
FB_SLEEP(2) AS zzz
FROM rdb$database
CROSS JOIN (SELECT 1 AS foo
FROM rdb$database
UNION ALL
SELECT 2
FROM rdb$database) d
INTO :ts, :zzz do
SUSPEND;
END
^
SET TERM ; ^
SELECT * FROM ExampleSP;