summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/pathkeys.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-03-19 20:29:08 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-03-19 20:30:08 -0400
commitb310b6e31ce5aa9e456c43c0e8e93248b0c84c02 (patch)
treee5168fcfdb231a9889e87e309f38a9e0f05a7896 /src/backend/optimizer/path/pathkeys.c
parent025f4c72f029242a6aaf3f14bb6d7da4ce070f72 (diff)
Revise collation derivation method and expression-tree representation.
All expression nodes now have an explicit output-collation field, unless they are known to only return a noncollatable data type (such as boolean or record). Also, nodes that can invoke collation-aware functions store a separate field that is the collation value to pass to the function. This avoids confusion that arises when a function has collatable inputs and noncollatable output type, or vice versa. Also, replace the parser's on-the-fly collation assignment method with a post-pass over the completed expression tree. This allows us to use a more complex (and hopefully more nearly spec-compliant) assignment rule without paying for it in extra storage in every expression node. Fix assorted bugs in the planner's handling of collations by making collation one of the defining properties of an EquivalenceClass and by converting CollateExprs into discardable RelabelType nodes during expression preprocessing.
Diffstat (limited to 'src/backend/optimizer/path/pathkeys.c')
-rw-r--r--src/backend/optimizer/path/pathkeys.c140
1 files changed, 48 insertions, 92 deletions
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index fd759281ed5..de3e4ac74e7 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -18,8 +18,6 @@
#include "postgres.h"
#include "access/skey.h"
-#include "catalog/pg_collation.h"
-#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
@@ -31,10 +29,10 @@
#include "utils/lsyscache.h"
-static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
+static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static PathKey *make_canonical_pathkey(PlannerInfo *root,
- EquivalenceClass *eclass, Oid opfamily, Oid collation,
+ EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
@@ -54,14 +52,13 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
* convenience routine to build the specified node.
*/
static PathKey *
-makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
+makePathKey(EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first)
{
PathKey *pk = makeNode(PathKey);
pk->pk_eclass = eclass;
pk->pk_opfamily = opfamily;
- pk->pk_collation = collation;
pk->pk_strategy = strategy;
pk->pk_nulls_first = nulls_first;
@@ -79,7 +76,7 @@ makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation,
*/
static PathKey *
make_canonical_pathkey(PlannerInfo *root,
- EquivalenceClass *eclass, Oid opfamily, Oid collation,
+ EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first)
{
PathKey *pk;
@@ -95,7 +92,6 @@ make_canonical_pathkey(PlannerInfo *root,
pk = (PathKey *) lfirst(lc);
if (eclass == pk->pk_eclass &&
opfamily == pk->pk_opfamily &&
- collation == pk->pk_collation &&
strategy == pk->pk_strategy &&
nulls_first == pk->pk_nulls_first)
return pk;
@@ -107,7 +103,7 @@ make_canonical_pathkey(PlannerInfo *root,
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
- pk = makePathKey(eclass, opfamily, collation, strategy, nulls_first);
+ pk = makePathKey(eclass, opfamily, strategy, nulls_first);
root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
MemoryContextSwitchTo(oldcontext);
@@ -209,7 +205,6 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
cpathkey = make_canonical_pathkey(root,
eclass,
pathkey->pk_opfamily,
- pathkey->pk_collation,
pathkey->pk_strategy,
pathkey->pk_nulls_first);
@@ -241,6 +236,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
Expr *expr,
Oid opfamily,
Oid opcintype,
+ Oid collation,
bool reverse_sort,
bool nulls_first,
Index sortref,
@@ -251,7 +247,6 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
Oid equality_op;
List *opfamilies;
EquivalenceClass *eclass;
- Oid collation;
strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
@@ -273,47 +268,21 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
elog(ERROR, "could not find opfamilies for equality operator %u",
equality_op);
- /*
- * When dealing with binary-compatible opclasses, we have to ensure that
- * the exposed type of the expression tree matches the declared input type
- * of the opclass, except when that is a polymorphic type (compare the
- * behavior of parse_coerce.c). This ensures that we can correctly match
- * the indexkey or sortclause expression to other expressions we find in
- * the query, because arguments of ordinary operator expressions will be
- * cast that way. (We have to do this for indexkeys because they are
- * represented without any explicit relabel in pg_index, and for sort
- * clauses because the parser is likewise cavalier about putting relabels
- * on them.)
- */
- if (exprType((Node *) expr) != opcintype &&
- !IsPolymorphicType(opcintype))
- {
- /* Strip any existing RelabelType, and add a new one if needed */
- while (expr && IsA(expr, RelabelType))
- expr = (Expr *) ((RelabelType *) expr)->arg;
- if (exprType((Node *) expr) != opcintype)
- expr = (Expr *) makeRelabelType(expr,
- opcintype,
- -1,
- COERCE_DONTCARE);
- }
-
/* Now find or (optionally) create a matching EquivalenceClass */
- eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies,
+ eclass = get_eclass_for_sort_expr(root, expr, opfamilies,
+ opcintype, collation,
sortref, create_it);
/* Fail if no EC and !create_it */
if (!eclass)
return NULL;
- collation = exprCollation((Node *) expr);
-
/* And finally we can find or create a PathKey node */
if (canonicalize)
- return make_canonical_pathkey(root, eclass, opfamily, collation,
+ return make_canonical_pathkey(root, eclass, opfamily,
strategy, nulls_first);
else
- return makePathKey(eclass, opfamily, collation, strategy, nulls_first);
+ return makePathKey(eclass, opfamily, strategy, nulls_first);
}
/*
@@ -333,7 +302,8 @@ make_pathkey_from_sortop(PlannerInfo *root,
bool canonicalize)
{
Oid opfamily,
- opcintype;
+ opcintype,
+ collation;
int16 strategy;
/* Find the operator in pg_amop --- failure shouldn't happen */
@@ -341,10 +311,15 @@ make_pathkey_from_sortop(PlannerInfo *root,
&opfamily, &opcintype, &strategy))
elog(ERROR, "operator %u is not a valid ordering operator",
ordering_op);
+
+ /* Because SortGroupClause doesn't carry collation, consult the expr */
+ collation = exprCollation((Node *) expr);
+
return make_pathkey_from_sortinfo(root,
expr,
opfamily,
opcintype,
+ collation,
(strategy == BTGreaterStrategyNumber),
nulls_first,
sortref,
@@ -575,6 +550,7 @@ build_index_pathkeys(PlannerInfo *root,
indexkey,
index->sortopfamily[i],
index->opcintype[i],
+ index->indexcollations[i],
reverse_sort,
nulls_first,
0,
@@ -698,8 +674,9 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
outer_ec =
get_eclass_for_sort_expr(root,
outer_expr,
- sub_member->em_datatype,
sub_eclass->ec_opfamilies,
+ sub_member->em_datatype,
+ sub_eclass->ec_collation,
0,
false);
@@ -712,7 +689,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
- sub_pathkey->pk_collation,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
}
@@ -742,23 +718,14 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
{
EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j);
Expr *sub_expr = sub_member->em_expr;
- Expr *sub_stripped;
+ Oid sub_expr_type = sub_member->em_datatype;
+ Oid sub_expr_coll = sub_eclass->ec_collation;
ListCell *k;
- /*
- * We handle two cases: the sub_pathkey key can be either an
- * exact match for a targetlist entry, or it could match after
- * stripping RelabelType nodes. (We need that case since
- * make_pathkey_from_sortinfo could add or remove
- * RelabelType.)
- */
- sub_stripped = sub_expr;
- while (sub_stripped && IsA(sub_stripped, RelabelType))
- sub_stripped = ((RelabelType *) sub_stripped)->arg;
-
foreach(k, sub_tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(k);
+ Expr *tle_expr;
Expr *outer_expr;
EquivalenceClass *outer_ec;
PathKey *outer_pk;
@@ -768,40 +735,31 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
if (tle->resjunk)
continue;
- if (equal(tle->expr, sub_expr))
- {
- /* Exact match */
- outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid, tle);
- }
- else
- {
- Expr *tle_stripped;
-
- tle_stripped = tle->expr;
- while (tle_stripped && IsA(tle_stripped, RelabelType))
- tle_stripped = ((RelabelType *) tle_stripped)->arg;
-
- if (equal(tle_stripped, sub_stripped))
- {
- /* Match after discarding RelabelType */
- outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid, tle);
- if (exprType((Node *) outer_expr) !=
- exprType((Node *) sub_expr))
- outer_expr = (Expr *)
- makeRelabelType(outer_expr,
- exprType((Node *) sub_expr),
- -1,
- COERCE_DONTCARE);
- }
- else
- continue;
- }
+ /*
+ * The targetlist entry is considered to match if it
+ * matches after sort-key canonicalization. That is
+ * needed since the sub_expr has been through the same
+ * process.
+ */
+ tle_expr = canonicalize_ec_expression(tle->expr,
+ sub_expr_type,
+ sub_expr_coll);
+ if (!equal(tle_expr, sub_expr))
+ continue;
- /* Found a representation for this sub_pathkey */
+ /*
+ * Build a representation of this targetlist entry as
+ * an outer Var.
+ */
+ outer_expr = (Expr *) makeVarFromTargetEntry(rel->relid,
+ tle);
+
+ /* See if we have a matching EC for that */
outer_ec = get_eclass_for_sort_expr(root,
outer_expr,
- sub_member->em_datatype,
sub_eclass->ec_opfamilies,
+ sub_expr_type,
+ sub_expr_coll,
0,
false);
@@ -815,7 +773,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
outer_pk = make_canonical_pathkey(root,
outer_ec,
sub_pathkey->pk_opfamily,
- sub_pathkey->pk_collation,
sub_pathkey->pk_strategy,
sub_pathkey->pk_nulls_first);
/* score = # of equivalence peers */
@@ -1024,15 +981,17 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
restrictinfo->left_ec =
get_eclass_for_sort_expr(root,
(Expr *) get_leftop(clause),
- lefttype,
restrictinfo->mergeopfamilies,
+ lefttype,
+ ((OpExpr *) clause)->inputcollid,
0,
true);
restrictinfo->right_ec =
get_eclass_for_sort_expr(root,
(Expr *) get_rightop(clause),
- righttype,
restrictinfo->mergeopfamilies,
+ righttype,
+ ((OpExpr *) clause)->inputcollid,
0,
true);
}
@@ -1337,7 +1296,6 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
pathkey = make_canonical_pathkey(root,
ec,
linitial_oid(ec->ec_opfamilies),
- DEFAULT_COLLATION_OID,
BTLessStrategyNumber,
false);
/* can't be redundant because no duplicate ECs */
@@ -1431,7 +1389,6 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
pathkey = make_canonical_pathkey(root,
ieclass,
opathkey->pk_opfamily,
- opathkey->pk_collation,
opathkey->pk_strategy,
opathkey->pk_nulls_first);
@@ -1552,7 +1509,6 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
PathKey *query_pathkey = (PathKey *) lfirst(l);
if (pathkey->pk_eclass == query_pathkey->pk_eclass &&
- pathkey->pk_collation == query_pathkey->pk_collation &&
pathkey->pk_opfamily == query_pathkey->pk_opfamily)
{
/*