被遗忘的赋值运算符“=”和常见的“:=”

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 标准运算符 =>。仍然支持:= 以实现向后兼容性。但是在不需要在非常旧的版本上运行的新代码中使用标准运算符。

记录在手册中,章节Using Named Notation。 Here's the commit with explanation in GIT.

这适用于函数调用(SQL范围)中的命名参数赋值,不适用于plpgsql代码中的赋值运算符:=,它保持不变。

【讨论】:

以上是关于被遗忘的赋值运算符“=”和常见的“:=”的主要内容,如果未能解决你的问题,请参考以下文章

运算符和表达式

C++中赋值运算操作符和=重载有啥区别?

C++ 继承多态关系中的赋值运算符的重载=operator()

C++ 继承多态关系中的赋值运算符的重载=operator()

为啥赋值运算符可以在 Rust 中被链接?

Python学习-算术运算符,赋值运算符和复合运算符