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.

¿Fue útil?

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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top