PostgreSQL函数返回void

18

PL/pgSQLSQL 中的函数可以定义为 RETURNS void。最近我发现了结果中的一个奇怪差异。

考虑以下演示:

CREATE OR REPLACE FUNCTION f_sql()
  RETURNS void
  LANGUAGE sql AS
'SELECT NULL::void';  -- = "do nothing", no special meaning


CREATE OR REPLACE FUNCTION f_plpgsql()
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
NULL;  -- = "do nothing", no special meaning
END;
$func$;

函数f_sql()使用了在SQL函数中只有一个可能的方式,即将SELECT(作为最后一条命令)用于返回void类型的函数。我之所以使用它,只是因为它是这个测试目的中最简单的方法——任何其他带有UPDATEDELETE等命令的函数都会显示相同的行为。

现在,void是一个虚构的类型。虽然plpgsql函数似乎返回与空字符串等价的void类型——实际上是''::void。但sql函数似乎返回NULL::void

db=# SELECT f_sql() IS NULL;
 ?column?
----------
 t

db=# SELECT f_sql()::text IS NULL;
 ?column?
----------
 t

db=# SELECT f_plpgsql() IS NULL;
 ?column?
----------
 f

db=# SELECT f_plpgsql()::text = '';
 ?column?
----------
 t

这可能会产生微妙且令人困惑的副作用。
这种差异背后的原因是什么?


它们被声明为返回void;也许我们没有必要去看它们返回的值与任何东西相比较。 (如果您真的想阻止人们查看返回值,您可以使其返回随机值。 下次我写应该返回void的内容时可能会这样做。) - Mike Sherrill 'Cat Recall'
@Catcall:是的,有人可能会认为根本不应该检查一个空值。但仍然感觉很奇怪,因为该值取决于所选择的语言而不同。这不应该发生。我会在有时间时提交一个错误报告,看看核心团队对此的看法。 - Erwin Brandstetter
几年后,我偶然发现了同样的问题!我学到了你在无返回SQL函数中使用SELECT NULL::void的有用技巧:顺便说一下,你提供的链接似乎已经失效了(尽管这个技巧在9.3版本中仍然有效)。 - user533832
@JackDouglas。感谢您的更新。自从我们在这里讨论后,Pavel显然已经从网站上删除了该章节。我现在已经删除了这个失效的链接。 - Erwin Brandstetter
1个回答

11

(我对这段源代码不是专家,已经警告过了。)

源代码在这里在线上。我省略了文件名;您可以搜索函数名称以找到其定义。我保留了行号(通常),因为这样更容易剪切和粘贴,而不同的行号意味着源代码已经改变。

简单来说,一些“void”返回值可能是空的 C 字符串(即空的空终止字符串),而另一些则是空指针。

以下是看起来相关的源代码部分。

00228 /*
00229  * void_out     - output routine for pseudo-type VOID.
00230  *
00231  * We allow this so that "SELECT function_returning_void(...)" works.
00232  */
00233 Datum
00234 void_out(PG_FUNCTION_ARGS)
00235 {
00236     PG_RETURN_CSTRING(pstrdup(""));
00237 }

00251 /*
00252  * void_send    - binary output routine for pseudo-type VOID.
00253  *
00254  * We allow this so that "SELECT function_returning_void(...)" works
00255  * even when binary output is requested.
00256  */
00257 Datum
00258 void_send(PG_FUNCTION_ARGS)
00259 {
00260     StringInfoData buf;
00261 
00262     /* send an empty string */
00263     pq_begintypsend(&buf);
00264     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
00265 }

我们还有

00285 /* To return a NULL do this: */
00286 #define PG_RETURN_NULL()  \
00287     do { fcinfo->isnull = true; return (Datum) 0; } while (0)
00288 
00289 /* A few internal functions return void (which is not the same as NULL!) */
00290 #define PG_RETURN_VOID()     return (Datum) 0

我认为这样做很合理,一个通过PG_RETURN_VOID()返回的用户定义函数不等同于一个通过void_out()或void_send()返回的函数。我还不知道原因,但我必须停下来睡觉了。


非常有趣!这个行为是故意的吗?也许是一个 bug 吗? - Erwin Brandstetter
这是一个错误,请报告它。 - Pavel Stehule
但我不确定是什么问题 - 没有一致的行为,测试“void is null”也没有意义。 - Pavel Stehule

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接