Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c6b3c93

Browse files
committed
Make operator precedence follow the SQL standard more closely.
While the SQL standard is pretty vague on the overall topic of operator precedence (because it never presents a unified BNF for all expressions), it does seem reasonable to conclude from the spec for <boolean value expression> that OR has the lowest precedence, then AND, then NOT, then IS tests, then the six standard comparison operators, then everything else (since any non-boolean operator in a WHERE clause would need to be an argument of one of these). We were only sort of on board with that: most notably, while "<" ">" and "=" had properly low precedence, "<=" ">=" and "<>" were treated as generic operators and so had significantly higher precedence. And "IS" tests were even higher precedence than those, which is very clearly wrong per spec. Another problem was that "foo NOT SOMETHING bar" constructs, such as "x NOT LIKE y", were treated inconsistently because of a bison implementation artifact: they had the documented precedence with respect to operators to their right, but behaved like NOT (i.e., very low priority) with respect to operators to their left. Fixing the precedence issues is just a small matter of rearranging the precedence declarations in gram.y, except for the NOT problem, which requires adding an additional lookahead case in base_yylex() so that we can attach a different token precedence to NOT LIKE and allied two-word operators. The bulk of this patch is not the bug fix per se, but adding logic to parse_expr.c to allow giving warnings if an expression has changed meaning because of these precedence changes. These warnings are off by default and are enabled by the new GUC operator_precedence_warning. It's believed that very few applications will be affected by these changes, but it was agreed that a warning mechanism is essential to help debug any that are.
1 parent 21dcda2 commit c6b3c93

File tree

18 files changed

+723
-95
lines changed

18 files changed

+723
-95
lines changed

doc/src/sgml/config.sgml

+23
Original file line numberDiff line numberDiff line change
@@ -6816,6 +6816,29 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
68166816
</listitem>
68176817
</varlistentry>
68186818

6819+
<varlistentry id="guc-operator-precedence-warning" xreflabel="operator_precedence_warning">
6820+
<term><varname>operator_precedence_warning</varname> (<type>boolean</type>)
6821+
<indexterm>
6822+
<primary><varname>operator_precedence_warning</> configuration parameter</primary>
6823+
</indexterm>
6824+
</term>
6825+
<listitem>
6826+
<para>
6827+
When on, the parser will emit a warning for any construct that might
6828+
have changed meanings since <productname>PostgreSQL</> 9.4 as a result
6829+
of changes in operator precedence. This is useful for auditing
6830+
applications to see if precedence changes have broken anything; but it
6831+
is not meant to be kept turned on in production, since it will warn
6832+
about some perfectly valid, standard-compliant SQL code.
6833+
The default is <literal>off</>.
6834+
</para>
6835+
6836+
<para>
6837+
See <xref linkend="sql-precedence"> for more information.
6838+
</para>
6839+
</listitem>
6840+
</varlistentry>
6841+
68196842
<varlistentry id="guc-quote-all-identifiers" xreflabel="quote-all-identifiers">
68206843
<term><varname>quote_all_identifiers</varname> (<type>boolean</type>)
68216844
<indexterm>

doc/src/sgml/syntax.sgml

+40-44
Original file line numberDiff line numberDiff line change
@@ -984,10 +984,11 @@ CAST ( '<replaceable>string</replaceable>' AS <replaceable>type</replaceable> )
984984
associativity of the operators in <productname>PostgreSQL</>.
985985
Most operators have the same precedence and are left-associative.
986986
The precedence and associativity of the operators is hard-wired
987-
into the parser. This can lead to non-intuitive behavior; for
988-
example the Boolean operators <literal>&lt;</> and
989-
<literal>&gt;</> have a different precedence than the Boolean
990-
operators <literal>&lt;=</> and <literal>&gt;=</>. Also, you will
987+
into the parser.
988+
</para>
989+
990+
<para>
991+
You will
991992
sometimes need to add parentheses when using combinations of
992993
binary and unary operators. For instance:
993994
<programlisting>
@@ -1008,7 +1009,7 @@ SELECT (5 !) - 6;
10081009
</para>
10091010

10101011
<table id="sql-precedence-table">
1011-
<title>Operator Precedence (decreasing)</title>
1012+
<title>Operator Precedence (highest to lowest)</title>
10121013

10131014
<tgroup cols="3">
10141015
<thead>
@@ -1063,63 +1064,35 @@ SELECT (5 !) - 6;
10631064
</row>
10641065

