Postgresql – Postgres evaluates syntax error as boolean in Update statement

postgresqlupdate

Recently I came across a bug in code, equivalent to this snippet:

create table testing(num int, dt date, istrue boolean);

insert into testing values (1, '2018-01-01', True);

UPDATE testing SET num = 2, istrue = null AND dt = '2018-01-01';

The UPDATE statement should have actually been:

UPDATE testing SET num = 2, istrue = null WHERE dt = '2018-01-01';

Yet postgresql happily accepted the UPDATE as valid and it evaluated the code:

null AND dt = '2018-01-01'

after the second equal sign as a boolean and therefore updated data based on bad logic.

I have tried this in other SQL flavors and it is not allowed, they want a WHERE.

I'm not a postgres person but it does seem like a very odd syntax quirk, or is it a bug?

Best Answer

Yet postgresql happily accepted the UPDATE as valid and it evaluated the code:

null AND dt = '2018-01-01'

after the second equal sign as a boolean and therefore updated data based on bad logic.

Bad things happen where there is bad code. In this case, the code was not equivalent to the logic you wanted, and had replaced the WHERE with AND. This kind - of bad substitution - often results in errors. The rare times that it doesn't result in error you get this - bad - results where you get different/unexpected results from a query or - even catastrophic - you update the whole table where you wanted to update a few rows.

I have tried this in other SQL flavors and it is not allowed, they want a WHERE.

I don't know of any DBMS that requires WHERE in an UPDATE statement. SQL Server, Oracle, DB2, SQLite, MySQL, neither of them requires it. That is the job of the database developers, code reviewers and testing - to assure that code aligns with business logic and requirements.

I'm not a Postgres person but it does seem like a very odd syntax quirk, or is it a bug?

It's definitely not a bug. The code is parsed and executed - as you correctly assumed - as:

UPDATE 
    testing 
SET 
    num = 2, 
    istrue = ( null AND (dt = '2018-01-01') )
;

resulting in updating the whole table. istrue will be updated with a value of NULL for the rows where dt is NULL or equal to '2018-01-01' and with a value of FALSE for the rest of the rows.