]> granicus.if.org Git - postgresql/commitdiff
Remove unnecessary restrictions about RowExprs in transformAExprIn().
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 9 Jun 2013 22:39:20 +0000 (18:39 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 9 Jun 2013 22:39:20 +0000 (18:39 -0400)
When the existing code here was written, it made sense to special-case
RowExprs because that was the only way that we could handle row comparisons
at all.  Now that we have record_eq() and arrays of composites, the generic
logic for "scalar" types will in fact work on RowExprs too, so there's no
reason to throw error for combinations of RowExprs and other ways of
forming composite values, nor to ignore the possibility of using a
ScalarArrayOpExpr.  But keep using the old logic when comparing two
RowExprs, for consistency with the main transformAExprOp() logic.  (This
allows some cases with not-quite-identical rowtypes to succeed, so we might
get push-back if we removed it.)  Per bug #8198 from Rafal Rzepecki.

Back-patch to all supported branches, since this works fine as far back as
8.4.

Rafal Rzepecki and Tom Lane

src/backend/parser/parse_expr.c
src/test/regress/expected/rowtypes.out
src/test/regress/sql/rowtypes.sql

index 7f0995fae1fb75ba8beaf58920f9e6c2e3849093..06f6512c4e4205b6c55455d25eadb0cffa1838a2 100644 (file)
@@ -889,7 +889,7 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
        else if (lexpr && IsA(lexpr, RowExpr) &&
                         rexpr && IsA(rexpr, RowExpr))
        {
-               /* "row op row" */
+               /* ROW() op ROW() is handled specially */
                lexpr = transformExprRecurse(pstate, lexpr);
                rexpr = transformExprRecurse(pstate, rexpr);
                Assert(IsA(lexpr, RowExpr));
@@ -994,7 +994,7 @@ transformAExprDistinct(ParseState *pstate, A_Expr *a)
        if (lexpr && IsA(lexpr, RowExpr) &&
                rexpr && IsA(rexpr, RowExpr))
        {
-               /* "row op row" */
+               /* ROW() op ROW() is handled specially */
                return make_row_distinct_op(pstate, a->name,
                                                                        (RowExpr *) lexpr,
                                                                        (RowExpr *) rexpr,
@@ -1093,7 +1093,6 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
        List       *rvars;
        List       *rnonvars;
        bool            useOr;
-       bool            haveRowExpr;
        ListCell   *l;
 
        /*
@@ -1106,24 +1105,21 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
 
        /*
         * We try to generate a ScalarArrayOpExpr from IN/NOT IN, but this is only
-        * possible if the inputs are all scalars (no RowExprs) and there is a
-        * suitable array type available.  If not, we fall back to a boolean
-        * condition tree with multiple copies of the lefthand expression. Also,
-        * any IN-list items that contain Vars are handled as separate boolean
-        * conditions, because that gives the planner more scope for optimization
-        * on such clauses.
+        * possible if there is a suitable array type available.  If not, we fall
+        * back to a boolean condition tree with multiple copies of the lefthand
+        * expression.  Also, any IN-list items that contain Vars are handled as
+        * separate boolean conditions, because that gives the planner more scope
+        * for optimization on such clauses.
         *
-        * First step: transform all the inputs, and detect whether any are
-        * RowExprs or contain Vars.
+        * First step: transform all the inputs, and detect whether any contain
+        * Vars.
         */
        lexpr = transformExprRecurse(pstate, a->lexpr);
-       haveRowExpr = (lexpr && IsA(lexpr, RowExpr));
        rexprs = rvars = rnonvars = NIL;
        foreach(l, (List *) a->rexpr)
        {
                Node       *rexpr = transformExprRecurse(pstate, lfirst(l));
 
-               haveRowExpr |= (rexpr && IsA(rexpr, RowExpr));
                rexprs = lappend(rexprs, rexpr);
                if (contain_vars_of_level(rexpr, 0))
                        rvars = lappend(rvars, rexpr);
@@ -1133,9 +1129,9 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
 
        /*
         * ScalarArrayOpExpr is only going to be useful if there's more than one
-        * non-Var righthand item.      Also, it won't work for RowExprs.
+        * non-Var righthand item.
         */
-       if (!haveRowExpr && list_length(rnonvars) > 1)
+       if (list_length(rnonvars) > 1)
        {
                List       *allexprs;
                Oid                     scalar_type;
@@ -1151,8 +1147,13 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
                allexprs = list_concat(list_make1(lexpr), rnonvars);
                scalar_type = select_common_type(pstate, allexprs, NULL, NULL);
 
-               /* Do we have an array type to use? */
-               if (OidIsValid(scalar_type))
+               /*
+                * Do we have an array type to use?  Aside from the case where there
+                * isn't one, we don't risk using ScalarArrayOpExpr when the common
+                * type is RECORD, because the RowExpr comparison logic below can cope
+                * with some cases of non-identical row types.
+                */
+               if (OidIsValid(scalar_type) && scalar_type != RECORDOID)
                        array_type = get_array_type(scalar_type);
                else
                        array_type = InvalidOid;
@@ -1203,14 +1204,10 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
                Node       *rexpr = (Node *) lfirst(l);
                Node       *cmp;
 
-               if (haveRowExpr)
+               if (IsA(lexpr, RowExpr) &&
+                       IsA(rexpr, RowExpr))
                {
-                       if (!IsA(lexpr, RowExpr) ||
-                               !IsA(rexpr, RowExpr))
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                  errmsg("arguments of row IN must all be row expressions"),
-                                                parser_errposition(pstate, a->location)));
+                       /* ROW() op ROW() is handled specially */
                        cmp = make_row_comparison_op(pstate,
                                                                                 a->name,
                                                          (List *) copyObject(((RowExpr *) lexpr)->args),
@@ -1218,11 +1215,14 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
                                                                                 a->location);
                }
                else
+               {
+                       /* Ordinary scalar operator */
                        cmp = (Node *) make_op(pstate,
                                                                   a->name,
                                                                   copyObject(lexpr),
                                                                   rexpr,
                                                                   a->location);
+               }
 
                cmp = coerce_to_boolean(pstate, cmp, "IN");
                if (result == NULL)
index d93150aea64487512af6574a50e663dd9581577d..e3ec9cde62c5cd1132e4a8e5364a56ea930bbb38 100644 (file)
@@ -205,6 +205,25 @@ ERROR:  could not determine interpretation of row comparison operator ~~
 LINE 1: select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail;
                                 ^
 HINT:  Row comparison operators must be associated with btree operator families.
+-- Comparisons of ROW() expressions can cope with some type mismatches
+select ROW(1,2) = ROW(1,2::int8);
+ ?column? 
+----------
+ t
+(1 row)
+
+select ROW(1,2) in (ROW(3,4), ROW(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select ROW(1,2) in (ROW(3,4), ROW(1,2::int8));
+ ?column? 
+----------
+ t
+(1 row)
+
 -- Check row comparison with a subselect
 select unique1, unique2 from tenk1
 where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3)
@@ -217,6 +236,16 @@ order by 1;
 (2 rows)
 
 -- Also check row comparison with an indexable condition
+explain (costs off)
+select thousand, tenthous from tenk1
+where (thousand, tenthous) >= (997, 5000)
+order by thousand, tenthous;
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+   Index Cond: (ROW(thousand, tenthous) >= ROW(997, 5000))
+(2 rows)
+
 select thousand, tenthous from tenk1
 where (thousand, tenthous) >= (997, 5000)
 order by thousand, tenthous;
@@ -249,6 +278,26 @@ order by thousand, tenthous;
       999 |     9999
 (25 rows)
 
+-- Check row comparisons with IN
+select * from int8_tbl i8 where i8 in (row(123,456));  -- fail, type mismatch
+ERROR:  cannot compare dissimilar column types bigint and integer at record column 1
+explain (costs off)
+select * from int8_tbl i8
+where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
+                                                 QUERY PLAN                                                  
+-------------------------------------------------------------------------------------------------------------
+ Seq Scan on int8_tbl i8
+   Filter: (i8.* = ANY (ARRAY[ROW(123::bigint, 456::bigint)::int8_tbl, '(4567890123456789,123)'::int8_tbl]))
+(2 rows)
+
+select * from int8_tbl i8
+where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
+        q1        | q2  
+------------------+-----
+              123 | 456
+ 4567890123456789 | 123
+(2 rows)
+
 -- Check some corner cases involving empty rowtypes
 select ROW();
  row 
index 55e1ff9a9e92c0d0292940458af8514a9131ecd3..3e0f156945335e44a8fee474a9dea95d29fd0043 100644 (file)
@@ -95,6 +95,11 @@ select ROW('ABC','DEF') ~<=~ ROW('DEF','ABC') as true;
 select ROW('ABC','DEF') ~>=~ ROW('DEF','ABC') as false;
 select ROW('ABC','DEF') ~~ ROW('DEF','ABC') as fail;
 
+-- Comparisons of ROW() expressions can cope with some type mismatches
+select ROW(1,2) = ROW(1,2::int8);
+select ROW(1,2) in (ROW(3,4), ROW(1,2));
+select ROW(1,2) in (ROW(3,4), ROW(1,2::int8));
+
 -- Check row comparison with a subselect
 select unique1, unique2 from tenk1
 where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3)
@@ -102,10 +107,25 @@ where (unique1, unique2) < any (select ten, ten from tenk1 where hundred < 3)
 order by 1;
 
 -- Also check row comparison with an indexable condition
+explain (costs off)
+select thousand, tenthous from tenk1
+where (thousand, tenthous) >= (997, 5000)
+order by thousand, tenthous;
+
 select thousand, tenthous from tenk1
 where (thousand, tenthous) >= (997, 5000)
 order by thousand, tenthous;
 
+-- Check row comparisons with IN
+select * from int8_tbl i8 where i8 in (row(123,456));  -- fail, type mismatch
+
+explain (costs off)
+select * from int8_tbl i8
+where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
+
+select * from int8_tbl i8
+where i8 in (row(123,456)::int8_tbl, '(4567890123456789,123)');
+
 -- Check some corner cases involving empty rowtypes
 select ROW();
 select ROW() IS NULL;