Re: Precedence of standard comparison operators - Mailing list pgsql-hackers
| From | Tom Lane |
|---|---|
| Subject | Re: Precedence of standard comparison operators |
| Date | |
| Msg-id | 13885.1424563144@sss.pgh.pa.us Whole thread Raw |
| In response to | Re: Precedence of standard comparison operators (Tom Lane <tgl@sss.pgh.pa.us>) |
| Responses |
Re: Precedence of standard comparison operators
Re: Precedence of standard comparison operators |
| List | pgsql-hackers |
Attached is a draft patch to bring the precedence of comparison operators
and IS tests into line with the SQL standard. I have not yet looked into
producing warnings for changes in parsing decisions; but I was gratified
to discover that this patch results in none, nada, zero changes in any
of our regression tests. So that's at least some evidence that it may
not be a huge problem in practice. Pending writing some code for warnings,
I thought I'd throw this up in case anyone wants to try it on applications
they have handy.
Credit where credit is due dept: this is mostly the work of Serge Rielau
of Salesforce.
regards, tom lane
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 4b81b08..c88e7d9 100644
*** a/doc/src/sgml/syntax.sgml
--- b/doc/src/sgml/syntax.sgml
*************** CAST ( '<replaceable>string</replaceable
*** 984,993 ****
associativity of the operators in <productname>PostgreSQL</>.
Most operators have the same precedence and are left-associative.
The precedence and associativity of the operators is hard-wired
! into the parser. This can lead to non-intuitive behavior; for
! example the Boolean operators <literal><</> and
! <literal>></> have a different precedence than the Boolean
! operators <literal><=</> and <literal>>=</>. Also, you will
sometimes need to add parentheses when using combinations of
binary and unary operators. For instance:
<programlisting>
--- 984,994 ----
associativity of the operators in <productname>PostgreSQL</>.
Most operators have the same precedence and are left-associative.
The precedence and associativity of the operators is hard-wired
! into the parser.
! </para>
!
! <para>
! You will
sometimes need to add parentheses when using combinations of
binary and unary operators. For instance:
<programlisting>
*************** SELECT (5 !) - 6;
*** 1063,1087 ****
</row>
<row>
! <entry><token>IS</token></entry>
! <entry></entry>
! <entry><literal>IS TRUE</>, <literal>IS FALSE</>, <literal>IS NULL</>, etc</entry>
! </row>
!
! <row>
! <entry><token>ISNULL</token></entry>
! <entry></entry>
! <entry>test for null</entry>
! </row>
!
! <row>
! <entry><token>NOTNULL</token></entry>
! <entry></entry>
! <entry>test for not null</entry>
! </row>
!
! <row>
! <entry>(any other)</entry>
<entry>left</entry>
<entry>all other native and user-defined operators</entry>
</row>
--- 1064,1070 ----
</row>
<row>
! <entry>(any other operator)</entry>
<entry>left</entry>
<entry>all other native and user-defined operators</entry>
</row>
*************** SELECT (5 !) - 6;
*** 1111,1125 ****
</row>
<row>
! <entry><token><</token> <token>></token></entry>
<entry></entry>
! <entry>less than, greater than</entry>
</row>
<row>
! <entry><token>=</token></entry>
! <entry>right</entry>
! <entry>equality, assignment</entry>
</row>
<row>
--- 1094,1109 ----
</row>
<row>
! <entry><token><</token> <token>></token> <token>=</token> <token><=</token> <token>>=</token>
<token><></token>
! </entry>
<entry></entry>
! <entry>comparison operators</entry>
</row>
<row>
! <entry><token>IS</token> <token>ISNULL</token> <token>NOTNULL</token></entry>
! <entry></entry>
! <entry><literal>IS TRUE</>, <literal>IS FALSE</>, <literal>IS NULL</>, etc</entry>
</row>
<row>
*************** SELECT (5 !) - 6;
*** 1159,1165 ****
SELECT 3 OPERATOR(pg_catalog.+) 4;
</programlisting>
the <literal>OPERATOR</> construct is taken to have the default precedence
! shown in <xref linkend="sql-precedence-table"> for <quote>any other</> operator. This is true no matter
which specific operator appears inside <literal>OPERATOR()</>.
</para>
</sect2>
--- 1143,1150 ----
SELECT 3 OPERATOR(pg_catalog.+) 4;
</programlisting>
the <literal>OPERATOR</> construct is taken to have the default precedence
! shown in <xref linkend="sql-precedence-table"> for
! <quote>any other operator</>. This is true no matter
which specific operator appears inside <literal>OPERATOR()</>.
</para>
</sect2>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 36dac29..f98158c 100644
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 532,537 ****
--- 532,538 ----
%token <str> IDENT FCONST SCONST BCONST XCONST Op
%token <ival> ICONST PARAM
%token TYPECAST DOT_DOT COLON_EQUALS
+ %token LESS_EQUALS GREATER_EQUALS NOT_EQUALS
/*
* If you want to make any keyword changes, update the keyword table in
*************** static Node *makeRecursiveViewSelect(cha
*** 645,652 ****
%left OR
%left AND
%right NOT
! %right '='
! %nonassoc '<' '>'
%nonassoc LIKE ILIKE SIMILAR
%nonassoc ESCAPE
%nonassoc OVERLAPS
--- 646,653 ----
%left OR
%left AND
%right NOT
! %nonassoc IS ISNULL NOTNULL /* IS sets precedence for IS NULL, etc */
! %nonassoc '<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
%nonassoc LIKE ILIKE SIMILAR
%nonassoc ESCAPE
%nonassoc OVERLAPS
*************** static Node *makeRecursiveViewSelect(cha
*** 676,684 ****
%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 '^'
--- 677,682 ----
*************** a_expr: c_expr { $$ = $1; }
*** 11202,11207 ****
--- 11200,11211 ----
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); }
| a_expr '=' a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }
+ | a_expr LESS_EQUALS a_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $3, @2); }
+ | a_expr GREATER_EQUALS a_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); }
+ | a_expr NOT_EQUALS a_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); }
| a_expr qual_Op a_expr %prec Op
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
*************** b_expr: c_expr
*** 11566,11571 ****
--- 11570,11581 ----
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); }
| b_expr '=' b_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }
+ | b_expr LESS_EQUALS b_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $3, @2); }
+ | b_expr GREATER_EQUALS b_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); }
+ | b_expr NOT_EQUALS b_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); }
| b_expr qual_Op b_expr %prec Op
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
| qual_Op b_expr %prec Op
*************** MathOp: '+' { $$ = "+"; }
*** 12485,12490 ****
--- 12495,12503 ----
| '<' { $$ = "<"; }
| '>' { $$ = ">"; }
| '=' { $$ = "="; }
+ | LESS_EQUALS { $$ = "<="; }
+ | GREATER_EQUALS { $$ = ">="; }
+ | NOT_EQUALS { $$ = "<>"; }
;
qual_Op: Op
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index a78ce03..7ce7a47 100644
*** a/src/backend/parser/scan.l
--- b/src/backend/parser/scan.l
*************** ident_cont [A-Za-z\200-\377_0-9\$]
*** 331,339 ****
--- 331,344 ----
identifier {ident_start}{ident_cont}*
+ /* Assorted special-case operators and operator-like tokens */
typecast "::"
dot_dot \.\.
colon_equals ":="
+ less_equals "<="
+ greater_equals ">="
+ less_greater "<>"
+ not_equals "!="
/*
* "self" is the set of chars that should be returned as single-character
*************** other .
*** 808,813 ****
--- 813,840 ----
return COLON_EQUALS;
}
+ {less_equals} {
+ SET_YYLLOC();
+ return LESS_EQUALS;
+ }
+
+ {greater_equals} {
+ SET_YYLLOC();
+ return GREATER_EQUALS;
+ }
+
+ {less_greater} {
+ /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
+ SET_YYLLOC();
+ return NOT_EQUALS;
+ }
+
+ {not_equals} {
+ /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
+ SET_YYLLOC();
+ return NOT_EQUALS;
+ }
+
{self} {
SET_YYLLOC();
return yytext[0];
*************** other .
*** 885,895 ****
if (nchars >= NAMEDATALEN)
yyerror("operator too long");
! /* Convert "!=" operator to "<>" for compatibility */
! if (strcmp(yytext, "!=") == 0)
! yylval->str = pstrdup("<>");
! else
! yylval->str = pstrdup(yytext);
return Op;
}
--- 912,918 ----
if (nchars >= NAMEDATALEN)
yyerror("operator too long");
! yylval->str = pstrdup(yytext);
return Op;
}
diff --git a/src/bin/psql/psqlscan.l b/src/bin/psql/psqlscan.l
index fb3fa11..a37cd2c 100644
*** a/src/bin/psql/psqlscan.l
--- b/src/bin/psql/psqlscan.l
*************** ident_cont [A-Za-z\200-\377_0-9\$]
*** 355,363 ****
--- 355,368 ----
identifier {ident_start}{ident_cont}*
+ /* Assorted special-case operators and operator-like tokens */
typecast "::"
dot_dot \.\.
colon_equals ":="
+ less_equals "<="
+ greater_equals ">="
+ less_greater "<>"
+ not_equals "!="
/*
* "self" is the set of chars that should be returned as single-character
*************** other .
*** 669,674 ****
--- 674,695 ----
ECHO;
}
+ {less_equals} {
+ ECHO;
+ }
+
+ {greater_equals} {
+ ECHO;
+ }
+
+ {less_greater} {
+ ECHO;
+ }
+
+ {not_equals} {
+ ECHO;
+ }
+
/*
* These rules are specific to psql --- they implement parenthesis
* counting and detection of command-ending semicolon. These must
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 506a313..761cfab 100644
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
*************** static void check_raise_parameters(PLp
*** 227,232 ****
--- 227,233 ----
%token <str> IDENT FCONST SCONST BCONST XCONST Op
%token <ival> ICONST PARAM
%token TYPECAST DOT_DOT COLON_EQUALS
+ %token LESS_EQUALS GREATER_EQUALS NOT_EQUALS
/*
* Other tokens recognized by plpgsql's lexer interface layer (pl_scanner.c).
pgsql-hackers by date: