Firebird постоянная UDF

Можно ли сделать ВНЕШНУЮ ФУНКЦИЮ постоянной или неизменной, чтобы Firebird знал, что не нужно пересчитывать ее в течение одного оператора SQL?

В приведенном ниже примере (Firebird 2.1) я бы хотел, чтобы GETTIMEOFDAY() вел себя как CURRENT_TIMESTAMP, но вместо этого он оценивается дважды:

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

Как видите, значение "TS" остается постоянным, но мой "TIME_T" продвигается через вызовы FB_SLEEP(). (FB_SLEEP - это удобная функция для приостановки на указанное количество секунд.)

Возможно ли то, что я хочу? Я знаю, что PostgreSQL разрешает именно это благодаря своей концепции СТАБИЛЬНЫХ ФУНКЦИЙ.

2 ответа

Решение

Вкратце

Краткий ответ "нет", и сопровождающее руководство "всегда запускайте свой сервер в UTC".

обходные

  • Простейший случай: стабильные метки времени UTC
    (Это было моей первоначальной целью.) Если CURRENT_TIMESTAMP достаточно точен, просто запустите свой сервер в формате UTC. UDF не требуется.

  • Явно предварительно вычислить UDF
    Нет прямого способа "стабилизировать" UDF. Таким образом, большинству клиентов лучше просто предварительно вычислить возвращаемое значение UDF, сделав это буквальное значение доступным в качестве параметра, предоставленного клиентом, в GTT и т. Д.

клюдж

CURRENT_TRANSACTION и CURRENT_TIMESTAMP, взятые вместе, эффективно идентифицируют отдельный запрос, по крайней мере, с точностью до CURRENT_TIMESTAMP. (Снова предполагая, что часы находятся в UTC, чтобы время не повторилось во время изменения летнего времени.)

Это означает, что выбираемая хранимая процедура может кэшировать возвращаемое значение UDF в виде строки, используя RDB$SET_CONTEXT и RDB$GET_CONTEXT, сохраняя в контексте USER_TRANSACTION и отключая CURRENT_TIMESTAMP. Добавьте немного дополнительной логики, чтобы также сократить количество записей, хранящихся в USER_TRANSACTION. Тьфу.

AFAIK, вы не можете пометить UDF как постоянную или неизменную в Firebird, но в качестве обходного пути вы можете полагаться на встроенное представление (или производную таблицу), чтобы достичь того, что вы хотите: выберите значение только один раз и используйте его как константу в ваших результатах. У меня нет UDF под рукой, чтобы сделать тест, так что, возможно, какая-то синтаксическая ошибка, но я надеюсь, что вы поймете эту идею:

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;

Вы также можете положиться на выбираемую хранимую процедуру для однократного запуска udf и добавления столбца к результатам данного запроса.

Редактировать В соответствии с просьбой, я включаю хранимую процедуру:

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;
Другие вопросы по тегам