mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
Transform OR-clauses to SAOP's during index matching
This commit makes match_clause_to_indexcol() match "(indexkey op C1) OR (indexkey op C2) ... (indexkey op CN)" expression to the index while transforming it into "indexkey op ANY(ARRAY[C1, C2, ...])" (ScalarArrayOpExpr node). This transformation allows handling long OR-clauses with single IndexScan avoiding diving them into a slower BitmapOr. We currently restrict Ci to be either Const or Param to apply this transformation only when it's clearly beneficial. However, in the future, we might switch to a liberal understanding of constants, as it is in other cases. Discussion: https://postgr.es/m/567ED6CA.2040504%40sigaev.ru Author: Alena Rybakina, Andrey Lepikhov, Alexander Korotkov Reviewed-by: Peter Geoghegan, Ranier Vilela, Alexander Korotkov, Robert Haas Reviewed-by: Jian He, Tom Lane, Nikolay Shaplov
This commit is contained in:
@ -20,6 +20,7 @@
|
|||||||
#include "access/stratnum.h"
|
#include "access/stratnum.h"
|
||||||
#include "access/sysattr.h"
|
#include "access/sysattr.h"
|
||||||
#include "catalog/pg_am.h"
|
#include "catalog/pg_am.h"
|
||||||
|
#include "catalog/pg_amop.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_opfamily.h"
|
#include "catalog/pg_opfamily.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
@ -32,8 +33,10 @@
|
|||||||
#include "optimizer/paths.h"
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/prep.h"
|
#include "optimizer/prep.h"
|
||||||
#include "optimizer/restrictinfo.h"
|
#include "optimizer/restrictinfo.h"
|
||||||
|
#include "utils/array.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/selfuncs.h"
|
#include "utils/selfuncs.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
/* XXX see PartCollMatchesExprColl */
|
/* XXX see PartCollMatchesExprColl */
|
||||||
@ -177,6 +180,10 @@ static IndexClause *match_rowcompare_to_indexcol(PlannerInfo *root,
|
|||||||
RestrictInfo *rinfo,
|
RestrictInfo *rinfo,
|
||||||
int indexcol,
|
int indexcol,
|
||||||
IndexOptInfo *index);
|
IndexOptInfo *index);
|
||||||
|
static IndexClause *match_orclause_to_indexcol(PlannerInfo *root,
|
||||||
|
RestrictInfo *rinfo,
|
||||||
|
int indexcol,
|
||||||
|
IndexOptInfo *index);
|
||||||
static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
|
static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
|
||||||
RestrictInfo *rinfo,
|
RestrictInfo *rinfo,
|
||||||
int indexcol,
|
int indexcol,
|
||||||
@ -2149,7 +2156,10 @@ match_clause_to_index(PlannerInfo *root,
|
|||||||
* (3) must match the collation of the index, if collation is relevant.
|
* (3) must match the collation of the index, if collation is relevant.
|
||||||
*
|
*
|
||||||
* Our definition of "const" is exceedingly liberal: we allow anything that
|
* Our definition of "const" is exceedingly liberal: we allow anything that
|
||||||
* doesn't involve a volatile function or a Var of the index's relation.
|
* doesn't involve a volatile function or a Var of the index's relation
|
||||||
|
* except for a boolean OR expression input: due to a trade-off between the
|
||||||
|
* expected execution speedup and planning complexity, we limit or->saop
|
||||||
|
* transformation by obvious cases when an index scan can get a profit.
|
||||||
* In particular, Vars belonging to other relations of the query are
|
* In particular, Vars belonging to other relations of the query are
|
||||||
* accepted here, since a clause of that form can be used in a
|
* accepted here, since a clause of that form can be used in a
|
||||||
* parameterized indexscan. It's the responsibility of higher code levels
|
* parameterized indexscan. It's the responsibility of higher code levels
|
||||||
@ -2179,6 +2189,10 @@ match_clause_to_index(PlannerInfo *root,
|
|||||||
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
|
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
|
||||||
* the clause is of the form "indexkey op ANY (arrayconst)".
|
* the clause is of the form "indexkey op ANY (arrayconst)".
|
||||||
*
|
*
|
||||||
|
* It is also possible to match a list of OR clauses if it might be
|
||||||
|
* transformed into a single ScalarArrayOpExpr clause. On success,
|
||||||
|
* the returning index clause will contain a trasformed clause.
|
||||||
|
*
|
||||||
* For boolean indexes, it is also possible to match the clause directly
|
* For boolean indexes, it is also possible to match the clause directly
|
||||||
* to the indexkey; or perhaps the clause is (NOT indexkey).
|
* to the indexkey; or perhaps the clause is (NOT indexkey).
|
||||||
*
|
*
|
||||||
@ -2228,9 +2242,9 @@ match_clause_to_indexcol(PlannerInfo *root,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clause must be an opclause, funcclause, ScalarArrayOpExpr, or
|
* Clause must be an opclause, funcclause, ScalarArrayOpExpr,
|
||||||
* RowCompareExpr. Or, if the index supports it, we can handle IS
|
* RowCompareExpr, or OR-clause that could be converted to SAOP. Or, if
|
||||||
* NULL/NOT NULL clauses.
|
* the index supports it, we can handle IS NULL/NOT NULL clauses.
|
||||||
*/
|
*/
|
||||||
if (IsA(clause, OpExpr))
|
if (IsA(clause, OpExpr))
|
||||||
{
|
{
|
||||||
@ -2248,6 +2262,10 @@ match_clause_to_indexcol(PlannerInfo *root,
|
|||||||
{
|
{
|
||||||
return match_rowcompare_to_indexcol(root, rinfo, indexcol, index);
|
return match_rowcompare_to_indexcol(root, rinfo, indexcol, index);
|
||||||
}
|
}
|
||||||
|
else if (restriction_is_or_clause(rinfo))
|
||||||
|
{
|
||||||
|
return match_orclause_to_indexcol(root, rinfo, indexcol, index);
|
||||||
|
}
|
||||||
else if (index->amsearchnulls && IsA(clause, NullTest))
|
else if (index->amsearchnulls && IsA(clause, NullTest))
|
||||||
{
|
{
|
||||||
NullTest *nt = (NullTest *) clause;
|
NullTest *nt = (NullTest *) clause;
|
||||||
@ -2771,6 +2789,264 @@ match_rowcompare_to_indexcol(PlannerInfo *root,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* match_orclause_to_indexcol()
|
||||||
|
* Handles the OR-expr case for match_clause_to_indexcol() in the case
|
||||||
|
* when it could be transformed to ScalarArrayOpExpr.
|
||||||
|
*
|
||||||
|
* In this routine, we attempt to transform a list of OR-clause args into a
|
||||||
|
* single SAOP expression matching the target index column. On success,
|
||||||
|
* return an IndexClause, containing the transformed expression or NULL,
|
||||||
|
* if failed.
|
||||||
|
*/
|
||||||
|
static IndexClause *
|
||||||
|
match_orclause_to_indexcol(PlannerInfo *root,
|
||||||
|
RestrictInfo *rinfo,
|
||||||
|
int indexcol,
|
||||||
|
IndexOptInfo *index)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
BoolExpr *orclause = (BoolExpr *) rinfo->orclause;
|
||||||
|
Node *indexExpr = NULL;
|
||||||
|
List *consts = NIL;
|
||||||
|
Node *arrayNode = NULL;
|
||||||
|
ScalarArrayOpExpr *saopexpr = NULL;
|
||||||
|
Oid matchOpno = InvalidOid;
|
||||||
|
IndexClause *iclause;
|
||||||
|
Oid consttype = InvalidOid;
|
||||||
|
Oid arraytype = InvalidOid;
|
||||||
|
Oid inputcollid = InvalidOid;
|
||||||
|
bool firstTime = true;
|
||||||
|
bool haveParam = false;
|
||||||
|
|
||||||
|
Assert(IsA(orclause, BoolExpr));
|
||||||
|
Assert(orclause->boolop == OR_EXPR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to convert a list of OR-clauses to a single SAOP expression. Each
|
||||||
|
* OR entry must be in the form: (indexkey operator constant) or (constant
|
||||||
|
* operator indexkey). Operators of all the entries must match. Constant
|
||||||
|
* might be either Const or Param. To be effective, give up on the first
|
||||||
|
* non-matching entry. Exit is implemented as a break from the loop,
|
||||||
|
* which is catched afterwards.
|
||||||
|
*/
|
||||||
|
foreach(lc, orclause->args)
|
||||||
|
{
|
||||||
|
RestrictInfo *subRinfo;
|
||||||
|
OpExpr *subClause;
|
||||||
|
Oid opno;
|
||||||
|
Node *leftop,
|
||||||
|
*rightop;
|
||||||
|
Node *constExpr;
|
||||||
|
|
||||||
|
if (!IsA(lfirst(lc), RestrictInfo))
|
||||||
|
break;
|
||||||
|
|
||||||
|
subRinfo = (RestrictInfo *) lfirst(lc);
|
||||||
|
|
||||||
|
/* Only operator clauses can match */
|
||||||
|
if (!IsA(subRinfo->clause, OpExpr))
|
||||||
|
break;
|
||||||
|
|
||||||
|
subClause = (OpExpr *) subRinfo->clause;
|
||||||
|
opno = subClause->opno;
|
||||||
|
|
||||||
|
/* Only binary operators can match */
|
||||||
|
if (list_length(subClause->args) != 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The parameters below must match between sub-rinfo and its parent as
|
||||||
|
* make_restrictinfo() fills them with the same values, and further
|
||||||
|
* modifications are also the same for the whole subtree. However,
|
||||||
|
* still make a sanity check.
|
||||||
|
*/
|
||||||
|
Assert(subRinfo->is_pushed_down == rinfo->is_pushed_down);
|
||||||
|
Assert(subRinfo->is_clone == rinfo->is_clone);
|
||||||
|
Assert(subRinfo->security_level == rinfo->security_level);
|
||||||
|
Assert(bms_equal(subRinfo->incompatible_relids, rinfo->incompatible_relids));
|
||||||
|
Assert(bms_equal(subRinfo->outer_relids, rinfo->outer_relids));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also, check that required_relids in sub-rinfo is subset of parent's
|
||||||
|
* required_relids.
|
||||||
|
*/
|
||||||
|
Assert(bms_is_subset(subRinfo->required_relids, rinfo->required_relids));
|
||||||
|
|
||||||
|
/* Only the operator returning a boolean suit the transformation. */
|
||||||
|
if (get_op_rettype(opno) != BOOLOID)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for clauses of the form: (indexkey operator constant) or
|
||||||
|
* (constant operator indexkey). Determine indexkey side first, check
|
||||||
|
* the constant later.
|
||||||
|
*/
|
||||||
|
leftop = (Node *) linitial(subClause->args);
|
||||||
|
rightop = (Node *) lsecond(subClause->args);
|
||||||
|
if (match_index_to_operand(leftop, indexcol, index))
|
||||||
|
{
|
||||||
|
indexExpr = leftop;
|
||||||
|
constExpr = rightop;
|
||||||
|
}
|
||||||
|
else if (match_index_to_operand(rightop, indexcol, index))
|
||||||
|
{
|
||||||
|
opno = get_commutator(opno);
|
||||||
|
if (!OidIsValid(opno))
|
||||||
|
{
|
||||||
|
/* commutator doesn't exist, we can't reverse the order */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
indexExpr = rightop;
|
||||||
|
constExpr = leftop;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore any RelabelType node above the operands. This is needed to
|
||||||
|
* be able to apply indexscanning in binary-compatible-operator cases.
|
||||||
|
* Note: we can assume there is at most one RelabelType node;
|
||||||
|
* eval_const_expressions() will have simplified if more than one.
|
||||||
|
*/
|
||||||
|
if (IsA(constExpr, RelabelType))
|
||||||
|
constExpr = (Node *) ((RelabelType *) constExpr)->arg;
|
||||||
|
if (IsA(indexExpr, RelabelType))
|
||||||
|
indexExpr = (Node *) ((RelabelType *) indexExpr)->arg;
|
||||||
|
|
||||||
|
/* We allow constant to be Const or Param */
|
||||||
|
if (!IsA(constExpr, Const) && !IsA(constExpr, Param))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Forbid transformation for composite types, records. */
|
||||||
|
if (type_is_rowtype(exprType(constExpr)) ||
|
||||||
|
type_is_rowtype(exprType(indexExpr)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save information about the operator, type, and collation for the
|
||||||
|
* first matching qual. Then, check that subsequent quals match the
|
||||||
|
* first.
|
||||||
|
*/
|
||||||
|
if (firstTime)
|
||||||
|
{
|
||||||
|
matchOpno = opno;
|
||||||
|
consttype = exprType(constExpr);
|
||||||
|
arraytype = get_array_type(consttype);
|
||||||
|
inputcollid = subClause->inputcollid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that the operator is presented in the opfamily and that
|
||||||
|
* the expression collation matches the index collation. Also,
|
||||||
|
* there must be an array type to construct an array later.
|
||||||
|
*/
|
||||||
|
if (!IndexCollMatchesExprColl(index->indexcollations[indexcol], inputcollid) ||
|
||||||
|
!op_in_opfamily(matchOpno, index->opfamily[indexcol]) ||
|
||||||
|
!OidIsValid(arraytype))
|
||||||
|
break;
|
||||||
|
firstTime = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (opno != matchOpno ||
|
||||||
|
inputcollid != subClause->inputcollid ||
|
||||||
|
consttype != exprType(constExpr))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsA(constExpr, Param))
|
||||||
|
haveParam = true;
|
||||||
|
consts = lappend(consts, constExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Catch the break from the loop above. Normally, a foreach() loop ends
|
||||||
|
* up with a NULL list cell. A non-NULL list cell indicates a break from
|
||||||
|
* the foreach() loop. Free the consts list and return NULL then.
|
||||||
|
*/
|
||||||
|
if (lc != NULL)
|
||||||
|
{
|
||||||
|
list_free(consts);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assemble an array from the list of constants. It seems more profitable
|
||||||
|
* to build a const array. But in the presence of parameters, we don't
|
||||||
|
* have a specific value here and must employ an ArrayExpr instead.
|
||||||
|
*/
|
||||||
|
if (haveParam)
|
||||||
|
{
|
||||||
|
ArrayExpr *arrayExpr = makeNode(ArrayExpr);
|
||||||
|
|
||||||
|
/* array_collid will be set by parse_collate.c */
|
||||||
|
arrayExpr->element_typeid = consttype;
|
||||||
|
arrayExpr->array_typeid = arraytype;
|
||||||
|
arrayExpr->multidims = false;
|
||||||
|
arrayExpr->elements = consts;
|
||||||
|
arrayExpr->location = -1;
|
||||||
|
|
||||||
|
arrayNode = (Node *) arrayExpr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int16 typlen;
|
||||||
|
bool typbyval;
|
||||||
|
char typalign;
|
||||||
|
Datum *elems;
|
||||||
|
int i = 0;
|
||||||
|
ArrayType *arrayConst;
|
||||||
|
|
||||||
|
get_typlenbyvalalign(consttype, &typlen, &typbyval, &typalign);
|
||||||
|
|
||||||
|
elems = (Datum *) palloc(sizeof(Datum) * list_length(consts));
|
||||||
|
foreach_node(Const, value, consts)
|
||||||
|
{
|
||||||
|
Assert(!value->constisnull && value->constvalue);
|
||||||
|
|
||||||
|
elems[i++] = value->constvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayConst = construct_array(elems, i, consttype,
|
||||||
|
typlen, typbyval, typalign);
|
||||||
|
arrayNode = (Node *) makeConst(arraytype, -1, inputcollid,
|
||||||
|
-1, PointerGetDatum(arrayConst),
|
||||||
|
false, false);
|
||||||
|
|
||||||
|
pfree(elems);
|
||||||
|
list_free(consts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build the SAOP expression node */
|
||||||
|
saopexpr = makeNode(ScalarArrayOpExpr);
|
||||||
|
saopexpr->opno = matchOpno;
|
||||||
|
saopexpr->opfuncid = get_opcode(matchOpno);
|
||||||
|
saopexpr->hashfuncid = InvalidOid;
|
||||||
|
saopexpr->negfuncid = InvalidOid;
|
||||||
|
saopexpr->useOr = true;
|
||||||
|
saopexpr->inputcollid = inputcollid;
|
||||||
|
saopexpr->args = list_make2(indexExpr, arrayNode);
|
||||||
|
saopexpr->location = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finally, build an IndexClause based on the SAOP node. Use
|
||||||
|
* make_simple_restrictinfo() to get RestrictInfo with clean selectivity
|
||||||
|
* estimations, because they may differ from the estimation made for an OR
|
||||||
|
* clause. Although it is not a lossy expression, keep the original rinfo
|
||||||
|
* in iclause->rinfo as prescribed.
|
||||||
|
*/
|
||||||
|
iclause = makeNode(IndexClause);
|
||||||
|
iclause->rinfo = rinfo;
|
||||||
|
iclause->indexquals = list_make1(make_simple_restrictinfo(root,
|
||||||
|
&saopexpr->xpr));
|
||||||
|
iclause->lossy = false;
|
||||||
|
iclause->indexcol = indexcol;
|
||||||
|
iclause->indexcols = NIL;
|
||||||
|
return iclause;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* expand_indexqual_rowcompare --- expand a single indexqual condition
|
* expand_indexqual_rowcompare --- expand a single indexqual condition
|
||||||
* that is a RowCompareExpr
|
* that is a RowCompareExpr
|
||||||
|
@ -1845,17 +1845,10 @@ EXPLAIN (COSTS OFF)
|
|||||||
SELECT * FROM tenk1
|
SELECT * FROM tenk1
|
||||||
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
QUERY PLAN
|
QUERY PLAN
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
Bitmap Heap Scan on tenk1
|
Index Scan using tenk1_thous_tenthous on tenk1
|
||||||
Recheck Cond: (((thousand = 42) AND (tenthous = 1)) OR ((thousand = 42) AND (tenthous = 3)) OR ((thousand = 42) AND (tenthous = 42)))
|
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[])))
|
||||||
-> BitmapOr
|
(2 rows)
|
||||||
-> Bitmap Index Scan on tenk1_thous_tenthous
|
|
||||||
Index Cond: ((thousand = 42) AND (tenthous = 1))
|
|
||||||
-> Bitmap Index Scan on tenk1_thous_tenthous
|
|
||||||
Index Cond: ((thousand = 42) AND (tenthous = 3))
|
|
||||||
-> Bitmap Index Scan on tenk1_thous_tenthous
|
|
||||||
Index Cond: ((thousand = 42) AND (tenthous = 42))
|
|
||||||
(9 rows)
|
|
||||||
|
|
||||||
SELECT * FROM tenk1
|
SELECT * FROM tenk1
|
||||||
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
@ -1864,6 +1857,24 @@ SELECT * FROM tenk1
|
|||||||
42 | 5530 | 0 | 2 | 2 | 2 | 42 | 42 | 42 | 42 | 42 | 84 | 85 | QBAAAA | SEIAAA | OOOOxx
|
42 | 5530 | 0 | 2 | 2 | 2 | 42 | 42 | 42 | 42 | 42 | 84 | 85 | QBAAAA | SEIAAA | OOOOxx
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42);
|
||||||
|
QUERY PLAN
|
||||||
|
----------------------------------------------------------------------------------------
|
||||||
|
Index Scan using tenk1_thous_tenthous on tenk1
|
||||||
|
Index Cond: ((thousand = 42) AND (tenthous = ANY (ARRAY[1, (InitPlan 1).col1, 42])))
|
||||||
|
InitPlan 1
|
||||||
|
-> Result
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42);
|
||||||
|
unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
|
||||||
|
---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
|
||||||
|
42 | 5530 | 0 | 2 | 2 | 2 | 42 | 42 | 42 | 42 | 42 | 84 | 85 | QBAAAA | SEIAAA | OOOOxx
|
||||||
|
(1 row)
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM tenk1
|
SELECT count(*) FROM tenk1
|
||||||
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
@ -1875,12 +1886,9 @@ SELECT count(*) FROM tenk1
|
|||||||
-> BitmapAnd
|
-> BitmapAnd
|
||||||
-> Bitmap Index Scan on tenk1_hundred
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
Index Cond: (hundred = 42)
|
Index Cond: (hundred = 42)
|
||||||
-> BitmapOr
|
|
||||||
-> Bitmap Index Scan on tenk1_thous_tenthous
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
Index Cond: (thousand = 42)
|
Index Cond: (thousand = ANY ('{42,99}'::integer[]))
|
||||||
-> Bitmap Index Scan on tenk1_thous_tenthous
|
(8 rows)
|
||||||
Index Cond: (thousand = 99)
|
|
||||||
(11 rows)
|
|
||||||
|
|
||||||
SELECT count(*) FROM tenk1
|
SELECT count(*) FROM tenk1
|
||||||
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
@ -1889,6 +1897,238 @@ SELECT count(*) FROM tenk1
|
|||||||
10
|
10
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Index Scan using tenk1_thous_tenthous on tenk1
|
||||||
|
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3,42}'::integer[])))
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
|
unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4
|
||||||
|
---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
|
||||||
|
42 | 5530 | 0 | 2 | 2 | 2 | 42 | 42 | 42 | 42 | 42 | 84 | 85 | QBAAAA | SEIAAA | OOOOxx
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1::numeric OR tenthous = 3::int4 OR tenthous = 42::numeric);
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: (thousand = 42)
|
||||||
|
Filter: (((tenthous)::numeric = '1'::numeric) OR (tenthous = 3) OR ((tenthous)::numeric = '42'::numeric))
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 42)
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE tenthous = 1::numeric OR tenthous = 3::int4 OR tenthous = 42::numeric;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
Seq Scan on tenk1
|
||||||
|
Filter: (((tenthous)::numeric = '1'::numeric) OR (tenthous = 3) OR ((tenthous)::numeric = '42'::numeric))
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: ((hundred = 42) AND ((thousand = 42) OR (thousand = 99)))
|
||||||
|
-> BitmapAnd
|
||||||
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = ANY ('{42,99}'::integer[]))
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand);
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: ((hundred = 42) AND ((thousand < 42) OR (thousand < 99) OR (43 > thousand) OR (42 > thousand)))
|
||||||
|
-> BitmapAnd
|
||||||
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand < ANY ('{42,99,43,42}'::integer[]))
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: (((thousand = 42) AND ((tenthous = 1) OR (tenthous = 3))) OR (thousand = 41))
|
||||||
|
-> BitmapOr
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: ((thousand = 42) AND (tenthous = ANY ('{1,3}'::integer[])))
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 41)
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: (((hundred = 42) AND ((thousand = 42) OR (thousand = 99) OR (tenthous < 2))) OR (thousand = 41))
|
||||||
|
-> BitmapOr
|
||||||
|
-> BitmapAnd
|
||||||
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> BitmapOr
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 42)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 99)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (tenthous < 2)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 41)
|
||||||
|
(16 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
20
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: ((hundred = 42) AND ((thousand = 42) OR (thousand = 41) OR ((thousand = 99) AND (tenthous = 2))))
|
||||||
|
-> BitmapAnd
|
||||||
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> BitmapOr
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 42)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: (thousand = 41)
|
||||||
|
-> Bitmap Index Scan on tenk1_thous_tenthous
|
||||||
|
Index Cond: ((thousand = 99) AND (tenthous = 2))
|
||||||
|
(13 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1, tenk2
|
||||||
|
WHERE tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk1.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Nested Loop
|
||||||
|
Join Filter: ((tenk2.thousand = 42) OR (tenk1.thousand = 41) OR (tenk2.tenthous = 2))
|
||||||
|
-> Bitmap Heap Scan on tenk1
|
||||||
|
Recheck Cond: (hundred = 42)
|
||||||
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> Materialize
|
||||||
|
-> Bitmap Heap Scan on tenk2
|
||||||
|
Recheck Cond: (hundred = 42)
|
||||||
|
-> Bitmap Index Scan on tenk2_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
(12 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1, tenk2
|
||||||
|
WHERE tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk2.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Nested Loop
|
||||||
|
-> Bitmap Heap Scan on tenk2
|
||||||
|
Recheck Cond: (hundred = 42)
|
||||||
|
Filter: ((thousand = 42) OR (thousand = 41) OR (tenthous = 2))
|
||||||
|
-> Bitmap Index Scan on tenk2_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> Index Only Scan using tenk1_hundred on tenk1
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
(9 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1 JOIN tenk2 ON
|
||||||
|
tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk2.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Nested Loop
|
||||||
|
-> Bitmap Heap Scan on tenk2
|
||||||
|
Recheck Cond: (hundred = 42)
|
||||||
|
Filter: ((thousand = 42) OR (thousand = 41) OR (tenthous = 2))
|
||||||
|
-> Bitmap Index Scan on tenk2_hundred
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
-> Index Only Scan using tenk1_hundred on tenk1
|
||||||
|
Index Cond: (hundred = 42)
|
||||||
|
(9 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1 LEFT JOIN tenk2 ON
|
||||||
|
tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk2.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Nested Loop Left Join
|
||||||
|
Join Filter: (tenk1.hundred = 42)
|
||||||
|
-> Index Only Scan using tenk1_hundred on tenk1
|
||||||
|
-> Memoize
|
||||||
|
Cache Key: tenk1.hundred
|
||||||
|
Cache Mode: logical
|
||||||
|
-> Index Scan using tenk2_hundred on tenk2
|
||||||
|
Index Cond: (hundred = tenk1.hundred)
|
||||||
|
Filter: ((thousand = 42) OR (thousand = 41) OR (tenthous = 2))
|
||||||
|
(10 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Check behavior with duplicate index column contents
|
-- Check behavior with duplicate index column contents
|
||||||
--
|
--
|
||||||
|
@ -4348,15 +4348,64 @@ select * from tenk1 a join tenk1 b on
|
|||||||
Index Cond: (hundred = 4)
|
Index Cond: (hundred = 4)
|
||||||
-> Materialize
|
-> Materialize
|
||||||
-> Bitmap Heap Scan on tenk1 a
|
-> Bitmap Heap Scan on tenk1 a
|
||||||
Recheck Cond: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
|
Recheck Cond: ((unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7)))
|
||||||
|
Filter: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
|
||||||
-> BitmapOr
|
-> BitmapOr
|
||||||
-> Bitmap Index Scan on tenk1_unique1
|
-> Bitmap Index Scan on tenk1_unique1
|
||||||
Index Cond: (unique1 = 1)
|
Index Cond: (unique1 = 1)
|
||||||
-> Bitmap Index Scan on tenk1_unique2
|
-> Bitmap Index Scan on tenk1_unique2
|
||||||
Index Cond: (unique2 = 3)
|
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
|
||||||
|
(18 rows)
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from tenk1 a join tenk1 b on
|
||||||
|
(a.unique1 = 1 and b.unique1 = 2) or
|
||||||
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
||||||
|
QUERY PLAN
|
||||||
|
----------------------------------------------------------------------------------------------------------------------
|
||||||
|
Nested Loop
|
||||||
|
Join Filter: (((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4)))
|
||||||
|
-> Bitmap Heap Scan on tenk1 b
|
||||||
|
Recheck Cond: ((unique1 = 2) OR (hundred = 4))
|
||||||
|
-> BitmapOr
|
||||||
|
-> Bitmap Index Scan on tenk1_unique1
|
||||||
|
Index Cond: (unique1 = 2)
|
||||||
|
-> Bitmap Index Scan on tenk1_hundred
|
||||||
|
Index Cond: (hundred = 4)
|
||||||
|
-> Materialize
|
||||||
|
-> Bitmap Heap Scan on tenk1 a
|
||||||
|
Recheck Cond: ((unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7)))
|
||||||
|
Filter: ((unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
|
||||||
|
-> BitmapOr
|
||||||
|
-> Bitmap Index Scan on tenk1_unique1
|
||||||
|
Index Cond: (unique1 = 1)
|
||||||
-> Bitmap Index Scan on tenk1_unique2
|
-> Bitmap Index Scan on tenk1_unique2
|
||||||
Index Cond: (unique2 = 7)
|
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
|
||||||
(19 rows)
|
(18 rows)
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from tenk1 a join tenk1 b on
|
||||||
|
(a.unique1 < 20 or a.unique1 = 3 or a.unique1 = 1 and b.unique1 = 2) or
|
||||||
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Nested Loop
|
||||||
|
Join Filter: ((a.unique1 < 20) OR (a.unique1 = 3) OR ((a.unique1 = 1) AND (b.unique1 = 2)) OR (((a.unique2 = 3) OR (a.unique2 = 7)) AND (b.hundred = 4)))
|
||||||
|
-> Seq Scan on tenk1 b
|
||||||
|
-> Materialize
|
||||||
|
-> Bitmap Heap Scan on tenk1 a
|
||||||
|
Recheck Cond: ((unique1 < 20) OR (unique1 = 3) OR (unique1 = 1) OR ((unique2 = 3) OR (unique2 = 7)))
|
||||||
|
Filter: ((unique1 < 20) OR (unique1 = 3) OR (unique1 = 1) OR (unique2 = 3) OR (unique2 = 7))
|
||||||
|
-> BitmapOr
|
||||||
|
-> Bitmap Index Scan on tenk1_unique1
|
||||||
|
Index Cond: (unique1 < 20)
|
||||||
|
-> Bitmap Index Scan on tenk1_unique1
|
||||||
|
Index Cond: (unique1 = 3)
|
||||||
|
-> Bitmap Index Scan on tenk1_unique1
|
||||||
|
Index Cond: (unique1 = 1)
|
||||||
|
-> Bitmap Index Scan on tenk1_unique2
|
||||||
|
Index Cond: (unique2 = ANY ('{3,7}'::integer[]))
|
||||||
|
(16 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- test placement of movable quals in a parameterized join tree
|
-- test placement of movable quals in a parameterized join tree
|
||||||
|
@ -4494,6 +4494,13 @@ SELECT * FROM rls_tbl WHERE a <<< 1000;
|
|||||||
---
|
---
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM rls_tbl WHERE a <<< 1000 or a <<< 900;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------
|
||||||
|
Result
|
||||||
|
One-Time Filter: false
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
DROP OPERATOR <<< (int, int);
|
DROP OPERATOR <<< (int, int);
|
||||||
DROP FUNCTION op_leak(int, int);
|
DROP FUNCTION op_leak(int, int);
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
|
@ -3254,6 +3254,8 @@ CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
|||||||
restrict = scalarltsel);
|
restrict = scalarltsel);
|
||||||
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||||
ERROR: permission denied for table priv_test_tbl
|
ERROR: permission denied for table priv_test_tbl
|
||||||
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 OR b <<< 0;
|
||||||
|
ERROR: permission denied for table priv_test_tbl
|
||||||
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||||
ERROR: permission denied for table priv_test_tbl
|
ERROR: permission denied for table priv_test_tbl
|
||||||
-- Grant access via a security barrier view, but hide all data
|
-- Grant access via a security barrier view, but hide all data
|
||||||
@ -3268,6 +3270,11 @@ SELECT * FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not l
|
|||||||
---+---
|
---+---
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tststats.priv_test_view WHERE a <<< 0 OR b <<< 0; -- Should not leak
|
||||||
|
a | b
|
||||||
|
---+---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
DELETE FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
DELETE FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||||
-- Grant table access, but hide all data with RLS
|
-- Grant table access, but hide all data with RLS
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
@ -3280,6 +3287,11 @@ SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not le
|
|||||||
---+---
|
---+---
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 OR b <<< 0;
|
||||||
|
a | b
|
||||||
|
---+---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||||
-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
|
-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
|
@ -129,6 +129,37 @@ CREATE INDEX guid1_btree ON guid1 USING BTREE (guid_field);
|
|||||||
CREATE INDEX guid1_hash ON guid1 USING HASH (guid_field);
|
CREATE INDEX guid1_hash ON guid1 USING HASH (guid_field);
|
||||||
-- unique index test
|
-- unique index test
|
||||||
CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field);
|
CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field);
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT COUNT(*) FROM guid1 WHERE guid_field <> '11111111111111111111111111111111' OR
|
||||||
|
guid_field <> '3f3e3c3b-3a30-3938-3736-353433a2313e';
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Seq Scan on guid1
|
||||||
|
Filter: ((guid_field <> '11111111-1111-1111-1111-111111111111'::uuid) OR (guid_field <> '3f3e3c3b-3a30-3938-3736-353433a2313e'::uuid))
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT COUNT(*) FROM guid1 WHERE guid_field <= '22222222-2222-2222-2222-222222222222' OR
|
||||||
|
guid_field <= '11111111111111111111111111111111' OR
|
||||||
|
guid_field <= '3f3e3c3b-3a30-3938-3736-353433a2313e';
|
||||||
|
QUERY PLAN
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Seq Scan on guid1
|
||||||
|
Filter: ((guid_field <= '22222222-2222-2222-2222-222222222222'::uuid) OR (guid_field <= '11111111-1111-1111-1111-111111111111'::uuid) OR (guid_field <= '3f3e3c3b-3a30-3938-3736-353433a2313e'::uuid))
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT COUNT(*) FROM guid1 WHERE guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e' OR
|
||||||
|
guid_field = '11111111111111111111111111111111';
|
||||||
|
QUERY PLAN
|
||||||
|
----------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Seq Scan on guid1
|
||||||
|
Filter: ((guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e'::uuid) OR (guid_field = '11111111-1111-1111-1111-111111111111'::uuid))
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
-- should fail
|
-- should fail
|
||||||
INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111');
|
INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111');
|
||||||
ERROR: duplicate key value violates unique constraint "guid1_unique_btree"
|
ERROR: duplicate key value violates unique constraint "guid1_unique_btree"
|
||||||
|
@ -732,12 +732,81 @@ SELECT * FROM tenk1
|
|||||||
SELECT * FROM tenk1
|
SELECT * FROM tenk1
|
||||||
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42);
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = (SELECT 1 + 2) OR tenthous = 42);
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF)
|
EXPLAIN (COSTS OFF)
|
||||||
SELECT count(*) FROM tenk1
|
SELECT count(*) FROM tenk1
|
||||||
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
SELECT count(*) FROM tenk1
|
SELECT count(*) FROM tenk1
|
||||||
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1::numeric OR tenthous = 3::int4 OR tenthous = 42::numeric);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT * FROM tenk1
|
||||||
|
WHERE tenthous = 1::numeric OR tenthous = 3::int4 OR tenthous = 42::numeric;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand);
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand < 42 OR thousand < 99 OR 43 > thousand OR 42 > thousand);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3) OR thousand = 41;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 99 OR tenthous < 2) OR thousand = 41;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
|
||||||
|
SELECT count(*) FROM tenk1
|
||||||
|
WHERE hundred = 42 AND (thousand = 42 OR thousand = 41 OR thousand = 99 AND tenthous = 2);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1, tenk2
|
||||||
|
WHERE tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk1.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1, tenk2
|
||||||
|
WHERE tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk2.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1 JOIN tenk2 ON
|
||||||
|
tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk2.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT count(*) FROM tenk1 LEFT JOIN tenk2 ON
|
||||||
|
tenk1.hundred = 42 AND (tenk2.thousand = 42 OR tenk2.thousand = 41 OR tenk2.tenthous = 2) AND
|
||||||
|
tenk2.hundred = tenk1.hundred;
|
||||||
--
|
--
|
||||||
-- Check behavior with duplicate index column contents
|
-- Check behavior with duplicate index column contents
|
||||||
--
|
--
|
||||||
|
@ -1462,6 +1462,15 @@ select * from tenk1 a join tenk1 b on
|
|||||||
(a.unique1 = 1 and b.unique1 = 2) or
|
(a.unique1 = 1 and b.unique1 = 2) or
|
||||||
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
select * from tenk1 a join tenk1 b on
|
||||||
|
(a.unique1 = 1 and b.unique1 = 2) or
|
||||||
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
||||||
|
explain (costs off)
|
||||||
|
select * from tenk1 a join tenk1 b on
|
||||||
|
(a.unique1 < 20 or a.unique1 = 3 or a.unique1 = 1 and b.unique1 = 2) or
|
||||||
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- test placement of movable quals in a parameterized join tree
|
-- test placement of movable quals in a parameterized join tree
|
||||||
--
|
--
|
||||||
|
@ -2177,6 +2177,7 @@ CREATE FUNCTION op_leak(int, int) RETURNS bool
|
|||||||
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
||||||
restrict = scalarltsel);
|
restrict = scalarltsel);
|
||||||
SELECT * FROM rls_tbl WHERE a <<< 1000;
|
SELECT * FROM rls_tbl WHERE a <<< 1000;
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM rls_tbl WHERE a <<< 1000 or a <<< 900;
|
||||||
DROP OPERATOR <<< (int, int);
|
DROP OPERATOR <<< (int, int);
|
||||||
DROP FUNCTION op_leak(int, int);
|
DROP FUNCTION op_leak(int, int);
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
|
@ -1634,6 +1634,7 @@ CREATE FUNCTION op_leak(int, int) RETURNS bool
|
|||||||
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int,
|
||||||
restrict = scalarltsel);
|
restrict = scalarltsel);
|
||||||
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||||
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 OR b <<< 0;
|
||||||
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied
|
||||||
|
|
||||||
-- Grant access via a security barrier view, but hide all data
|
-- Grant access via a security barrier view, but hide all data
|
||||||
@ -1645,6 +1646,7 @@ GRANT SELECT, DELETE ON tststats.priv_test_view TO regress_stats_user1;
|
|||||||
-- Should now have access via the view, but see nothing and leak nothing
|
-- Should now have access via the view, but see nothing and leak nothing
|
||||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||||
SELECT * FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
SELECT * FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||||
|
SELECT * FROM tststats.priv_test_view WHERE a <<< 0 OR b <<< 0; -- Should not leak
|
||||||
DELETE FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
DELETE FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||||
|
|
||||||
-- Grant table access, but hide all data with RLS
|
-- Grant table access, but hide all data with RLS
|
||||||
@ -1655,6 +1657,7 @@ GRANT SELECT, DELETE ON tststats.priv_test_tbl TO regress_stats_user1;
|
|||||||
-- Should now have direct table access, but see nothing and leak nothing
|
-- Should now have direct table access, but see nothing and leak nothing
|
||||||
SET SESSION AUTHORIZATION regress_stats_user1;
|
SET SESSION AUTHORIZATION regress_stats_user1;
|
||||||
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||||
|
SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 OR b <<< 0;
|
||||||
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak
|
||||||
|
|
||||||
-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
|
-- privilege checks for pg_stats_ext and pg_stats_ext_exprs
|
||||||
|
@ -63,6 +63,18 @@ CREATE INDEX guid1_hash ON guid1 USING HASH (guid_field);
|
|||||||
|
|
||||||
-- unique index test
|
-- unique index test
|
||||||
CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field);
|
CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field);
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT COUNT(*) FROM guid1 WHERE guid_field <> '11111111111111111111111111111111' OR
|
||||||
|
guid_field <> '3f3e3c3b-3a30-3938-3736-353433a2313e';
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT COUNT(*) FROM guid1 WHERE guid_field <= '22222222-2222-2222-2222-222222222222' OR
|
||||||
|
guid_field <= '11111111111111111111111111111111' OR
|
||||||
|
guid_field <= '3f3e3c3b-3a30-3938-3736-353433a2313e';
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT COUNT(*) FROM guid1 WHERE guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e' OR
|
||||||
|
guid_field = '11111111111111111111111111111111';
|
||||||
|
|
||||||
-- should fail
|
-- should fail
|
||||||
INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111');
|
INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111');
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user