文章分享:诡异的 unnest 函数

From: Quan Zongliang <quanzongliang(at)gmail(dot)com>
To: pgsql-zh-general(at)postgresql(dot)org
Subject: 文章分享:诡异的 unnest 函数
Date: 2015-12-29 02:47:56
Message-ID: 5681F45C.6040400@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-zh-general

函数 unnest 定义如下:

CREATE OR REPLACE FUNCTION unnest(anyarray)
RETURNS SETOF anyelement AS
'array_unnest'
LANGUAGE internal IMMUTABLE STRICT
COST 1
ROWS 100;
为了可读性,这是还原后的SQL 语句,实际上它是在 pg_proc.h 中定义的:

DATA(insert OID = 2331 ( unnest PGNSP PGUID 12 1 100 0 0 f f f
f t t i 1 0 2283 "2277" _null_ _null_ _null_ _null_ array_unnest _null_
_null_ _null_ ));

运行一下:
postgres=# select unnest(array[10,20]);
unnest
--------
10
20
(2 rows)

postgres=#

很正常是吧,换种方式:

postgres=# SELECT unnest(array[10,20],array['foo','bar'],array[1.0]);
ERROR: function unnest(integer[], text[], numeric[]) does not exist
LINE 1: SELECT unnest(array[10,20],array['foo','bar'],array[1.0]);
^
HINT: No function matches the given name and argument types. You might
need to add explicit type casts.
postgres=#
这也正常对吧,毕竟只支持一个参数嘛,再换种方式:

postgres=# SELECT * FROM unnest(array[10,20],array['foo','bar'],array[1.0]);
unnest | unnest | unnest
--------+--------+--------
10 | foo | 1.0
20 | bar |
(2 rows)

postgres=#
非常不科学的运行成功,三个参数!

在 FROM 子句里边的函数调用叫做 RangeFunction,而只有这个 unnest 做了特殊
处理,实际上三个参数函数被调用了三次。

代码(parse_clause.c)在这里:
if (IsA(fexpr, FuncCall))
{
FuncCall *fc = (FuncCall *) fexpr;

if (list_length(fc->funcname) == 1 &&
strcmp(strVal(linitial(fc->funcname)), "unnest") == 0 &&
list_length(fc->args) > 1 &&
fc->agg_order == NIL &&
fc->agg_filter == NULL &&
!fc->agg_star &&
!fc->agg_distinct &&
!fc->func_variadic &&
fc->over == NULL &&
coldeflist == NIL)
{
ListCell *lc;

foreach(lc, fc->args)
{
Node *arg = (Node *) lfirst(lc);
FuncCall *newfc;

newfc = makeFuncCall(SystemFuncName("unnest"),
list_make1(arg),
fc->location);
... // 更多

语法引擎(gram.y)代码如下:
from_list:
table_ref { $$ = list_make1($1); }
| from_list ',' table_ref { $$ = lappend($1, $3); }
;

/*
* table_ref is where an alias clause can be attached.
*/
table_ref: relation_expr opt_alias_clause
... // 省略
| func_table func_alias_clause
{
RangeFunction *n = (RangeFunction *) $1;
n->alias = linitial($2);
n->coldeflist = lsecond($2);
$$ = (Node *) n;
}

文章结束

即时通讯工具无法保存历史提问,不利于知识共享。
欢迎使用 PostgreSQL中文邮件列表
欢迎使用云栖问答:http://yq.aliyun.com/ask

Browse pgsql-zh-general by date

  From Date Subject
Next Message 李海龙 2015-12-29 02:50:41 Re: [pgsql-zh-general] 工作机会2015-12-29
Previous Message 张文升 2015-12-29 02:13:07 Re: 工作机会2015-12-29