The forgotten assignment operator "=" and the commonplace ":="
Asked Answered
R

4

53

The documentation for PL/pgSQL says, that declaration and assignment to variables is done with :=. But a simple, shorter and more modern (see footnote) = seems to work as expected:

    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

Please note, that Pl/pgSQL can distinguish assignment and comparison clearly as shown in the line

      WHILE NOT i = 25 LOOP

So, the questions are:

  • Didn't I find some section in the docs which mention and/or explains this?
  • Are there any known consequences using = instead of :=?

Edit / Footnote:

Please take the "more modern" part with a wink like in A Brief, Incomplete, and Mostly Wrong History of Programming Languages:

1970 - Niklaus Wirth creates Pascal, a procedural language. Critics immediately denounce Pascal because it uses "x := x + y" syntax instead of the more familiar C-like "x = x + y". This criticism happens in spite of the fact that C has not yet been invented.

1972 - Dennis Ritchie invents a powerful gun that shoots both forward and backward simultaneously. Not satisfied with the number of deaths and permanent maimings from that invention he invents C and Unix.

Resign answered 18/9, 2011 at 15:2 Comment(6)
It's indeed strange that it works. You might want to post that to the PG mailing list so that the PG developers can say something regarding this.Spragens
Is there any advantage to using = rather than :=? Being "more modern" doesn't strike me as an advantage.Flannelette
Usualy I'd like to concur. But when was the last computer language invented, which a) is halfway widely used and b) uses ':=' for assignment? I think that must have been already several decades away. On the other hand I have set that in italics to make it somewhat ;-)Resign
Anecdotally, I've run in to no problems using only = instead of :=. It was initially an accident (habbit from other languages), but I noticed PostgreSQL was willing to create the functions and that they ran fine, so I've stuck with it.Platinous
I once read something that made me think = went to the DB engine while := was interpreted. But that may be crazy.Normandnormandy
@A.H.You should look at xquery - it was invented some time in the last 5-10 years, is under active development and uses ":="Enucleate
H
58

In PL/PgSQL parser, assignment operator is defined as

assign_operator : '='
                | COLON_EQUALS
                ;

This is a legacy feature, present in source code since 1998, when it was introduced - as we can see in the PostgreSQL Git repo.

Starting from version 9.4 it is oficially documented.

This idiosyncrasy - of having two operators for same thing - was raised on pgsql users list, and some people requested it to be removed, but it's still kept in the core because fair corpus of legacy code relies on it.

See this message from Tom Lane (core Pg developer).

So, to answer your questions straight:

Didn't I find some section in the docs which mention and/or explains this?

You did not find it because it was undocumented, which is fixed as of version 9.4.

Are there any known consequences using = instead of :=.

There are no side consequences of using =, but you should use := for assignment to make your code more readable, and (as a side effect) more compatible with PL/SQL.

Update: there may be a side consequence in rare scenarios (see Erwin's answer)


UPDATE: answer updated thanks to input from Daniel, Sandy & others.

Hendrik answered 3/10, 2011 at 22:10 Comment(7)
Thanks for the thorough research. I find it very interesting, that the question on the PostgreSQL mailinglist came up just a couple of weeks before my question was posted. Seems, it was time for that ;-)Resign
@filiprem, Regarding "future-proofing", Well.... it's unlikely that = will break in the future.Decker
In your answer, you have written this about colon equals: 'It's planned to be removed but still kept in because some people rely on it'. This should mean that using a colon equal can cause a problem, if they go ahead with their plan. But, at the end of your answer you have written 'There are no side consequences of using =, but you should use := for assignment to make your code future-proof.'. Aren't these two statements contradicting each other?Custom
@Sandy, right. I should rather say "It can be removed after many years of being marked as obsolete." Regarding "side consequences": There could be some. Not frequent, but possible. Anyway, removal of this tiny gotcha is not worth breaking legacy code. I bet we will have it for several years at least.Hendrik
Since version 9.4 it documents variable { := | = } expression as the assignment syntax so the option that prevailed is that = for assignment is fully supported.Assured
Thanks @DanielVérité - so it looks like they are NOT going to make it obsolete.... good or bad, we have to live with it. :-)Hendrik
It's definitely not a PL/SQL compatibility feature, because in PL/SQL only := is allowed for assignment.Spragens
H
41

Q1

This has finally been added to the official documentation with Postgres 9.4:

An assignment of a value to a PL/pgSQL variable is written as:

