火鸟常数UDF

发布于 2024-10-22 13:57:02 字数 1173 浏览 5 评论 0原文

是否可以将 EXTERNAL FUNCTION 设置为常量或不可变,以便 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 正是通过其稳定函数的概念允许这样做。

Is it possible to make an EXTERNAL FUNCTION as constant or immutable, so that Firebird knows not to recompute it during the course of one SQL statement?

In the example below (Firebird 2.1), I'd like GETTIMEOFDAY() to behave like CURRENT_TIMESTAMP, but it is instead evaluated twice:

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

As you can see, the value of "TS" remains constant, but my "TIME_T" advances across the FB_SLEEP() calls. (FB_SLEEP is a convenience function to pause for the given number of seconds.)

Is what I want possible? I know PostgreSQL permits precisely this with its concept of STABLE FUNCTIONS.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

帅的被狗咬 2024-10-29 13:57:02

AFAIK,您不能在 Firebird 中将 UDF 标记为常量或不可变,但作为一种解决方法,您可以依靠内联视图(又名派生表)来实现您想要的:仅选择一次值并将其用作常量在你的结果中。我手头没有任何 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;

AFAIK, you cannot mark a UDF as constant or inmutable in Firebird, but as a workaround you can rely on a in-line view (aka derived table) to achieve what you want: select the value just one time and use it as a constant in your results. I do not have any UDF at hand to make a test, so maybe some syntax error, but I hope you catch the idea behind this:

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;

You can also rely on a selectable stored procedure to run the udf once and add the column to the results of a given query

Edit As requested, I'm including the Stored procedure:

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;
彩扇题诗 2024-10-29 13:57:02

简述

简短的回答是“否”,随附的指导是“始终在 UTC 中运行服务器”。

解决方法

  • 最简单的情况:稳定的 UTC 时间戳
    (这是我最初的目标。)如果 CURRENT_TIMESTAMP 足够精确,只需以 UTC 运行服务器即可。不需要UDF。

  • 显式预计算 UDF
    没有直接支持的方法来“稳定”UDF。因此,大多数客户端最好简单地预先计算 UDF 的返回值,使该文字值可用作 GTT 等中客户端提供的参数。

Kludge

CURRENT_TRANSACTION 和 CURRENT_TIMESTAMP 一起有效地标识单个查询,至少达到 CURRENT_TIMESTAMP 的精度。 (再次假设时钟采用 UTC 格式,以免时间在夏令时更改期间重复出现。)

考虑到这一点,可选择的存储过程可以使用 RDB$SET_CONTEXT 将 UDF 的返回值缓存为字符串,并且RDB$GET_CONTEXT,存储在 USER_TRANSACTION 上下文中并关闭 CURRENT_TIMESTAMP。添加一些额外的逻辑来修剪存储在 USER_TRANSACTION 下的条目数量。恶心。

In Brief

The short answer is "no," and the accompanying guidance is "always run your server in UTC."

Workarounds

  • Simplest case: stable UTC timestamps
    (This was my original aim.) If CURRENT_TIMESTAMP is precise enough, simply run your server in UTC. No UDF required.

  • Explicitly precompute UDF
    There's no directly supported way to "stabilize" a UDF. So, most clients are better off simply pre-computing the UDF's return value, making that literal value available as a client-supplied parameter, in a GTT, etc.

Kludge

CURRENT_TRANSACTION and CURRENT_TIMESTAMP taken together effectively identify an individual query, at least to the precision of CURRENT_TIMESTAMP. (Again presuming that the clock is in UTC, lest time repeat itself during a daylight savings change.)

That in mind, a selectable stored procedure could cache the UDF's return value as a string using RDB$SET_CONTEXT and RDB$GET_CONTEXT, storing in the USER_TRANSACTION context and keying off of CURRENT_TIMESTAMP. Add in a little extra logic to prune the number of entries stored under USER_TRANSACTION, too. Yuck.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文