]> granicus.if.org Git - postgresql/commitdiff
Fix unexpected side-effects of operator_precedence_warning.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Apr 2016 03:17:36 +0000 (23:17 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 22 Apr 2016 03:17:36 +0000 (23:17 -0400)
The implementation of that feature involves injecting nodes into the
raw parsetree where explicit parentheses appear.  Various places in
parse_expr.c that test to see "is this child node of type Foo" need to
look through such nodes, else we'll get different behavior when
operator_precedence_warning is on than when it is off.  Note that we only
need to handle this when testing untransformed child nodes, since the
AEXPR_PAREN nodes will be gone anyway after transformExprRecurse.

Per report from Scott Ribe and additional code-reading.  Back-patch
to 9.5 where this feature was added.

Report: <ED37E303-1B0A-4CD8-8E1E-B9C4C2DD9A17@elevated-dev.com>

src/backend/parser/parse_expr.c

index ebda55dfd15a07028c402ecc80dbdab743ff2646..8b285165d5f4d8c37f0165cc56ff63fa72e69beb 100644 (file)
@@ -857,6 +857,14 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
                        emit_precedence_warnings(pstate, opgroup, opname,
                                                                         lexpr, rexpr,
                                                                         a->location);
+
+               /* Look through AEXPR_PAREN nodes so they don't affect tests below */
+               while (lexpr && IsA(lexpr, A_Expr) &&
+                          ((A_Expr *) lexpr)->kind == AEXPR_PAREN)
+                       lexpr = ((A_Expr *) lexpr)->lexpr;
+               while (rexpr && IsA(rexpr, A_Expr) &&
+                          ((A_Expr *) rexpr)->kind == AEXPR_PAREN)
+                       rexpr = ((A_Expr *) rexpr)->lexpr;
        }
 
        /*
@@ -1903,6 +1911,11 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
                Node       *e = (Node *) lfirst(element);
                Node       *newe;
 
+               /* Look through AEXPR_PAREN nodes so they don't affect test below */
+               while (e && IsA(e, A_Expr) &&
+                          ((A_Expr *) e)->kind == AEXPR_PAREN)
+                       e = ((A_Expr *) e)->lexpr;
+
                /*
                 * If an element is itself an A_ArrayExpr, recurse directly so that we
                 * can pass down any target type we were given.
@@ -2453,21 +2466,32 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
 /*
  * Handle an explicit CAST construct.
  *
- * Transform the argument, then look up the type name and apply any necessary
+ * Transform the argument, look up the type name, and apply any necessary
  * coercion function(s).
  */
 static Node *
 transformTypeCast(ParseState *pstate, TypeCast *tc)
 {
        Node       *result;
+       Node       *arg = tc->arg;
        Node       *expr;
        Oid                     inputType;
        Oid                     targetType;
        int32           targetTypmod;
        int                     location;
 
+       /* Look up the type name first */
        typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod);
 
+       /*
+        * Look through any AEXPR_PAREN nodes that may have been inserted thanks
+        * to operator_precedence_warning.  Otherwise, ARRAY[]::foo[] behaves
+        * differently from (ARRAY[])::foo[].
+        */
+       while (arg && IsA(arg, A_Expr) &&
+                  ((A_Expr *) arg)->kind == AEXPR_PAREN)
+               arg = ((A_Expr *) arg)->lexpr;
+
        /*
         * If the subject of the typecast is an ARRAY[] construct and the target
         * type is an array type, we invoke transformArrayExpr() directly so that
@@ -2475,7 +2499,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
         * transformArrayExpr() might not infer the correct type.  Otherwise, just
         * transform the argument normally.
         */
-       if (IsA(tc->arg, A_ArrayExpr))
+       if (IsA(arg, A_ArrayExpr))
        {
                Oid                     targetBaseType;
                int32           targetBaseTypmod;
@@ -2493,16 +2517,16 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
                if (OidIsValid(elementType))
                {
                        expr = transformArrayExpr(pstate,
-                                                                         (A_ArrayExpr *) tc->arg,
+                                                                         (A_ArrayExpr *) arg,
                                                                          targetBaseType,
                                                                          elementType,
                                                                          targetBaseTypmod);
                }
                else
-                       expr = transformExprRecurse(pstate, tc->arg);
+                       expr = transformExprRecurse(pstate, arg);
        }
        else
-               expr = transformExprRecurse(pstate, tc->arg);
+               expr = transformExprRecurse(pstate, arg);
 
        inputType = exprType(expr);
        if (inputType == InvalidOid)