variable { := | = } expression;

[...] Equal (=) can be used instead of PL/SQL-compliant :=.

Q2

Are there any known consequences using = instead of :=?

Yes, I had a case with severe consequences: Function call with named parameters - which is related but not exactly the same thing.

Strictly speaking, the distinction in this case is made in SQL code. But that's an academic differentiation to the unsuspecting programmer.1

Consider the function:

CREATE FUNCTION f_oracle(is_true boolean = TRUE)  -- correct use of "="
  RETURNS text
  LANGUAGE sql AS
$func$
SELECT CASE $1
       WHEN TRUE  THEN 'That''s true.'
       WHEN FALSE THEN 'That''s false.'
       ELSE 'How should I know?'
       END
$func$;

Note the correct use of = in the function definition. That's part of the CREATE FUNCTION syntax - in the style of an SQL assignment.2

Function call with named notation:

SELECT * FROM f_oracle(is_true := TRUE);

Postgres identifies := as parameter assignment and all is well. However:

SELECT * FROM f_oracle(is_true = TRUE);

Since = is the SQL equality operator, Postgres interprets is_true = TRUE as SQL expression in the context of the calling statement and tries to evaluate it before passing the result as unnamed positional parameter. It looks for an identifier is_true in the outer scope. If that can't be found:

ERROR:  column "is_true" does not exist

That's the lucky case and, luckily, also the common one.

When is_true can be found in the outer scope (and data types are compatible), is_true = TRUE is a valid expression with a boolean result that is accepted by the function. No error occurs. Clearly, this is the intention of the programmer using the SQL equality operator = ...

This db<>fiddle demonstrates the effect.
Old sqlfiddle

Very hard to debug if you're unaware of the distinction between = and :=.
Always use the the correct operator.


1 When using named notation in function calls, only := is the correct assignment operator. This applies to functions of all languages, not just PL/pgSQL, up to and including pg 9.4. See below.

2 One can use = (or DEFAULT) to define default values for function parameters. That's not related to the problem at hand in any way. It's just remarkably close to the incorrect use case.

Postgres 9.0 - 9.4: Transition from := to =>

The SQL standard for assignment to named function parameters is => (and Oracle's PL/SQL uses it. Postgres could not do the same, since the operator had previously been unreserved, so it's using PL/pgSQL's assignment operator := instead. With the release of Postgres 9.0 the use of => for other purposes has been deprecated. The release notes:

Deprecate use of => as an operator name (Robert Haas)

Future versions of PostgreSQL will probably reject this operator name entirely, in order to support the SQL-standard notation for named function parameters. For the moment, it is still allowed, but a warning is emitted when such an operator is defined.

If you should be using => for something else, cease and desist. It will break in the future.

Postgres 9.5: use => now

Starting with this release, the SQL standard operator => is used. := is still supported for backward compatibility. But use the standard operator in new code that doesn't need to run on very old versions.

This applies to named parameter assignment in function calls (SQL scope), not to the assignment operator := in plpgsql code, which remains unchanged.

Harter answered 24/2, 2014 at 23:14 Comment(0)
R
3

A partial answer to my own question:

The PL/pgSQL section Obtaining the Result Status shows two examples using a special syntax:

GET DIAGNOSTICS variable = item [ , ... ]; 
GET DIAGNOSTICS integer_var = ROW_COUNT;

I tried both := and = and they work both.

But GET DIAGNOSTICS is special syntax, so one can argue, that this is also not a normal PL/pgSQL assignment operation.

Resign answered 22/9, 2011 at 19:33 Comment(1)
GET DIAGNOSTICS is defined by ANSI/SQL -- and SQL uses "=" for both - comparation and assignment.Belgium
E
1

Reading the Postgresql 9 documentation:

This page lists "=" as an assignment operator in the table on operator precedence.

But strangely this page (assignment operator documentation) doesn't mention it.

Enucleate answered 22/9, 2011 at 19:0 Comment(3)
That's exactly the point: The first link is the generic SQL part and = is the comparison operator in this context as shown in the example. The second link is about PL/pgSQL and there the assignment is described as :=. The question is still open. :-)Resign
I must correct myself halfway: The first link mentions "equality, assignment" in the precedence table, but in SQL context assignment means UPDATE table SET column **=** value.Resign
Yes I see your point - the doc is about SQL, not the procedural SQL wrapper language.Enucleate

© 2022 - 2024 — McMap. All rights reserved.