10651066
<row>
1066-
<entry><token>IS</token></entry>
1067-
<entry></entry>
1068-
<entry><literal>IS TRUE</>, <literal>IS FALSE</>, <literal>IS NULL</>, etc</entry>
1069-
</row>
1070-
1071-
<row>
1072-
<entry><token>ISNULL</token></entry>
1073-
<entry></entry>
1074-
<entry>test for null</entry>
1075-
</row>
1076-
1077-
<row>
1078-
<entry><token>NOTNULL</token></entry>
1079-
<entry></entry>
1080-
<entry>test for not null</entry>
1081-
</row>
1082-
1083-
<row>
1084-
<entry>(any other)</entry>
1067+
<entry>(any other operator)</entry>
10851068
<entry>left</entry>
10861069
<entry>all other native and user-defined operators</entry>
10871070
</row>
10881071

1089-
<row>
1090-
<entry><token>IN</token></entry>
1091-
<entry></entry>
1092-
<entry>set membership</entry>
1093-
</row>
1094-
1095-
<row>
1096-
<entry><token>BETWEEN</token></entry>
1097-
<entry></entry>
1098-
<entry>range containment</entry>
1099-
</row>
1100-
11011072
<row>
11021073
<entry><token>OVERLAPS</token></entry>
11031074
<entry></entry>
11041075
<entry>time interval overlap</entry>
11051076
</row>
11061077

11071078
<row>
1108-
<entry><token>LIKE</token> <token>ILIKE</token> <token>SIMILAR</token></entry>
1079+
<entry><token>BETWEEN</token> <token>IN</token> <token>LIKE</token> <token>ILIKE</token> <token>SIMILAR</token></entry>
11091080
<entry></entry>
1110-
<entry>string pattern matching</entry>
1081+
<entry>range containment, set membership, string matching</entry>
11111082
</row>
11121083

11131084
<row>
1114-
<entry><token>&lt;</token> <token>&gt;</token></entry>
1085+
<entry><token>&lt;</token> <token>&gt;</token> <token>=</token> <token>&lt;=</token> <token>&gt;=</token> <token>&lt;&gt;</token>
1086+
</entry>
11151087
<entry></entry>
1116-
<entry>less than, greater than</entry>
1088+
<entry>comparison operators</entry>
11171089
</row>
11181090

11191091
<row>
1120-
<entry><token>=</token></entry>
1121-
<entry>right</entry>
1122-
<entry>equality, assignment</entry>
1092+
<entry><token>IS</token> <token>ISNULL</token> <token>NOTNULL</token></entry>
1093+
<entry></entry>
1094+
<entry><literal>IS TRUE</>, <literal>IS FALSE</>, <literal>IS
1095+
NULL</>, <literal>IS DISTINCT FROM</>, etc</entry>
11231096
</row>
11241097

11251098
<row>
@@ -1159,9 +1132,32 @@ SELECT (5 !) - 6;
11591132
SELECT 3 OPERATOR(pg_catalog.+) 4;
11601133
</programlisting>
11611134
the <literal>OPERATOR</> construct is taken to have the default precedence
1162-
shown in <xref linkend="sql-precedence-table"> for <quote>any other</> operator. This is true no matter
1135+
shown in <xref linkend="sql-precedence-table"> for
1136+
<quote>any other operator</>. This is true no matter
11631137
which specific operator appears inside <literal>OPERATOR()</>.
11641138
</para>
1139+
1140+
<note>
1141+
<para>
1142+
<productname>PostgreSQL</> versions before 9.5 used slightly different
1143+
operator precedence rules. In particular, <token>&lt;=</token>
1144+
<token>&gt;=</token> and <token>&lt;&gt;</token> used to be treated as
1145+
generic operators; <literal>IS</> tests used to have higher priority;
1146+
and <literal>NOT BETWEEN</> and related constructs acted inconsistently,
1147+
being taken in some cases as having the precedence of <literal>NOT</>
1148+
rather than <literal>BETWEEN</>. These rules were changed for better
1149+
compliance with the SQL standard and to reduce confusion from
1150+
inconsistent treatment of logically equivalent constructs. In most
1151+
cases, these changes will result in no behavioral change, or perhaps
1152+
in <quote>no such operator</> failures which can be resolved by adding
1153+
parentheses. However there are corner cases in which a query might
1154+
change behavior without any parsing error being reported. If you are
1155+
concerned about whether these changes have silently broken something,
1156+
you can test your application with the configuration
1157+
parameter <xref linkend="guc-operator-precedence-warning"> turned on
1158+
to see if any warnings are logged.
1159+
</para>
1160+
</note>
11651161
</sect2>
11661162
</sect1>
11671163

src/backend/nodes/outfuncs.c

+3
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,9 @@ _outAExpr(StringInfo str, const A_Expr *node)
25462546
appendStringInfoString(str, " NOT_BETWEEN_SYM ");
25472547
WRITE_NODE_FIELD(name);
25482548
break;
2549+
case AEXPR_PAREN:
2550+
appendStringInfoString(str, " PAREN");
2551+
break;
25492552
default:
25502553
appendStringInfoString(str, " ??");
25512554
break;

0 commit comments

Comments
 (0)