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));
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,
List *rvars;
List *rnonvars;
bool useOr;
- bool haveRowExpr;
ListCell *l;
/*
/*
* 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);
/*
* 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;
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;
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),
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)
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)
(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;
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
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)
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;