*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.22 2007/01/05 22:19:27 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execGrouping.c,v 1.23 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* The result is a palloc'd array.
*/
FmgrInfo *
-execTuplesMatchPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx)
+execTuplesMatchPrepare(int numCols,
+ Oid *eqOperators)
{
- FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
+ FmgrInfo *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
int i;
for (i = 0; i < numCols; i++)
{
- AttrNumber att = matchColIdx[i];
- Oid typid = tupdesc->attrs[att - 1]->atttypid;
+ Oid eq_opr = eqOperators[i];
Oid eq_function;
- eq_function = equality_oper_funcid(typid);
- fmgr_info(eq_function, &eqfunctions[i]);
+ eq_function = get_opcode(eq_opr);
+ fmgr_info(eq_function, &eqFunctions[i]);
}
- return eqfunctions;
+ return eqFunctions;
}
/*
* Look up the equality and hashing functions needed for a TupleHashTable.
*
* This is similar to execTuplesMatchPrepare, but we also need to find the
- * hash functions associated with the equality operators. *eqfunctions and
- * *hashfunctions receive the palloc'd result arrays.
+ * hash functions associated with the equality operators. *eqFunctions and
+ * *hashFunctions receive the palloc'd result arrays.
*/
void
-execTuplesHashPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx,
- FmgrInfo **eqfunctions,
- FmgrInfo **hashfunctions)
+execTuplesHashPrepare(int numCols,
+ Oid *eqOperators,
+ FmgrInfo **eqFunctions,
+ FmgrInfo **hashFunctions)
{
int i;
- *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
- *hashfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
+ *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
+ *hashFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
for (i = 0; i < numCols; i++)
{
- AttrNumber att = matchColIdx[i];
- Oid typid = tupdesc->attrs[att - 1]->atttypid;
- Operator optup;
- Oid eq_opr;
+ Oid eq_opr = eqOperators[i];
Oid eq_function;
Oid hash_function;
- optup = equality_oper(typid, false);
- eq_opr = oprid(optup);
- eq_function = oprfuncid(optup);
- ReleaseSysCache(optup);
+ eq_function = get_opcode(eq_opr);
hash_function = get_op_hash_function(eq_opr);
if (!OidIsValid(hash_function)) /* should not happen */
elog(ERROR, "could not find hash function for hash operator %u",
eq_opr);
- fmgr_info(eq_function, &(*eqfunctions)[i]);
- fmgr_info(hash_function, &(*hashfunctions)[i]);
+ fmgr_info(eq_function, &(*eqFunctions)[i]);
+ fmgr_info(hash_function, &(*hashFunctions)[i]);
}
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.148 2007/01/09 02:14:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.149 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (node->numCols > 0)
{
if (node->aggstrategy == AGG_HASHED)
- execTuplesHashPrepare(ExecGetScanType(&aggstate->ss),
- node->numCols,
- node->grpColIdx,
+ execTuplesHashPrepare(node->numCols,
+ node->grpOperators,
&aggstate->eqfunctions,
&aggstate->hashfunctions);
else
aggstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
- node->numCols,
- node->grpColIdx);
+ execTuplesMatchPrepare(node->numCols,
+ node->grpOperators);
}
/*
&peraggstate->inputtypeLen,
&peraggstate->inputtypeByVal);
+ /*
+ * Look up the sorting and comparison operators to use. XXX it's
+ * pretty bletcherous to be making this sort of semantic decision
+ * in the executor. Probably the parser should decide this and
+ * record it in the Aggref node ... or at latest, do it in the
+ * planner.
+ */
eq_function = equality_oper_funcid(inputTypes[0]);
fmgr_info(eq_function, &(peraggstate->equalfn));
peraggstate->sortOperator = ordering_oper_opid(inputTypes[0]);
* locate group boundaries.
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.66 2007/01/05 22:19:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.67 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Precompute fmgr lookup data for inner loop
*/
grpstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss),
- node->numCols,
- node->grpColIdx);
+ execTuplesMatchPrepare(node->numCols,
+ node->grpOperators);
return grpstate;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.84 2007/01/05 22:19:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.85 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* the two expressions from the original clause.
*
* In addition to the expressions themselves, the planner passes the btree
- * opfamily OID and btree strategy number (BTLessStrategyNumber or
- * BTGreaterStrategyNumber) that identify the intended merge semantics for
- * each merge key. The mergejoinable operator is an equality operator in
- * this opfamily, and the two inputs are guaranteed to be ordered in either
- * increasing or decreasing (respectively) order according to this opfamily.
- * This allows us to obtain the needed comparison functions from the opfamily.
+ * opfamily OID, btree strategy number (BTLessStrategyNumber or
+ * BTGreaterStrategyNumber), and nulls-first flag that identify the intended
+ * merge semantics for each merge key. The mergejoinable operator is an
+ * equality operator in this opfamily, and the two inputs are guaranteed to be
+ * ordered in either increasing or decreasing (respectively) order according
+ * to this opfamily. This allows us to obtain the needed comparison functions
+ * from the opfamily.
*/
static MergeJoinClause
-MJExamineQuals(List *mergeclauses, List *mergefamilies, List *mergestrategies,
+MJExamineQuals(List *mergeclauses,
+ Oid *mergefamilies,
+ int *mergestrategies,
+ bool *mergenullsfirst,
PlanState *parent)
{
MergeJoinClause clauses;
int nClauses = list_length(mergeclauses);
int iClause;
ListCell *cl;
- ListCell *cf;
- ListCell *cs;
clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData));
iClause = 0;
- cf = list_head(mergefamilies);
- cs = list_head(mergestrategies);
foreach(cl, mergeclauses)
{
OpExpr *qual = (OpExpr *) lfirst(cl);
MergeJoinClause clause = &clauses[iClause];
- Oid opfamily;
- StrategyNumber opstrategy;
+ Oid opfamily = mergefamilies[iClause];
+ StrategyNumber opstrategy = mergestrategies[iClause];
+ bool nulls_first = mergenullsfirst[iClause];
int op_strategy;
Oid op_lefttype;
Oid op_righttype;
RegProcedure cmpproc;
AclResult aclresult;
- opfamily = lfirst_oid(cf);
- cf = lnext(cf);
- opstrategy = lfirst_int(cs);
- cs = lnext(cs);
-
/* Later we'll support both ascending and descending sort... */
Assert(opstrategy == BTLessStrategyNumber);
clause->cmpstrategy = MERGEFUNC_CMP;
+ Assert(!nulls_first);
if (!IsA(qual, OpExpr))
elog(ERROR, "mergejoin clause is not an OpExpr");
*/
mergestate->mj_NumClauses = list_length(node->mergeclauses);
mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
- node->mergefamilies,
- node->mergestrategies,
+ node->mergeFamilies,
+ node->mergeStrategies,
+ node->mergeNullsFirst,
(PlanState *) mergestate);
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.23 2007/01/05 22:19:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeSetOp.c,v 1.24 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Precompute fmgr lookup data for inner loop
*/
setopstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetResultType(&setopstate->ps),
- node->numCols,
- node->dupColIdx);
+ execTuplesMatchPrepare(node->numCols,
+ node->dupOperators);
return setopstate;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeUnique.c,v 1.54 2007/01/05 22:19:28 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeUnique.c,v 1.55 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Precompute fmgr lookup data for inner loop
*/
uniquestate->eqfunctions =
- execTuplesMatchPrepare(ExecGetResultType(&uniquestate->ps),
- node->numCols,
- node->uniqColIdx);
+ execTuplesMatchPrepare(node->numCols,
+ node->uniqOperators);
return uniquestate;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.360 2007/01/09 02:14:11 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.361 2007/01/10 18:06:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
_copyMergeJoin(MergeJoin *from)
{
MergeJoin *newnode = makeNode(MergeJoin);
+ int numCols;
/*
* copy node superclass fields
* copy remainder of node
*/
COPY_NODE_FIELD(mergeclauses);
- COPY_NODE_FIELD(mergefamilies);
- COPY_NODE_FIELD(mergestrategies);
+ numCols = list_length(from->mergeclauses);
+ COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
+ COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
+ COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
return newnode;
}
COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
+ COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
return newnode;
}
COPY_SCALAR_FIELD(aggstrategy);
COPY_SCALAR_FIELD(numCols);
if (from->numCols > 0)
+ {
COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
+ COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
+ }
COPY_SCALAR_FIELD(numGroups);
return newnode;
*/
COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(uniqColIdx, from->numCols * sizeof(AttrNumber));
+ COPY_POINTER_FIELD(uniqOperators, from->numCols * sizeof(Oid));
return newnode;
}
COPY_SCALAR_FIELD(cmd);
COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
+ COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid));
COPY_SCALAR_FIELD(flagColIdx);
return newnode;
COPY_BITMAPSET_FIELD(lefthand);
COPY_BITMAPSET_FIELD(righthand);
COPY_NODE_FIELD(sub_targetlist);
+ COPY_NODE_FIELD(in_operators);
return newnode;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.294 2007/01/09 02:14:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.295 2007/01/10 18:06:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COMPARE_BITMAPSET_FIELD(lefthand);
COMPARE_BITMAPSET_FIELD(righthand);
COMPARE_NODE_FIELD(sub_targetlist);
+ COMPARE_NODE_FIELD(in_operators);
return true;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.292 2007/01/09 02:14:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.293 2007/01/10 18:06:03 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
static void
_outMergeJoin(StringInfo str, MergeJoin *node)
{
+ int numCols;
+ int i;
+
WRITE_NODE_TYPE("MERGEJOIN");
_outJoinPlanInfo(str, (Join *) node);
WRITE_NODE_FIELD(mergeclauses);
- WRITE_NODE_FIELD(mergefamilies);
- WRITE_NODE_FIELD(mergestrategies);
+
+ numCols = list_length(node->mergeclauses);
+
+ appendStringInfo(str, " :mergeFamilies");
+ for (i = 0; i < numCols; i++)
+ appendStringInfo(str, " %u", node->mergeFamilies[i]);
+
+ appendStringInfo(str, " :mergeStrategies");
+ for (i = 0; i < numCols; i++)
+ appendStringInfo(str, " %d", node->mergeStrategies[i]);
+
+ appendStringInfo(str, " :mergeNullsFirst");
+ for (i = 0; i < numCols; i++)
+ appendStringInfo(str, " %d", (int) node->mergeNullsFirst[i]);
}
static void
appendStringInfo(str, " :grpColIdx");
for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %d", node->grpColIdx[i]);
+
+ appendStringInfo(str, " :grpOperators");
+ for (i = 0; i < node->numCols; i++)
+ appendStringInfo(str, " %u", node->grpOperators[i]);
}
static void
appendStringInfo(str, " :uniqColIdx");
for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %d", node->uniqColIdx[i]);
+
+ appendStringInfo(str, " :uniqOperators");
+ for (i = 0; i < node->numCols; i++)
+ appendStringInfo(str, " %u", node->uniqOperators[i]);
}
static void
for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %d", node->dupColIdx[i]);
+ appendStringInfo(str, " :dupOperators");
+ for (i = 0; i < node->numCols; i++)
+ appendStringInfo(str, " %d", node->dupOperators[i]);
+
WRITE_INT_FIELD(flagColIdx);
}
static void
_outMergePath(StringInfo str, MergePath *node)
{
+ int numCols;
+ int i;
+
WRITE_NODE_TYPE("MERGEPATH");
_outJoinPathInfo(str, (JoinPath *) node);
WRITE_NODE_FIELD(path_mergeclauses);
- WRITE_NODE_FIELD(path_mergefamilies);
- WRITE_NODE_FIELD(path_mergestrategies);
+
+ numCols = list_length(node->path_mergeclauses);
+
+ appendStringInfo(str, " :path_mergeFamilies");
+ for (i = 0; i < numCols; i++)
+ appendStringInfo(str, " %u", node->path_mergeFamilies[i]);
+
+ appendStringInfo(str, " :path_mergeStrategies");
+ for (i = 0; i < numCols; i++)
+ appendStringInfo(str, " %d", node->path_mergeStrategies[i]);
+
+ appendStringInfo(str, " :path_mergeNullsFirst");
+ for (i = 0; i < numCols; i++)
+ appendStringInfo(str, " %d", (int) node->path_mergeNullsFirst[i]);
+
WRITE_NODE_FIELD(outersortkeys);
WRITE_NODE_FIELD(innersortkeys);
}
WRITE_BITMAPSET_FIELD(lefthand);
WRITE_BITMAPSET_FIELD(righthand);
WRITE_NODE_FIELD(sub_targetlist);
+ WRITE_NODE_FIELD(in_operators);
}
static void
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.173 2007/01/08 16:09:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.174 2007/01/10 18:06:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Path *outer_path = path->jpath.outerjoinpath;
Path *inner_path = path->jpath.innerjoinpath;
List *mergeclauses = path->path_mergeclauses;
- List *mergefamilies = path->path_mergefamilies;
- List *mergestrategies = path->path_mergestrategies;
+ Oid *mergeFamilies = path->path_mergeFamilies;
+ int *mergeStrategies = path->path_mergeStrategies;
List *outersortkeys = path->outersortkeys;
List *innersortkeys = path->innersortkeys;
Cost startup_cost = 0;
firstclause = (RestrictInfo *) linitial(mergeclauses);
if (firstclause->left_mergescansel < 0) /* not computed yet? */
mergejoinscansel(root, (Node *) firstclause->clause,
- linitial_oid(mergefamilies),
- linitial_int(mergestrategies),
+ mergeFamilies[0],
+ mergeStrategies[0],
&firstclause->left_mergescansel,
&firstclause->right_mergescansel);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.109 2007/01/05 22:19:31 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.110 2007/01/10 18:06:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype);
-static void build_mergejoin_strat_lists(List *mergeclauses,
- List **mergefamilies, List **mergestrategies);
+static void build_mergejoin_strat_arrays(List *mergeclauses,
+ Oid **mergefamilies,
+ int **mergestrategies,
+ bool **mergenullsfirst);
/*
List *front_pathkey = (List *) lfirst(l);
List *cur_pathkeys;
List *cur_mergeclauses;
- List *mergefamilies;
- List *mergestrategies;
+ Oid *mergefamilies;
+ int *mergestrategies;
+ bool *mergenullsfirst;
List *outerkeys;
List *innerkeys;
List *merge_pathkeys;
outerkeys);
/* Build opfamily info for execution */
- build_mergejoin_strat_lists(cur_mergeclauses,
- &mergefamilies, &mergestrategies);
+ build_mergejoin_strat_arrays(cur_mergeclauses,
+ &mergefamilies,
+ &mergestrategies,
+ &mergenullsfirst);
/*
* And now we can make the path.
cur_mergeclauses,
mergefamilies,
mergestrategies,
+ mergenullsfirst,
outerkeys,
innerkeys));
}
Path *outerpath = (Path *) lfirst(l);
List *merge_pathkeys;
List *mergeclauses;
- List *mergefamilies;
- List *mergestrategies;
+ Oid *mergefamilies;
+ int *mergestrategies;
+ bool *mergenullsfirst;
List *innersortkeys;
List *trialsortkeys;
Path *cheapest_startup_inner;
innerrel);
/* Build opfamily info for execution */
- build_mergejoin_strat_lists(mergeclauses,
- &mergefamilies, &mergestrategies);
+ build_mergejoin_strat_arrays(mergeclauses,
+ &mergefamilies,
+ &mergestrategies,
+ &mergenullsfirst);
/*
* Generate a mergejoin on the basis of sorting the cheapest inner.
mergeclauses,
mergefamilies,
mergestrategies,
+ mergenullsfirst,
NIL,
innersortkeys));
newclauses = mergeclauses;
/* Build opfamily info for execution */
- build_mergejoin_strat_lists(newclauses,
- &mergefamilies, &mergestrategies);
+ build_mergejoin_strat_arrays(newclauses,
+ &mergefamilies,
+ &mergestrategies,
+ &mergenullsfirst);
add_path(joinrel, (Path *)
create_mergejoin_path(root,
newclauses,
mergefamilies,
mergestrategies,
+ mergenullsfirst,
NIL,
NIL));
cheapest_total_inner = innerpath;
}
/* Build opfamily info for execution */
- build_mergejoin_strat_lists(newclauses,
- &mergefamilies, &mergestrategies);
+ build_mergejoin_strat_arrays(newclauses,
+ &mergefamilies,
+ &mergestrategies,
+ &mergenullsfirst);
add_path(joinrel, (Path *)
create_mergejoin_path(root,
newclauses,
mergefamilies,
mergestrategies,
+ mergenullsfirst,
NIL,
NIL));
}
}
/*
- * Temporary hack to build opfamily and strategy lists needed for mergejoin
+ * Temporary hack to build opfamily and strategy info needed for mergejoin
* by the executor. We need to rethink the planner's handling of merge
* planning so that it can deal with multiple possible merge orders, but
* that's not done yet.
*/
static void
-build_mergejoin_strat_lists(List *mergeclauses,
- List **mergefamilies, List **mergestrategies)
+build_mergejoin_strat_arrays(List *mergeclauses,
+ Oid **mergefamilies,
+ int **mergestrategies,
+ bool **mergenullsfirst)
{
+ int nClauses = list_length(mergeclauses);
+ int i;
ListCell *l;
- *mergefamilies = NIL;
- *mergestrategies = NIL;
+ *mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
+ *mergestrategies = (int *) palloc(nClauses * sizeof(int));
+ *mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
+ i = 0;
foreach(l, mergeclauses)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
* We do not need to worry about whether the mergeclause will be
* commuted at runtime --- it's the same opfamily either way.
*/
- *mergefamilies = lappend_oid(*mergefamilies, restrictinfo->mergeopfamily);
+ (*mergefamilies)[i] = restrictinfo->mergeopfamily;
/*
* For the moment, strategy must always be LessThan --- see
* hack version of get_op_mergejoin_info
*/
- *mergestrategies = lappend_int(*mergestrategies, BTLessStrategyNumber);
+ (*mergestrategies)[i] = BTLessStrategyNumber;
+
+ /* And we only allow NULLS LAST, too */
+ (*mergenullsfirst)[i] = false;
+
+ i++;
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.220 2007/01/09 02:14:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.221 2007/01/10 18:06:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static Hash *make_hash(Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
- List *mergeclauses, List *mergefamilies, List *mergestrategies,
+ List *mergeclauses,
+ Oid *mergefamilies,
+ int *mergestrategies,
+ bool *mergenullsfirst,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
Plan *plan;
Plan *subplan;
List *uniq_exprs;
+ List *in_operators;
List *newtlist;
int nextresno;
bool newitems;
* To find the correct list of values to unique-ify, we look in the
* information saved for IN expressions. If this code is ever used in
* other scenarios, some other way of finding what to unique-ify will
- * be needed.
+ * be needed. The IN clause's operators are needed too, since they
+ * determine what the meaning of "unique" is in this context.
*----------
*/
uniq_exprs = NIL; /* just to keep compiler quiet */
+ in_operators = NIL;
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
if (bms_equal(ininfo->righthand, best_path->path.parent->relids))
{
uniq_exprs = ininfo->sub_targetlist;
+ in_operators = ininfo->in_operators;
break;
}
}
newtlist = subplan->targetlist;
numGroupCols = list_length(uniq_exprs);
groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber));
- groupColPos = 0;
+ groupColPos = 0;
foreach(l, uniq_exprs)
{
Node *uniqexpr = lfirst(l);
if (best_path->umethod == UNIQUE_PATH_HASH)
{
long numGroups;
+ Oid *groupOperators;
numGroups = (long) Min(best_path->rows, (double) LONG_MAX);
+ /*
+ * Get the (presumed hashable) equality operators for the Agg node
+ * to use. Normally these are the same as the IN clause operators,
+ * but if those are cross-type operators then the equality operators
+ * are the ones for the IN clause operators' RHS datatype.
+ */
+ groupOperators = (Oid *) palloc(numGroupCols * sizeof(Oid));
+ groupColPos = 0;
+ foreach(l, in_operators)
+ {
+ Oid in_oper = lfirst_oid(l);
+ Oid eq_oper;
+
+ eq_oper = get_compatible_hash_operator(in_oper, false);
+ if (!OidIsValid(eq_oper)) /* shouldn't happen */
+ elog(ERROR, "could not find compatible hash operator for operator %u",
+ in_oper);
+ groupOperators[groupColPos++] = eq_oper;
+ }
+
/*
* Since the Agg node is going to project anyway, we can give it the
* minimum output tlist, without any stuff we might have added to the
AGG_HASHED,
numGroupCols,
groupColIdx,
+ groupOperators,
numGroups,
0,
subplan);
{
List *sortList = NIL;
- for (groupColPos = 0; groupColPos < numGroupCols; groupColPos++)
+ /* Create an ORDER BY list to sort the input compatibly */
+ groupColPos = 0;
+ foreach(l, in_operators)
{
+ Oid in_oper = lfirst_oid(l);
+ Oid sortop;
TargetEntry *tle;
+ SortClause *sortcl;
+ sortop = get_ordering_op_for_equality_op(in_oper, false);
+ if (!OidIsValid(sortop)) /* shouldn't happen */
+ elog(ERROR, "could not find ordering operator for equality operator %u",
+ in_oper);
tle = get_tle_by_resno(subplan->targetlist,
groupColIdx[groupColPos]);
Assert(tle != NULL);
- sortList = addTargetToSortList(NULL, tle,
- sortList, subplan->targetlist,
- SORTBY_DEFAULT,
- SORTBY_NULLS_DEFAULT,
- NIL, false);
+ sortcl = makeNode(SortClause);
+ sortcl->tleSortGroupRef = assignSortGroupRef(tle,
+ subplan->targetlist);
+ sortcl->sortop = sortop;
+ sortcl->nulls_first = false;
+ sortList = lappend(sortList, sortcl);
+ groupColPos++;
}
plan = (Plan *) make_sort_from_sortclauses(root, sortList, subplan);
plan = (Plan *) make_unique(plan, sortList);
joinclauses,
otherclauses,
mergeclauses,
- best_path->path_mergefamilies,
- best_path->path_mergestrategies,
+ best_path->path_mergeFamilies,
+ best_path->path_mergeStrategies,
+ best_path->path_mergeNullsFirst,
outer_plan,
inner_plan,
best_path->jpath.jointype);
List *joinclauses,
List *otherclauses,
List *mergeclauses,
- List *mergefamilies,
- List *mergestrategies,
+ Oid *mergefamilies,
+ int *mergestrategies,
+ bool *mergenullsfirst,
Plan *lefttree,
Plan *righttree,
JoinType jointype)
plan->lefttree = lefttree;
plan->righttree = righttree;
node->mergeclauses = mergeclauses;
- node->mergefamilies = mergefamilies;
- node->mergestrategies = mergestrategies;
+ node->mergeFamilies = mergefamilies;
+ node->mergeStrategies = mergestrategies;
+ node->mergeNullsFirst = mergenullsfirst;
node->join.jointype = jointype;
node->join.joinqual = joinclauses;
* This might look like it could be merged with make_sort_from_sortclauses,
* but presently we *must* use the grpColIdx[] array to locate sort columns,
* because the child plan's tlist is not marked with ressortgroupref info
- * appropriate to the grouping node. So, only the sort direction info
+ * appropriate to the grouping node. So, only the sort ordering info
* is used from the GroupClause entries.
*/
Sort *
Agg *
make_agg(PlannerInfo *root, List *tlist, List *qual,
AggStrategy aggstrategy,
- int numGroupCols, AttrNumber *grpColIdx,
+ int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
long numGroups, int numAggs,
Plan *lefttree)
{
node->aggstrategy = aggstrategy;
node->numCols = numGroupCols;
node->grpColIdx = grpColIdx;
+ node->grpOperators = grpOperators;
node->numGroups = numGroups;
copy_plan_costsize(plan, lefttree); /* only care about copying size */
List *qual,
int numGroupCols,
AttrNumber *grpColIdx,
+ Oid *grpOperators,
double numGroups,
Plan *lefttree)
{
node->numCols = numGroupCols;
node->grpColIdx = grpColIdx;
+ node->grpOperators = grpOperators;
copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_group(&group_path, root,
/*
* distinctList is a list of SortClauses, identifying the targetlist items
- * that should be considered by the Unique filter.
+ * that should be considered by the Unique filter. The input path must
+ * already be sorted accordingly.
*/
Unique *
make_unique(Plan *lefttree, List *distinctList)
int numCols = list_length(distinctList);
int keyno = 0;
AttrNumber *uniqColIdx;
+ Oid *uniqOperators;
ListCell *slitem;
copy_plan_costsize(plan, lefttree);
plan->righttree = NULL;
/*
- * convert SortClause list into array of attr indexes, as wanted by exec
+ * convert SortClause list into arrays of attr indexes and equality
+ * operators, as wanted by executor
*/
Assert(numCols > 0);
uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+ uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols);
foreach(slitem, distinctList)
{
SortClause *sortcl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist);
- uniqColIdx[keyno++] = tle->resno;
+ uniqColIdx[keyno] = tle->resno;
+ uniqOperators[keyno] = get_equality_op_for_ordering_op(sortcl->sortop);
+ if (!OidIsValid(uniqOperators[keyno])) /* shouldn't happen */
+ elog(ERROR, "could not find equality operator for ordering operator %u",
+ sortcl->sortop);
+ keyno++;
}
node->numCols = numCols;
node->uniqColIdx = uniqColIdx;
+ node->uniqOperators = uniqOperators;
return node;
}
/*
* distinctList is a list of SortClauses, identifying the targetlist items
- * that should be considered by the SetOp filter.
+ * that should be considered by the SetOp filter. The input path must
+ * already be sorted accordingly.
*/
SetOp *
make_setop(SetOpCmd cmd, Plan *lefttree,
int numCols = list_length(distinctList);
int keyno = 0;
AttrNumber *dupColIdx;
+ Oid *dupOperators;
ListCell *slitem;
copy_plan_costsize(plan, lefttree);
plan->righttree = NULL;
/*
- * convert SortClause list into array of attr indexes, as wanted by exec
+ * convert SortClause list into arrays of attr indexes and equality
+ * operators, as wanted by executor
*/
Assert(numCols > 0);
dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+ dupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
foreach(slitem, distinctList)
{
SortClause *sortcl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist);
- dupColIdx[keyno++] = tle->resno;
+ dupColIdx[keyno] = tle->resno;
+ dupOperators[keyno] = get_equality_op_for_ordering_op(sortcl->sortop);
+ if (!OidIsValid(dupOperators[keyno])) /* shouldn't happen */
+ elog(ERROR, "could not find equality operator for ordering operator %u",
+ sortcl->sortop);
+ keyno++;
}
node->cmd = cmd;
node->numCols = numCols;
node->dupColIdx = dupColIdx;
+ node->dupOperators = dupOperators;
node->flagColIdx = flagColIdx;
return node;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.210 2007/01/05 22:19:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.211 2007/01/10 18:06:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
static double preprocess_limit(PlannerInfo *root,
double tuple_fraction,
int64 *offset_est, int64 *count_est);
+static Oid *extract_grouping_ops(List *groupClause);
static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
Path *cheapest_path, Path *sorted_path,
- double dNumGroups, AggClauseCounts *agg_counts);
-static bool hash_safe_grouping(PlannerInfo *root);
+ Oid *groupOperators, double dNumGroups,
+ AggClauseCounts *agg_counts);
static List *make_subplanTargetList(PlannerInfo *root, List *tlist,
AttrNumber **groupColIdx, bool *need_tlist_eval);
static void locate_grouping_columns(PlannerInfo *root,
List *sub_tlist;
List *group_pathkeys;
AttrNumber *groupColIdx = NULL;
+ Oid *groupOperators = NULL;
bool need_tlist_eval = true;
QualCost tlist_cost;
Path *cheapest_path;
sort_pathkeys = root->sort_pathkeys;
/*
- * If grouping, decide whether we want to use hashed grouping.
+ * If grouping, extract the grouping operators and decide whether we
+ * want to use hashed grouping.
*/
if (parse->groupClause)
{
+ groupOperators = extract_grouping_ops(parse->groupClause);
use_hashed_grouping =
choose_hashed_grouping(root, tuple_fraction,
cheapest_path, sorted_path,
- dNumGroups, &agg_counts);
+ groupOperators, dNumGroups,
+ &agg_counts);
/* Also convert # groups to long int --- but 'ware overflow! */
numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
AGG_HASHED,
numGroupCols,
groupColIdx,
+ groupOperators,
numGroups,
agg_counts.numAggs,
result_plan);
aggstrategy,
numGroupCols,
groupColIdx,
+ groupOperators,
numGroups,
agg_counts.numAggs,
result_plan);
(List *) parse->havingQual,
numGroupCols,
groupColIdx,
+ groupOperators,
dNumGroups,
result_plan);
/* The Group node won't change sort ordering */
return tuple_fraction;
}
+/*
+ * extract_grouping_ops - make an array of the equality operator OIDs
+ * for the GROUP BY clause
+ */
+static Oid *
+extract_grouping_ops(List *groupClause)
+{
+ int numCols = list_length(groupClause);
+ int colno = 0;
+ Oid *groupOperators;
+ ListCell *glitem;
+
+ groupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
+
+ foreach(glitem, groupClause)
+ {
+ GroupClause *groupcl = (GroupClause *) lfirst(glitem);
+
+ groupOperators[colno] = get_equality_op_for_ordering_op(groupcl->sortop);
+ if (!OidIsValid(groupOperators[colno])) /* shouldn't happen */
+ elog(ERROR, "could not find equality operator for ordering operator %u",
+ groupcl->sortop);
+ colno++;
+ }
+
+ return groupOperators;
+}
+
/*
* choose_hashed_grouping - should we use hashed grouping?
*/
static bool
choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
Path *cheapest_path, Path *sorted_path,
- double dNumGroups, AggClauseCounts *agg_counts)
+ Oid *groupOperators, double dNumGroups,
+ AggClauseCounts *agg_counts)
{
int numGroupCols = list_length(root->parse->groupClause);
double cheapest_path_rows;
List *current_pathkeys;
Path hashed_p;
Path sorted_p;
+ int i;
/*
* Check can't-do-it conditions, including whether the grouping operators
- * are hashjoinable.
+ * are hashjoinable. (We assume hashing is OK if they are marked
+ * oprcanhash. If there isn't actually a supporting hash function,
+ * the executor will complain at runtime.)
*
* Executor doesn't support hashed aggregation with DISTINCT aggregates.
* (Doing so would imply storing *all* the input values in the hash table,
return false;
if (agg_counts->numDistinctAggs != 0)
return false;
- if (!hash_safe_grouping(root))
- return false;
+ for (i = 0; i < numGroupCols; i++)
+ {
+ if (!op_hashjoinable(groupOperators[i]))
+ return false;
+ }
/*
* Don't do it if it doesn't look like the hashtable will fit into
return false;
}
-/*
- * hash_safe_grouping - are grouping operators hashable?
- *
- * We assume hashed aggregation will work if the datatype's equality operator
- * is marked hashjoinable.
- */
-static bool
-hash_safe_grouping(PlannerInfo *root)
-{
- ListCell *gl;
-
- foreach(gl, root->parse->groupClause)
- {
- GroupClause *grpcl = (GroupClause *) lfirst(gl);
- TargetEntry *tle = get_sortgroupclause_tle(grpcl,
- root->parse->targetList);
- Operator optup;
- bool oprcanhash;
-
- optup = equality_oper(exprType((Node *) tle->expr), true);
- if (!optup)
- return false;
- oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash;
- ReleaseSysCache(optup);
- if (!oprcanhash)
- return false;
- }
- return true;
-}
-
/*---------------
* make_subplanTargetList
* Generate appropriate target list when grouping is required.
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.116 2007/01/05 22:19:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.117 2007/01/10 18:06:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect;
+ List *in_operators;
Relids left_varnos;
int rtindex;
RangeTblEntry *rte;
RangeTblRef *rtr;
InClauseInfo *ininfo;
+ Node *result;
/*
* The sublink type must be "= ANY" --- that is, an IN operator. We
return NULL;
if (sublink->testexpr && IsA(sublink->testexpr, OpExpr))
{
+ Oid opno = ((OpExpr *) sublink->testexpr)->opno;
List *opfamilies;
List *opstrats;
- get_op_btree_interpretation(((OpExpr *) sublink->testexpr)->opno,
- &opfamilies, &opstrats);
+ get_op_btree_interpretation(opno, &opfamilies, &opstrats);
if (!list_member_int(opstrats, ROWCOMPARE_EQ))
return NULL;
+ in_operators = list_make1_oid(opno);
+ }
+ else if (and_clause(sublink->testexpr))
+ {
+ ListCell *lc;
+
+ /* OK, but we need to extract the per-column operator OIDs */
+ in_operators = NIL;
+ foreach(lc, ((BoolExpr *) sublink->testexpr)->args)
+ {
+ OpExpr *op = (OpExpr *) lfirst(lc);
+
+ if (!IsA(op, OpExpr)) /* probably shouldn't happen */
+ return NULL;
+ in_operators = lappend_oid(in_operators, op->opno);
+ }
}
- else if (!and_clause(sublink->testexpr))
+ else
return NULL;
/*
ininfo = makeNode(InClauseInfo);
ininfo->lefthand = left_varnos;
ininfo->righthand = bms_make_singleton(rtindex);
- root->in_info_list = lappend(root->in_info_list, ininfo);
+ ininfo->in_operators = in_operators;
/*
* Build the result qual expression. As a side effect,
* ininfo->sub_targetlist is filled with a list of Vars representing the
* subselect outputs.
*/
- return convert_testexpr(sublink->testexpr,
- rtindex,
- &ininfo->sub_targetlist);
+ result = convert_testexpr(sublink->testexpr,
+ rtindex,
+ &ininfo->sub_targetlist);
+
+ Assert(list_length(in_operators) == list_length(ininfo->sub_targetlist));
+
+ /* Add the completed node to the query's list */
+ root->in_info_list = lappend(root->in_info_list, ininfo);
+
+ return result;
}
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.228 2007/01/09 02:14:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.229 2007/01/10 18:06:04 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
FLATCOPY(newnode, ininfo, InClauseInfo);
MUTATE(newnode->sub_targetlist, ininfo->sub_targetlist, List *);
+ /* Assume we need not make a copy of in_operators list */
return (Node *) newnode;
}
break;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.135 2007/01/05 22:19:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.136 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parsetree.h"
#include "utils/memutils.h"
#include "utils/selfuncs.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
static List *translate_sub_tlist(List *tlist, int relid);
-static bool query_is_distinct_for(Query *query, List *colnos);
-static bool hash_safe_tlist(List *tlist);
+static bool query_is_distinct_for(Query *query, List *colnos, List *opids);
+static Oid distinct_col_search(int colno, List *colnos, List *opids);
+static bool hash_safe_operators(List *opids);
/*****************************************************************************
Path agg_path; /* dummy for result of cost_agg */
MemoryContext oldcontext;
List *sub_targetlist;
+ List *in_operators;
ListCell *l;
int numCols;
/*
* Try to identify the targetlist that will actually be unique-ified. In
* current usage, this routine is only used for sub-selects of IN clauses,
- * so we should be able to find the tlist in in_info_list.
+ * so we should be able to find the tlist in in_info_list. Get the IN
+ * clause's operators, too, because they determine what "unique" means.
*/
sub_targetlist = NIL;
+ in_operators = NIL;
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
if (bms_equal(ininfo->righthand, rel->relids))
{
sub_targetlist = ininfo->sub_targetlist;
+ in_operators = ininfo->in_operators;
break;
}
}
sub_tlist_colnos = translate_sub_tlist(sub_targetlist, rel->relid);
if (sub_tlist_colnos &&
- query_is_distinct_for(rte->subquery, sub_tlist_colnos))
+ query_is_distinct_for(rte->subquery,
+ sub_tlist_colnos, in_operators))
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->rows = rel->rows;
/*
* Is it safe to use a hashed implementation? If so, estimate and compare
- * costs. We only try this if we know the targetlist for sure (else we
- * can't be sure about the datatypes involved).
+ * costs. We only try this if we know the IN operators, else we can't
+ * check their hashability.
*/
pathnode->umethod = UNIQUE_PATH_SORT;
- if (enable_hashagg && sub_targetlist && hash_safe_tlist(sub_targetlist))
+ if (enable_hashagg && in_operators && hash_safe_operators(in_operators))
{
/*
* Estimate the overhead per hashtable entry at 64 bytes (same as in
*
* colnos is an integer list of output column numbers (resno's). We are
* interested in whether rows consisting of just these columns are certain
- * to be distinct.
+ * to be distinct. "Distinctness" is defined according to whether the
+ * corresponding upper-level equality operators listed in opids would think
+ * the values are distinct. (Note: the opids entries could be cross-type
+ * operators, and thus not exactly the equality operators that the subquery
+ * would use itself. We assume that the subquery is compatible if these
+ * operators appear in the same btree opfamily as the ones the subquery uses.)
*/
static bool
-query_is_distinct_for(Query *query, List *colnos)
+query_is_distinct_for(Query *query, List *colnos, List *opids)
{
ListCell *l;
+ Oid opid;
+
+ Assert(list_length(colnos) == list_length(opids));
/*
* DISTINCT (including DISTINCT ON) guarantees uniqueness if all the
- * columns in the DISTINCT clause appear in colnos.
+ * columns in the DISTINCT clause appear in colnos and operator
+ * semantics match.
*/
if (query->distinctClause)
{
TargetEntry *tle = get_sortgroupclause_tle(scl,
query->targetList);
- if (!list_member_int(colnos, tle->resno))
+ opid = distinct_col_search(tle->resno, colnos, opids);
+ if (!OidIsValid(opid) ||
+ !ops_in_same_btree_opfamily(opid, scl->sortop))
break; /* exit early if no match */
}
if (l == NULL) /* had matches for all? */
/*
* Similarly, GROUP BY guarantees uniqueness if all the grouped columns
- * appear in colnos.
+ * appear in colnos and operator semantics match.
*/
if (query->groupClause)
{
TargetEntry *tle = get_sortgroupclause_tle(grpcl,
query->targetList);
- if (!list_member_int(colnos, tle->resno))
+ opid = distinct_col_search(tle->resno, colnos, opids);
+ if (!OidIsValid(opid) ||
+ !ops_in_same_btree_opfamily(opid, grpcl->sortop))
break; /* exit early if no match */
}
if (l == NULL) /* had matches for all? */
{
/*
* If we have no GROUP BY, but do have aggregates or HAVING, then the
- * result is at most one row so it's surely unique.
+ * result is at most one row so it's surely unique, for any operators.
*/
if (query->hasAggs || query->havingQual)
return true;
/*
* UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row,
- * except with ALL
+ * except with ALL.
+ *
+ * XXX this code knows that prepunion.c will adopt the default ordering
+ * operator for each column datatype as the sortop. It'd probably be
+ * better if these operators were chosen at parse time and stored into
+ * the parsetree, instead of leaving bits of the planner to decide
+ * semantics.
*/
if (query->setOperations)
{
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
- if (!tle->resjunk &&
- !list_member_int(colnos, tle->resno))
+ if (tle->resjunk)
+ continue; /* ignore resjunk columns */
+
+ opid = distinct_col_search(tle->resno, colnos, opids);
+ if (!OidIsValid(opid) ||
+ !ops_in_same_btree_opfamily(opid,
+ ordering_oper_opid(exprType((Node *) tle->expr))))
break; /* exit early if no match */
}
if (l == NULL) /* had matches for all? */
}
/*
- * hash_safe_tlist - can datatypes of given tlist be hashed?
+ * distinct_col_search - subroutine for query_is_distinct_for
*
- * We assume hashed aggregation will work if the datatype's equality operator
- * is marked hashjoinable.
+ * If colno is in colnos, return the corresponding element of opids,
+ * else return InvalidOid. (We expect colnos does not contain duplicates,
+ * so the result is well-defined.)
+ */
+static Oid
+distinct_col_search(int colno, List *colnos, List *opids)
+{
+ ListCell *lc1,
+ *lc2;
+
+ forboth(lc1, colnos, lc2, opids)
+ {
+ if (colno == lfirst_int(lc1))
+ return lfirst_oid(lc2);
+ }
+ return InvalidOid;
+}
+
+/*
+ * hash_safe_operators - can all the specified IN operators be hashed?
*
- * XXX this probably should be somewhere else. See also hash_safe_grouping
- * in plan/planner.c.
+ * We assume hashed aggregation will work if each IN operator is marked
+ * hashjoinable. If the IN operators are cross-type, this could conceivably
+ * fail: the aggregation will need a hashable equality operator for the RHS
+ * datatype --- but it's pretty hard to conceive of a hash opclass that has
+ * cross-type hashing without support for hashing the individual types, so
+ * we don't expend cycles here to support the case. We could check
+ * get_compatible_hash_operator() instead of just op_hashjoinable(), but the
+ * former is a significantly more expensive test.
*/
static bool
-hash_safe_tlist(List *tlist)
+hash_safe_operators(List *opids)
{
- ListCell *tl;
+ ListCell *lc;
- foreach(tl, tlist)
+ foreach(lc, opids)
{
- Node *expr = (Node *) lfirst(tl);
- Operator optup;
- bool oprcanhash;
-
- optup = equality_oper(exprType(expr), true);
- if (!optup)
- return false;
- oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash;
- ReleaseSysCache(optup);
- if (!oprcanhash)
+ if (!op_hashjoinable(lfirst_oid(lc)))
return false;
}
return true;
* ordering for each merge clause
* 'mergestrategies' are the btree operator strategies identifying the merge
* ordering for each merge clause
+ * 'mergenullsfirst' are the nulls first/last flags for each merge clause
* 'outersortkeys' are the sort varkeys for the outer relation
* 'innersortkeys' are the sort varkeys for the inner relation
*/
List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
- List *mergefamilies,
- List *mergestrategies,
+ Oid *mergefamilies,
+ int *mergestrategies,
+ bool *mergenullsfirst,
List *outersortkeys,
List *innersortkeys)
{
pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->path_mergeclauses = mergeclauses;
- pathnode->path_mergefamilies = mergefamilies;
- pathnode->path_mergestrategies = mergestrategies;
+ pathnode->path_mergeFamilies = mergefamilies;
+ pathnode->path_mergeStrategies = mergestrategies;
+ pathnode->path_mergeNullsFirst = mergenullsfirst;
pathnode->outersortkeys = outersortkeys;
pathnode->innersortkeys = innersortkeys;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.162 2007/01/09 02:14:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.163 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Verify it's a valid ordering operator, and determine
* whether to consider it like ASC or DESC.
*/
- if (!get_op_compare_function(sortop, &cmpfunc, &reverse))
+ if (!get_compare_function_for_ordering_op(sortop,
+ &cmpfunc, &reverse))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("operator %s is not a valid ordering operator",
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.142 2007/01/09 02:14:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.143 2007/01/10 18:06:04 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
#endif
/*
- * get_op_compare_function
+ * get_compare_function_for_ordering_op
* Get the OID of the datatype-specific btree comparison function
* associated with an ordering operator (a "<" or ">" operator).
*
* (This indicates that the operator is not a valid ordering operator.)
*/
bool
-get_op_compare_function(Oid opno, Oid *cmpfunc, bool *reverse)
+get_compare_function_for_ordering_op(Oid opno, Oid *cmpfunc, bool *reverse)
{
bool result = false;
CatCList *catlist;
return result;
}
+/*
+ * get_equality_op_for_ordering_op
+ * Get the OID of the datatype-specific btree equality operator
+ * associated with an ordering operator (a "<" or ">" operator).
+ *
+ * Returns InvalidOid if no matching equality operator can be found.
+ * (This indicates that the operator is not a valid ordering operator.)
+ */
+Oid
+get_equality_op_for_ordering_op(Oid opno)
+{
+ Oid result = InvalidOid;
+ CatCList *catlist;
+ int i;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "<"
+ * or ">" operator of any btree opfamily. This is exactly like
+ * get_compare_function_for_ordering_op except we don't care whether the
+ * ordering op is "<" or ">" ... the equality operator will be the same
+ * either way.
+ */
+ catlist = SearchSysCacheList(AMOPOPID, 1,
+ ObjectIdGetDatum(opno),
+ 0, 0, 0);
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ /* must be btree */
+ if (aform->amopmethod != BTREE_AM_OID)
+ continue;
+
+ if (aform->amopstrategy == BTLessStrategyNumber ||
+ aform->amopstrategy == BTGreaterStrategyNumber)
+ {
+ /* Found a suitable opfamily, get matching equality operator */
+ result = get_opfamily_member(aform->amopfamily,
+ aform->amoplefttype,
+ aform->amoprighttype,
+ BTEqualStrategyNumber);
+ if (OidIsValid(result))
+ break;
+ /* failure probably shouldn't happen, but keep looking if so */
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+/*
+ * get_ordering_op_for_equality_op
+ * Get the OID of a datatype-specific btree ordering operator
+ * associated with an equality operator. (If there are multiple
+ * possibilities, assume any one will do.)
+ *
+ * This function is used when we have to sort data before unique-ifying,
+ * and don't much care which sorting op is used as long as it's compatible
+ * with the intended equality operator. Since we need a sorting operator,
+ * it should be single-data-type even if the given operator is cross-type.
+ * The caller specifies whether to find an op for the LHS or RHS data type.
+ *
+ * Returns InvalidOid if no matching ordering operator can be found.
+ */
+Oid
+get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
+{
+ Oid result = InvalidOid;
+ CatCList *catlist;
+ int i;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "="
+ * operator of any btree opfamily.
+ */
+ catlist = SearchSysCacheList(AMOPOPID, 1,
+ ObjectIdGetDatum(opno),
+ 0, 0, 0);
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ /* must be btree */
+ if (aform->amopmethod != BTREE_AM_OID)
+ continue;
+
+ if (aform->amopstrategy == BTEqualStrategyNumber)
+ {
+ /* Found a suitable opfamily, get matching ordering operator */
+ Oid typid;
+
+ typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
+ result = get_opfamily_member(aform->amopfamily,
+ typid, typid,
+ BTLessStrategyNumber);
+ if (OidIsValid(result))
+ break;
+ /* failure probably shouldn't happen, but keep looking if so */
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
+/*
+ * get_compatible_hash_operator
+ * Get the OID of a hash equality operator compatible with the given
+ * operator, but operating on its LHS or RHS datatype as specified.
+ *
+ * If the given operator is not cross-type, the result should be the same
+ * operator, but in cross-type situations it is different.
+ *
+ * Returns InvalidOid if no compatible operator can be found. (This indicates
+ * that the operator should not have been marked oprcanhash.)
+ */
+Oid
+get_compatible_hash_operator(Oid opno, bool use_lhs_type)
+{
+ Oid result = InvalidOid;
+ CatCList *catlist;
+ int i;
+
+ /*
+ * Search pg_amop to see if the target operator is registered as the "="
+ * operator of any hash opfamily. If the operator is registered in
+ * multiple opfamilies, assume we can use any one.
+ */
+ catlist = SearchSysCacheList(AMOPOPID, 1,
+ ObjectIdGetDatum(opno),
+ 0, 0, 0);
+
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple tuple = &catlist->members[i]->tuple;
+ Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+ if (aform->amopmethod == HASH_AM_OID &&
+ aform->amopstrategy == HTEqualStrategyNumber)
+ {
+ /* Found a suitable opfamily, get matching single-type operator */
+ Oid typid;
+
+ /* No extra lookup needed if given operator is single-type */
+ if (aform->amoplefttype == aform->amoprighttype)
+ {
+ result = opno;
+ break;
+ }
+ typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
+ result = get_opfamily_member(aform->amopfamily,
+ typid, typid,
+ HTEqualStrategyNumber);
+ if (OidIsValid(result))
+ break;
+ /* failure probably shouldn't happen, but keep looking if so */
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
/*
* get_op_hash_function
* Get the OID of the datatype-specific hash function associated with
ReleaseSysCacheList(catlist);
}
+/*
+ * ops_in_same_btree_opfamily
+ * Return TRUE if there exists a btree opfamily containing both operators.
+ * (This implies that they have compatible notions of equality.)
+ */
+bool
+ops_in_same_btree_opfamily(Oid opno1, Oid opno2)
+{
+ bool result = false;
+ CatCList *catlist;
+ int i;
+
+ /*
+ * We search through all the pg_amop entries for opno1.
+ */
+ catlist = SearchSysCacheList(AMOPOPID, 1,
+ ObjectIdGetDatum(opno1),
+ 0, 0, 0);
+ for (i = 0; i < catlist->n_members; i++)
+ {
+ HeapTuple op_tuple = &catlist->members[i]->tuple;
+ Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple);
+
+ /* must be btree */
+ if (op_form->amopmethod != BTREE_AM_OID)
+ continue;
+
+ if (op_in_opfamily(opno2, op_form->amopfamily))
+ {
+ result = true;
+ break;
+ }
+ }
+
+ ReleaseSysCacheList(catlist);
+
+ return result;
+}
+
/* ---------- AMPROC CACHES ---------- */
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.73 2007/01/09 02:14:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.74 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
AssertArg(attNums[i] != 0);
AssertArg(sortOperators[i] != 0);
- if (!get_op_compare_function(sortOperators[i],
- &sortFunction, &reverse))
+ if (!get_compare_function_for_ordering_op(sortOperators[i],
+ &sortFunction, &reverse))
elog(ERROR, "operator %u is not a valid ordering operator",
sortOperators[i]);
state->datumType = datumType;
/* lookup the ordering function */
- if (!get_op_compare_function(sortOperator,
- &sortFunction, &reverse))
+ if (!get_compare_function_for_ordering_op(sortOperator,
+ &sortFunction, &reverse))
elog(ERROR, "operator %u is not a valid ordering operator",
sortOperator);
fmgr_info(sortFunction, &state->sortOpFn);
{
bool reverse;
- if (!get_op_compare_function(sortOperator,
- sortFunction, &reverse))
+ if (!get_compare_function_for_ordering_op(sortOperator,
+ sortFunction, &reverse))
elog(ERROR, "operator %u is not a valid ordering operator",
sortOperator);
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.133 2007/01/05 22:19:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.134 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions,
MemoryContext evalContext);
-extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx);
-extern void execTuplesHashPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx,
- FmgrInfo **eqfunctions,
- FmgrInfo **hashfunctions);
+extern FmgrInfo *execTuplesMatchPrepare(int numCols,
+ Oid *eqOperators);
+extern void execTuplesHashPrepare(int numCols,
+ Oid *eqOperators,
+ FmgrInfo **eqFunctions,
+ FmgrInfo **hashFunctions);
extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions,
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.88 2007/01/09 02:14:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.89 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------------
* merge join node
+ *
+ * The expected ordering of each mergeable column is described by a btree
+ * opfamily OID, a direction (BTLessStrategyNumber or BTGreaterStrategyNumber)
+ * and a nulls-first flag. Note that the two sides of each mergeclause may
+ * be of different datatypes, but they are ordered the same way according to
+ * the common opfamily. The operator in each mergeclause must be an equality
+ * operator of the indicated opfamily.
* ----------------
*/
typedef struct MergeJoin
{
Join join;
List *mergeclauses; /* mergeclauses as expression trees */
- List *mergefamilies; /* OID list of btree opfamilies */
- List *mergestrategies; /* integer list of btree strategies */
+ /* these are arrays, but have the same length as the mergeclauses list: */
+ Oid *mergeFamilies; /* per-clause OIDs of btree opfamilies */
+ int *mergeStrategies; /* per-clause ordering (ASC or DESC) */
+ bool *mergeNullsFirst; /* per-clause nulls ordering */
} MergeJoin;
/* ----------------
Plan plan;
int numCols; /* number of grouping columns */
AttrNumber *grpColIdx; /* their indexes in the target list */
+ Oid *grpOperators; /* equality operators to compare with */
} Group;
/* ---------------
AggStrategy aggstrategy;
int numCols; /* number of grouping columns */
AttrNumber *grpColIdx; /* their indexes in the target list */
+ Oid *grpOperators; /* equality operators to compare with */
long numGroups; /* estimated number of groups in input */
} Agg;
{
Plan plan;
int numCols; /* number of columns to check for uniqueness */
- AttrNumber *uniqColIdx; /* indexes into the target list */
+ AttrNumber *uniqColIdx; /* their indexes in the target list */
+ Oid *uniqOperators; /* equality operators to compare with */
} Unique;
/* ----------------
SetOpCmd cmd; /* what to do */
int numCols; /* number of columns to check for
* duplicate-ness */
- AttrNumber *dupColIdx; /* indexes into the target list */
- AttrNumber flagColIdx;
+ AttrNumber *dupColIdx; /* their indexes in the target list */
+ Oid *dupOperators; /* equality operators to compare with */
+ AttrNumber flagColIdx; /* where is the flag column, if any */
} SetOp;
/* ----------------
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.131 2007/01/09 02:14:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.132 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* A mergejoin path has these fields.
*
* path_mergeclauses lists the clauses (in the form of RestrictInfos)
- * that will be used in the merge. The parallel lists path_mergefamilies
- * and path_mergestrategies specify the merge semantics for each clause
- * (in effect, defining the relevant sort ordering for each clause).
+ * that will be used in the merge. The parallel arrays path_mergeFamilies,
+ * path_mergeStrategies, and path_mergeNullsFirst specify the merge semantics
+ * for each clause (i.e., define the relevant sort ordering for each clause).
+ * (XXX is this the most reasonable path-time representation? It's at least
+ * partially redundant with the pathkeys of the input paths.)
*
* Note that the mergeclauses are a subset of the parent relation's
* restriction-clause list. Any join clauses that are not mergejoinable
{
JoinPath jpath;
List *path_mergeclauses; /* join clauses to be used for merge */
- List *path_mergefamilies; /* OID list of btree opfamilies */
- List *path_mergestrategies; /* integer list of btree strategies */
+ /* these are arrays, but have the same length as the mergeclauses list: */
+ Oid *path_mergeFamilies; /* per-clause OIDs of opfamilies */
+ int *path_mergeStrategies; /* per-clause ordering (ASC or DESC) */
+ bool *path_mergeNullsFirst; /* per-clause nulls ordering */
List *outersortkeys; /* keys for explicit sort, if any */
List *innersortkeys; /* keys for explicit sort, if any */
} MergePath;
* the order of joining and use special join methods at some join points.
* We record information about each such IN clause in an InClauseInfo struct.
* These structs are kept in the PlannerInfo node's in_info_list.
+ *
+ * Note: sub_targetlist is just a list of Vars or expressions; it does not
+ * contain TargetEntry nodes.
*/
typedef struct InClauseInfo
Relids lefthand; /* base relids in lefthand expressions */
Relids righthand; /* base relids coming from the subselect */
List *sub_targetlist; /* targetlist of original RHS subquery */
-
- /*
- * Note: sub_targetlist is just a list of Vars or expressions; it does not
- * contain TargetEntry nodes.
- */
+ List *in_operators; /* OIDs of the IN's equality operator(s) */
} InClauseInfo;
/*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.74 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.75 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
- List *mergefamilies,
- List *mergestrategies,
+ Oid *mergefamilies,
+ int *mergestrategies,
+ bool *mergenullsfirst,
List *outersortkeys,
List *innersortkeys);
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.96 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.97 2007/01/10 18:06:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
AttrNumber *grpColIdx, Plan *lefttree);
extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual,
AggStrategy aggstrategy,
- int numGroupCols, AttrNumber *grpColIdx,
+ int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
long numGroups, int numAggs,
Plan *lefttree);
extern Group *make_group(PlannerInfo *root, List *tlist, List *qual,
- int numGroupCols, AttrNumber *grpColIdx,
+ int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
double numGroups,
Plan *lefttree);
extern Material *make_material(Plan *lefttree);
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.111 2007/01/09 02:14:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.112 2007/01/10 18:06:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int16 strategy);
extern bool get_op_mergejoin_info(Oid eq_op, Oid *left_sortop,
Oid *right_sortop, Oid *opfamily);
-extern bool get_op_compare_function(Oid opno, Oid *cmpfunc, bool *reverse);
+extern bool get_compare_function_for_ordering_op(Oid opno,
+ Oid *cmpfunc, bool *reverse);
+extern Oid get_equality_op_for_ordering_op(Oid opno);
+extern Oid get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type);
+extern Oid get_compatible_hash_operator(Oid opno, bool use_lhs_type);
extern Oid get_op_hash_function(Oid opno);
extern void get_op_btree_interpretation(Oid opno,
List **opfamilies, List **opstrats);
+extern bool ops_in_same_btree_opfamily(Oid opno1, Oid opno2);
extern Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype,
int16 procnum);
extern char *get_attname(Oid relid, AttrNumber attnum);