被遗忘的赋值运算符“=”和常见的“:=”
Posted
技术标签:
【中文标题】被遗忘的赋值运算符“=”和常见的“:=”【英文标题】:The forgotten assignment operator "=" and the commonplace ":=" 【发布时间】:2011-11-19 17:04:10 【问题描述】:PL/pgSQL 的文档说,变量的声明和赋值是用:=
完成的。
但是一个简单、更短且更现代 (见脚注)=
似乎可以按预期工作:
CREATE OR REPLACE FUNCTION foo() RETURNS int AS $$
DECLARE
i int;
BEGIN
i = 0;
WHILE NOT i = 25 LOOP
i = i + 1;
i = i * i;
END LOOP;
RETURN i;
END;
$$ LANGUAGE plpgsql;
> SELECT foo();
25
请注意,PL/pgSQL可以清楚地区分赋值和比较,如行中所示
WHILE NOT i = 25 LOOP
所以,问题是:
我没有在文档中找到提到和/或解释这一点的部分吗? 使用=
代替:=
是否有任何已知后果?
编辑/脚注:
请像 A Brief, Incomplete, and Mostly Wrong History of Programming Languages 一样眨眼看“更现代”的部分:
1970 - Niklaus Wirth 创建了 Pascal,一种程序语言。批评家 立即谴责 Pascal,因为它使用 "x := x + y" 语法 而不是更熟悉的 C 类“x = x + y”。这种批评 尽管 C 尚未被发明,但仍会发生。
1972 - 丹尼斯·里奇发明了一种强大的枪,可以向前射击 并同时倒退。对死亡人数不满意 他发明了 C 和 Unix。
【问题讨论】:
它的工作原理确实很奇怪。您可能希望将其发布到 PG 邮件列表,以便 PG 开发人员可以就此发表意见。 使用=
而不是:=
有什么优势吗? “更现代”并不是我的优势。
通常我会同意。但是最后一种计算机语言是什么时候发明的,a) 已被广泛使用,b) 使用 ':=' 进行赋值?我想那肯定已经过了几十年了。另一方面,我已将其设置为斜体以使其有点;-)
有趣的是,我只使用=
而不是:=
没有遇到任何问题。最初是一个意外(来自其他语言的习惯),但我注意到 PostgreSQL 愿意创建这些函数并且它们运行良好,所以我坚持使用它。
@A.H.你应该看看 xquery - 它是在过去 5-10 年的某个时候发明的,正在积极开发中并使用“:=”
【参考方案1】:
阅读 Postgresql 9 文档:
This page 将“=”列为运算符优先级表中的赋值运算符。
但奇怪的是this page(赋值运算符文档)没有提到它。
【讨论】:
这正是重点:第一个链接是通用 SQL 部分,=
是此上下文中的 comparison 运算符,如示例所示。第二个链接是关于 PL/pgSQL 的,那里的 assignment 被描述为:=
。这个问题仍然悬而未决。 :-)
我必须半途而废:第一个链接在优先表中提到了“平等,赋值”,但在 SQL 上下文中,assignment 表示UPDATE table SET column **=** value
。
是的,我明白你的意思 - 文档是关于 SQL,而不是过程 SQL 包装语言。【参考方案2】:
我自己的问题的部分答案:
PL/pgSQL 部分Obtaining the Result Status 显示了两个使用特殊语法的示例:
GET DIAGNOSTICS variable = item [ , ... ];
GET DIAGNOSTICS integer_var = ROW_COUNT;
我尝试了:=
和=
,它们都可以。
但是GET DIAGNOSTICS
是一种特殊的语法,所以有人会争辩说,这也不是一个正常的 PL/pgSQL 赋值操作。
【讨论】:
GET DIAGNOSTICS 由 ANSI/SQL 定义——SQL 使用“=”进行比较和赋值。【参考方案3】:在PL/PgSQL解析器中,赋值运算符定义为
assign_operator : '='
| COLON_EQUALS
;
这是一项遗留功能,自 1998 年引入源代码以来一直存在 - 正如我们在 PostgreSQL Git repo 中看到的那样。
从 9.4 版开始是oficially documented。
这种特性——同一事物有两个运算符——在 pgsql 用户列表中被提出,有些人要求将其删除,但它仍保留在核心中,因为遗留代码的公平语料库依赖于它。
看到这个message from Tom Lane (core Pg developer)。
所以,直接回答你的问题:
我没有在文档中找到一些提到和/或解释的部分 这个?
你没有找到它,因为它没有记录,从 9.4 版开始修复。
使用 = 代替 := 是否有任何已知后果。
使用 = 没有副作用,但您应该使用 := 进行赋值以使您的代码更具可读性,并且(作为副作用)更兼容使用 PL/SQL。
更新:在极少数情况下可能会有副作用(参见 Erwin 的回答)
更新:感谢 Daniel、Sandy 和其他人的意见更新了答案。
【讨论】:
感谢您的深入研究。我觉得很有趣,PostgreSQL 邮件列表上的问题是在我的问题发布前几周出现的。看来,是时候这样做了 ;-) @filiprem,关于“面向未来”,嗯....=
不太可能在未来中断。
在您的回答中,您已经写了关于冒号等于的内容:“它计划被删除但仍然保留,因为有些人依赖它”。这应该意味着,如果他们继续他们的计划,使用冒号等号可能会导致问题。但是,在你回答的最后,你写了“使用 = 没有副作用,但你应该使用 := 进行赋值,以使你的代码面向未来。”。这两种说法是不是相互矛盾?
@Sandy,对。我宁愿说“它可以在被标记为过时多年后被删除。”关于“副作用”:可能有一些。不频繁,但可能。无论如何,删除这个小问题不值得破坏遗留代码。我敢打赌,我们至少会拥有它几年。
由于version 9.4 将variable := | = expression
记录为赋值语法,因此普遍的选项是完全支持=
进行赋值。【参考方案4】:
第一季度
这终于是added to the official documentation with Postgres 9.4:
对 PL/pgSQL 变量的赋值写成:
variable := | = expression;
[...] 可以使用相等 (
=
) 代替符合 PL/SQL 的:=
。
第二季度
使用
=
代替:=
是否有任何已知后果?
是的,我遇到了一个严重后果的案例:使用命名参数的函数调用 - 这是相关但不完全相同的事情。
严格来说,这种情况下的区别在于 SQL 代码。但这对于毫无戒心的程序员来说是一种学术差异。1
考虑函数:
CREATE FUNCTION f_oracle(is_true boolean = TRUE) -- correct use of "="
RETURNS text AS
$func$
SELECT CASE $1
WHEN TRUE THEN 'That''s true.'
WHEN FALSE THEN 'That''s false.'
ELSE 'How should I know?'
END
$func$ LANGUAGE sql;
旁白:注意在函数定义中正确使用=
。这是CREATE FUNCTION
语法的一部分——以 SQL 赋值的形式。2
使用命名符号的函数调用:
SELECT * FROM f_oracle(is_true := TRUE);
Postgres 将:=
识别为参数分配,一切正常。 但是:
SELECT * FROM f_oracle(is_true = TRUE);
由于 =
是 SQL 相等运算符,Postgres 将 is_true = TRUE
解释为调用语句上下文中的 SQL 表达式,并在将结果作为 未命名位置传递之前尝试对其进行评估参数。它在外部范围内查找标识符is_true
。如果找不到:
ERROR: column "is_true" does not exist
这是幸运的情况,幸运的是,也是常见的情况。
当is_true
可以在外部范围内找到时(并且数据类型兼容),is_true = TRUE
是一个有效的表达式,其结果为boolean
,函数接受。不会发生错误。显然,这是程序员使用 SQL 相等运算符=
...
这个SQL Fiddle 演示了效果。
如果您不知道 =
和 :=
之间的区别,则很难调试。始终使用正确的运算符。
1 使用named notation in function calls 时,只有:=
是正确的赋值运算符。这适用于所有语言的函数,而不仅仅是 PL/pgSQL,直到并包括 pg 9.4。见下文。
2
可以使用=
(或DEFAULT
)来定义default values for function parameters。这与手头的问题没有任何关系。它非常接近错误的用例。
Postgres 9.0 - 9.4:从 :=
转换到 =>
分配给命名函数参数的 SQL 标准是 =>
(和 Oracle's PL/SQL uses it。Postgres 不能这样做,因为该运算符之前没有保留,所以它使用 PL/pgSQL 的赋值运算符 :=
代替。随着 Postgres 9.0 的发布,不推荐将 =>
用于其他目的。Per release notes:
不赞成使用 => 作为运算符名称 (Robert Haas)
PostgreSQL 的未来版本可能会拒绝这个操作符名称 完全,为了支持命名的 SQL 标准表示法 函数参数。目前,它仍然是允许的,但是 定义此类运算符时会发出警告。
如果您应该将=>
用于其他用途,请停止并停止。以后会坏掉的。
Postgres 9.5:现在使用=>
从此版本开始,使用 SQL 标准运算符 =>
。仍然支持:=
以实现向后兼容性。但是在不需要在非常旧的版本上运行的新代码中使用标准运算符。
这适用于函数调用(SQL范围)中的命名参数赋值,不适用于plpgsql代码中的赋值运算符:=
,它保持不变。
【讨论】:
以上是关于被遗忘的赋值运算符“=”和常见的“:=”的主要内容,如果未能解决你的问题,请参考以下文章
C++ 继承多态关系中的赋值运算符的重载=operator()