Re: Precedence of standard comparison operators

From: Noah Misch <noah(at)leadboat(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: pgsql-hackers(at)postgreSQL(dot)org
Subject: Re: Precedence of standard comparison operators
Date: 2015-08-09 19:40:48
Message-ID: 20150809194048.GA1894878@tornado.leadboat.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Thu, Feb 19, 2015 at 10:48:34AM -0500, Tom Lane wrote:
> To wit, that the precedence of <= >= and <> is neither sane nor standards
> compliant.

> I claim that this behavior is contrary to spec as well as being
> unintuitive. Following the grammar productions in SQL99:

Between 1999 and 2006, SQL changed its representation of the grammar in this
area; I have appended to this message some of the key productions as of 2013.
I did not notice a semantic change, though.

> We have that right for = < > but not for the other three standard-mandated
> comparison operators. I think we should change the grammar so that all
> six act like < > do now, that is, they should have %nonassoc precedence
> just above NOT.
>
> Another thought, looking at this closely, is that we have the precedence
> of IS tests (IS NOT NULL etc) wrong as well: they should bind less tightly
> than user-defined ops, not more so.

SQL has two groups of IS tests with different precedence. The <boolean test>
productions IS [NOT] {TRUE | FALSE | UNKNOWN} have precedence just lower than
"<", and the <null predicate> productions IS [NOT] NULL have precedence equal
to "<". (An implementation giving them the same precedence can conform,
because conforming queries cannot notice the difference.)

I attempted to catalog the diverse precedence changes in commit c6b3c93:

> @@ -647,13 +654,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
> %left OR
> %left AND
> %right NOT
> -%right '='
> -%nonassoc '<' '>'
> -%nonassoc LIKE ILIKE SIMILAR
> -%nonassoc ESCAPE
> +%nonassoc IS ISNULL NOTNULL /* IS sets precedence for IS NULL, etc */
> +%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
> +%nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA
> +%nonassoc ESCAPE /* ESCAPE must be just above LIKE/ILIKE/SIMILAR */
> %nonassoc OVERLAPS
> -%nonassoc BETWEEN
> -%nonassoc IN_P
> %left POSTFIXOP /* dummy for postfix Op rules */
> /*
> * To support target_el without AS, we must give IDENT an explicit priority
> @@ -678,9 +683,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
> %nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
> %nonassoc IDENT NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING
> %left Op OPERATOR /* multi-character ops and user-defined operators */
> -%nonassoc NOTNULL
> -%nonassoc ISNULL
> -%nonassoc IS /* sets precedence for IS NULL, etc */
> %left '+' '-'
> %left '*' '/' '%'
> %left '^'

1. Decrease precedence of "<=", ">=" and "<>" to match "<".

2. Increase precedence of, for example, "BETWEEN x AND Y" to match precedence
with "BETWEEN" keyword instead of "AND" keyword. Make similar precedence
changes to other multiple-keyword productions involving "AND", "NOT", etc.

3. Decrease precedence of IS [NOT] {TRUE | FALSE | UNKNOWN} to fall between
NOT and "<".

4. Decrease precedence of IS [NOT] NULL and IS[NOT]NULL to match IS [NOT]
{TRUE | FALSE | UNKNOWN}.

5. Forbid chains of "=" (make it nonassoc), and increase its precedence to
match "<".

6. Decrease precedence of BETWEEN and IN keywords to match "LIKE".

> It's
> definitely weird that the IS tests bind more tightly than multicharacter
> Ops but less tightly than + - * /.

(1), (2) and (3) improve SQL conformance, and that last sentence seems to
explain your rationale for (4). I've been unable to explain (5) and (6). Why
in particular the following three precedence groups instead of combining them
as in SQL or subdividing further as in PostgreSQL 9.4?

> +%nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
> +%nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA

> %nonassoc OVERLAPS

Thanks,
nm

<comparison predicate> ::=
<row value predicand> <comparison predicate part 2>

<comparison predicate part 2> ::=
<comp op> <row value predicand>

<row value predicand> ::=
<row value special case>
| <row value constructor predicand>

# Syntax Rules
# 1) The declared type of a <row value special case> shall be a row type.
<row value special case> ::=
<nonparenthesized value expression primary>

# Things with precedence higher than comparison.
<row value constructor predicand> ::=
<common value expression>
| <boolean predicand>
| <explicit row value constructor>

# numeric: addition, multiplication
# string: concat, collate clause
# datetime: addition, AT TIME ZONE
# interval: addition, division
# UDT: <value expression primary>
# reference: <value expression primary>
# collection: array/multiset
<common value expression> ::=
<numeric value expression>
| <string value expression>
| <datetime value expression>
| <interval value expression>
| <user-defined type value expression>
| <reference value expression>
| <collection value expression>

<boolean predicand> ::=
<parenthesized boolean value expression>
| <nonparenthesized value expression primary>

<parenthesized boolean value expression> ::=
<left paren> <boolean value expression> <right paren>

# Things unambiguous without parens.
<nonparenthesized value expression primary> ::=
<unsigned value specification>
| <column reference>
| <set function specification>
| <window function>
| <scalar subquery>
| <case expression>
| <cast specification>
| <field reference>
| <subtype treatment>
| <method invocation>
| <static method invocation>
| <new specification>
| <attribute or method reference>
| <reference resolution>
| <collection value constructor>
| <array element reference>
| <multiset element reference>
| <next value expression>
| <routine invocation>
| <row pattern navigation operation>

<boolean value expression> ::=
<boolean term>
| <boolean value expression> OR <boolean term>

<boolean term> ::=
<boolean factor>
| <boolean term> AND <boolean factor>

<boolean factor> ::=
[ NOT ] <boolean test>

<boolean test> ::=
<boolean primary> [ IS [ NOT ] <truth value> ]

<boolean primary> ::=
<predicate>
| <boolean predicand>

<truth value> ::=
TRUE
| FALSE
| UNKNOWN

# Things with precedence equal to comparison.
<predicate> ::=
<comparison predicate>
| <between predicate>
| <in predicate>
| <like predicate>
| <similar predicate>
| <regex like predicate>
| <null predicate>
| <quantified comparison predicate>
| <exists predicate>
| <unique predicate>
| <normalized predicate>
| <match predicate>
| <overlaps predicate>
| <distinct predicate>
| <member predicate>
| <submultiset predicate>
| <set predicate>
| <type predicate>
| <period predicate>

<null predicate> ::=
<row value predicand> <null predicate part 2>

<null predicate part 2> ::=
IS [ NOT ] NULL

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2015-08-09 19:50:37 Moving SS_finalize_plan processing to the end of planning
Previous Message Peter Geoghegan 2015-08-09 19:18:57 Re: Assert in pg_stat_statements