Firebird постоянная UDF - PullRequest
       13

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

1 голос
/ 16 марта 2011

Можно ли сделать ВНЕШНУЮ ФУНКЦИЮ постоянной или неизменной, чтобы 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 разрешает именно это с его концепцией STABLE FUNCTIONS.

Ответы [ 2 ]

2 голосов
/ 16 марта 2011

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;
1 голос
/ 24 марта 2011

Вкратце

Краткий ответ - «нет», а сопровождающее руководство - «всегда запускайте свой сервер в 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. Тьфу.

...