遗忘的赋值运算符"="和常见的":="

A.H*_*.H. 41 postgresql plpgsql assignment-operator colon-equals

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
Run Code Online (Sandbox Code Playgroud)

请注意,Pl/pgSQL可以清楚地区分分配和比较,如行中所示

      WHILE NOT i = 25 LOOP
Run Code Online (Sandbox Code Playgroud)

所以,问题是:

  • 我没有在文档中找到提及和/或解释这个的部分吗?
  • 是否有使用=代替的已知后果:=

编辑/脚注:

请使用" 简短,不完整和错误的编程语言历史 "中的"更现代"部分:

1970年 - Niklaus Wirth创建了Pascal,一种程序语言.批评者立即谴责Pascal,因为它使用"x:= x + y"语法而不是更熟悉的类C"x = x + y".尽管C还没有被发明,但这种批评仍在发生.

1972年 - 丹尼斯·里奇发明了一种强大的枪,同时向前和向后射击.他对发明C和Unix的死亡和永久性伤害的数量不满意.

fil*_*rem 42

在PL/PgSQL解析器中,赋值运算符定义为

assign_operator : '='
                | COLON_EQUALS
                ;
Run Code Online (Sandbox Code Playgroud)

这是一个遗留特性,自1998年引入源代码时就出现了 - 正如我们在PostgreSQL Git仓库中看到的那样.

从版本9.4开始,它是经过特殊记录的.

这个特性 - 有两个运算符用于同一事物 - 在pgsql用户列表中引发,有些人要求将其删除,但它仍然保留在核心中,因为遗留代码的公平语料库依赖于它.

请参阅Tom Lane(核心Pg开发人员)的消息.

所以,直接回答你的问题:

我没有在文档中找到提及和/或解释这个的部分吗?

您没有找到它,因为它没有记录,从版本9.4开始修复.

使用=而不是:=有任何已知的后果吗?

使用=没有任何副作用,但您应该使用:=进行赋值以使您的代码更具可读性,并且(作为副作用)与PL/SQL更兼容.

更新:在极少数情况下可能会产生副作用(参见Erwin的回答)


更新:感谢丹尼尔,桑迪和其他人的意见.

  • 感谢您的深入研究.我觉得非常有趣,PostgreSQL邮件列表上的问题出现在我的问题发布前几周.好像,是时候了;-) (2认同)
  • 自[版本9.4](http://www.postgresql.org/docs/9.4/static/plpgsql-statements.html)以来,它记录了`变量{:= | =} expression`作为赋值语法,因此普遍存在的选项是完全支持`=`for assignment. (2认同)

Erw*_*ter 30

Q1

这最终被添加到Postgres 9.4的官方文档中:

将值分配给PL/pgSQL变量写为:

variable { := | = } expression;

[...] =可以使用Equal()代替PL/SQL兼容:=.

Q2

是否有使用=代替的已知后果:=

是的,我有一个严重后果的案例:使用命名参数调用函数 - 这是相关但不完全相同的事情.

严格地说,在这种情况下的区别在于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;
Run Code Online (Sandbox Code Playgroud)

旁白:注意=在函数定义中正确使用.这是CREATE FUNCTION语法的一部分- 以SQL赋值的方式.2

使用命名表示法的函数调用:

SELECT * FROM f_oracle(is_true := TRUE);
Run Code Online (Sandbox Code Playgroud)

Postgres标识:=为参数分配,一切都很好.但是:

SELECT * FROM f_oracle(is_true = TRUE);
Run Code Online (Sandbox Code Playgroud)

由于=是SQL等式运算符,Postgres is_true = TRUE在调用语句的上下文中将其解释为SQL表达式,并在将结果作为未命名的位置参数传递之前尝试对其进行求值.它is_true在外部范围中查找标识符.如果找不到:

ERROR:  column "is_true" does not exist
Run Code Online (Sandbox Code Playgroud)

这是幸运的情况,幸运的是,也是常见的情况.

如果is_true 可以在外部作用域中找到(并且数据类型是兼容的),is_true = TRUE则是一个有效的表达式,其boolean结果可被函数接受.没有错误发生.显然,这是程序员使用SQL相等运算符的意图=......

这个SQL小提琴演示了这种效果.

很辛苦,如果你不知道之间的区别的调试=:=.
始终使用正确的操作员.


1在函数调用中使用命名表示法时,只有:=正确的赋值运算符.这适用于所有语言的功能,而不仅仅是PL/pgSQL,包括第9.4页.见下文.

2 可以使用=(或DEFAULT)来定义函数参数的默认值.这与任何方面的问题无关.它非常接近不正确的用例.

Postgres 9.0 - 9.4:从过渡:==>

分配给命名函数参数的SQL标准是=>(并且Oracle的PL/SQL使用它 .Postgres不能这样做,因为操作符以前是未保留的,所以它使用PL/pgSQL的赋值运算符:=.随着Postgres 9.0的发布=>用于其他目的的使用已被弃用.每个发行说明:

不推荐使用=>作为运营商名称(Robert Haas)

PostgreSQL的未来版本可能会完全拒绝此运算符名称,以支持命名函数参数的SQL标准表示法.目前,它仍然被允许,但是在定义这样的操作符时会发出警告.

如果你应该使用=>其他东西,停止和停止.它将来会破裂.

Postgres 9.5:=>现在使用

从此版本开始,使用SQL标准运算符=>.:=仍然支持向后兼容性.但是在新代码中使用标准运算符,不需要在非常旧的版本上运行.

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