Commit a191a169 authored by Tom Lane's avatar Tom Lane

Change the planner-to-executor API so that the planner tells the executor

which comparison operators to use for plan nodes involving tuple comparison
(Agg, Group, Unique, SetOp).  Formerly the executor looked up the default
equality operator for the datatype, which was really pretty shaky, since it's
possible that the data being fed to the node is sorted according to some
nondefault operator class that could have an incompatible idea of equality.
The planner knows what it has sorted by and therefore can provide the right
equality operator to use.  Also, this change moves a couple of catalog lookups
out of the executor and into the planner, which should help startup time for
pre-planned queries by some small amount.  Modify the planner to remove some
other cavalier assumptions about always being able to use the default
operators.  Also add "nulls first/last" info to the Plan node for a mergejoin
--- neither the executor nor the planner can cope yet, but at least the API is
in place.
parent 5f6d7353
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -183,24 +183,22 @@ execTuplesUnequal(TupleTableSlot *slot1, ...@@ -183,24 +183,22 @@ execTuplesUnequal(TupleTableSlot *slot1,
* The result is a palloc'd array. * The result is a palloc'd array.
*/ */
FmgrInfo * FmgrInfo *
execTuplesMatchPrepare(TupleDesc tupdesc, execTuplesMatchPrepare(int numCols,
int numCols, Oid *eqOperators)
AttrNumber *matchColIdx)
{ {
FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); FmgrInfo *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
int i; int i;
for (i = 0; i < numCols; i++) for (i = 0; i < numCols; i++)
{ {
AttrNumber att = matchColIdx[i]; Oid eq_opr = eqOperators[i];
Oid typid = tupdesc->attrs[att - 1]->atttypid;
Oid eq_function; Oid eq_function;
eq_function = equality_oper_funcid(typid); eq_function = get_opcode(eq_opr);
fmgr_info(eq_function, &eqfunctions[i]); fmgr_info(eq_function, &eqFunctions[i]);
} }
return eqfunctions; return eqFunctions;
} }
/* /*
...@@ -208,40 +206,33 @@ execTuplesMatchPrepare(TupleDesc tupdesc, ...@@ -208,40 +206,33 @@ execTuplesMatchPrepare(TupleDesc tupdesc,
* Look up the equality and hashing functions needed for a TupleHashTable. * Look up the equality and hashing functions needed for a TupleHashTable.
* *
* This is similar to execTuplesMatchPrepare, but we also need to find the * This is similar to execTuplesMatchPrepare, but we also need to find the
* hash functions associated with the equality operators. *eqfunctions and * hash functions associated with the equality operators. *eqFunctions and
* *hashfunctions receive the palloc'd result arrays. * *hashFunctions receive the palloc'd result arrays.
*/ */
void void
execTuplesHashPrepare(TupleDesc tupdesc, execTuplesHashPrepare(int numCols,
int numCols, Oid *eqOperators,
AttrNumber *matchColIdx, FmgrInfo **eqFunctions,
FmgrInfo **eqfunctions, FmgrInfo **hashFunctions)
FmgrInfo **hashfunctions)
{ {
int i; int i;
*eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
*hashfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); *hashFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
for (i = 0; i < numCols; i++) for (i = 0; i < numCols; i++)
{ {
AttrNumber att = matchColIdx[i]; Oid eq_opr = eqOperators[i];
Oid typid = tupdesc->attrs[att - 1]->atttypid;
Operator optup;
Oid eq_opr;
Oid eq_function; Oid eq_function;
Oid hash_function; Oid hash_function;
optup = equality_oper(typid, false); eq_function = get_opcode(eq_opr);
eq_opr = oprid(optup);
eq_function = oprfuncid(optup);
ReleaseSysCache(optup);
hash_function = get_op_hash_function(eq_opr); hash_function = get_op_hash_function(eq_opr);
if (!OidIsValid(hash_function)) /* should not happen */ if (!OidIsValid(hash_function)) /* should not happen */
elog(ERROR, "could not find hash function for hash operator %u", elog(ERROR, "could not find hash function for hash operator %u",
eq_opr); eq_opr);
fmgr_info(eq_function, &(*eqfunctions)[i]); fmgr_info(eq_function, &(*eqFunctions)[i]);
fmgr_info(hash_function, &(*hashfunctions)[i]); fmgr_info(hash_function, &(*hashFunctions)[i]);
} }
} }
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1270,16 +1270,14 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) ...@@ -1270,16 +1270,14 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
if (node->numCols > 0) if (node->numCols > 0)
{ {
if (node->aggstrategy == AGG_HASHED) if (node->aggstrategy == AGG_HASHED)
execTuplesHashPrepare(ExecGetScanType(&aggstate->ss), execTuplesHashPrepare(node->numCols,
node->numCols, node->grpOperators,
node->grpColIdx,
&aggstate->eqfunctions, &aggstate->eqfunctions,
&aggstate->hashfunctions); &aggstate->hashfunctions);
else else
aggstate->eqfunctions = aggstate->eqfunctions =
execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss), execTuplesMatchPrepare(node->numCols,
node->numCols, node->grpOperators);
node->grpColIdx);
} }
/* /*
...@@ -1519,6 +1517,13 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) ...@@ -1519,6 +1517,13 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
&peraggstate->inputtypeLen, &peraggstate->inputtypeLen,
&peraggstate->inputtypeByVal); &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]); eq_function = equality_oper_funcid(inputTypes[0]);
fmgr_info(eq_function, &(peraggstate->equalfn)); fmgr_info(eq_function, &(peraggstate->equalfn));
peraggstate->sortOperator = ordering_oper_opid(inputTypes[0]); peraggstate->sortOperator = ordering_oper_opid(inputTypes[0]);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* locate group boundaries. * locate group boundaries.
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -211,9 +211,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags) ...@@ -211,9 +211,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
* Precompute fmgr lookup data for inner loop * Precompute fmgr lookup data for inner loop
*/ */
grpstate->eqfunctions = grpstate->eqfunctions =
execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss), execTuplesMatchPrepare(node->numCols,
node->numCols, node->grpOperators);
node->grpColIdx);
return grpstate; return grpstate;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -156,35 +156,36 @@ typedef struct MergeJoinClauseData ...@@ -156,35 +156,36 @@ typedef struct MergeJoinClauseData
* the two expressions from the original clause. * the two expressions from the original clause.
* *
* In addition to the expressions themselves, the planner passes the btree * In addition to the expressions themselves, the planner passes the btree
* opfamily OID and btree strategy number (BTLessStrategyNumber or * opfamily OID, btree strategy number (BTLessStrategyNumber or
* BTGreaterStrategyNumber) that identify the intended merge semantics for * BTGreaterStrategyNumber), and nulls-first flag that identify the intended
* each merge key. The mergejoinable operator is an equality operator in * merge semantics for each merge key. The mergejoinable operator is an
* this opfamily, and the two inputs are guaranteed to be ordered in either * equality operator in this opfamily, and the two inputs are guaranteed to be
* increasing or decreasing (respectively) order according to this opfamily. * ordered in either increasing or decreasing (respectively) order according
* This allows us to obtain the needed comparison functions from the opfamily. * to this opfamily. This allows us to obtain the needed comparison functions
* from the opfamily.
*/ */
static MergeJoinClause static MergeJoinClause
MJExamineQuals(List *mergeclauses, List *mergefamilies, List *mergestrategies, MJExamineQuals(List *mergeclauses,
Oid *mergefamilies,
int *mergestrategies,
bool *mergenullsfirst,
PlanState *parent) PlanState *parent)
{ {
MergeJoinClause clauses; MergeJoinClause clauses;
int nClauses = list_length(mergeclauses); int nClauses = list_length(mergeclauses);
int iClause; int iClause;
ListCell *cl; ListCell *cl;
ListCell *cf;
ListCell *cs;
clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData)); clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData));
iClause = 0; iClause = 0;
cf = list_head(mergefamilies);
cs = list_head(mergestrategies);
foreach(cl, mergeclauses) foreach(cl, mergeclauses)
{ {
OpExpr *qual = (OpExpr *) lfirst(cl); OpExpr *qual = (OpExpr *) lfirst(cl);
MergeJoinClause clause = &clauses[iClause]; MergeJoinClause clause = &clauses[iClause];
Oid opfamily; Oid opfamily = mergefamilies[iClause];
StrategyNumber opstrategy; StrategyNumber opstrategy = mergestrategies[iClause];
bool nulls_first = mergenullsfirst[iClause];
int op_strategy; int op_strategy;
Oid op_lefttype; Oid op_lefttype;
Oid op_righttype; Oid op_righttype;
...@@ -192,14 +193,10 @@ MJExamineQuals(List *mergeclauses, List *mergefamilies, List *mergestrategies, ...@@ -192,14 +193,10 @@ MJExamineQuals(List *mergeclauses, List *mergefamilies, List *mergestrategies,
RegProcedure cmpproc; RegProcedure cmpproc;
AclResult aclresult; 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... */ /* Later we'll support both ascending and descending sort... */
Assert(opstrategy == BTLessStrategyNumber); Assert(opstrategy == BTLessStrategyNumber);
clause->cmpstrategy = MERGEFUNC_CMP; clause->cmpstrategy = MERGEFUNC_CMP;
Assert(!nulls_first);
if (!IsA(qual, OpExpr)) if (!IsA(qual, OpExpr))
elog(ERROR, "mergejoin clause is not an OpExpr"); elog(ERROR, "mergejoin clause is not an OpExpr");
...@@ -1525,8 +1522,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) ...@@ -1525,8 +1522,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
*/ */
mergestate->mj_NumClauses = list_length(node->mergeclauses); mergestate->mj_NumClauses = list_length(node->mergeclauses);
mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses, mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
node->mergefamilies, node->mergeFamilies,
node->mergestrategies, node->mergeStrategies,
node->mergeNullsFirst,
(PlanState *) mergestate); (PlanState *) mergestate);
/* /*
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -267,9 +267,8 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags) ...@@ -267,9 +267,8 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
* Precompute fmgr lookup data for inner loop * Precompute fmgr lookup data for inner loop
*/ */
setopstate->eqfunctions = setopstate->eqfunctions =
execTuplesMatchPrepare(ExecGetResultType(&setopstate->ps), execTuplesMatchPrepare(node->numCols,
node->numCols, node->dupOperators);
node->dupColIdx);
return setopstate; return setopstate;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -159,9 +159,8 @@ ExecInitUnique(Unique *node, EState *estate, int eflags) ...@@ -159,9 +159,8 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
* Precompute fmgr lookup data for inner loop * Precompute fmgr lookup data for inner loop
*/ */
uniquestate->eqfunctions = uniquestate->eqfunctions =
execTuplesMatchPrepare(ExecGetResultType(&uniquestate->ps), execTuplesMatchPrepare(node->numCols,
node->numCols, node->uniqOperators);
node->uniqColIdx);
return uniquestate; return uniquestate;
} }
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -439,6 +439,7 @@ static MergeJoin * ...@@ -439,6 +439,7 @@ static MergeJoin *
_copyMergeJoin(MergeJoin *from) _copyMergeJoin(MergeJoin *from)
{ {
MergeJoin *newnode = makeNode(MergeJoin); MergeJoin *newnode = makeNode(MergeJoin);
int numCols;
/* /*
* copy node superclass fields * copy node superclass fields
...@@ -449,8 +450,10 @@ _copyMergeJoin(MergeJoin *from) ...@@ -449,8 +450,10 @@ _copyMergeJoin(MergeJoin *from)
* copy remainder of node * copy remainder of node
*/ */
COPY_NODE_FIELD(mergeclauses); COPY_NODE_FIELD(mergeclauses);
COPY_NODE_FIELD(mergefamilies); numCols = list_length(from->mergeclauses);
COPY_NODE_FIELD(mergestrategies); COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid));
COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int));
COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool));
return newnode; return newnode;
} }
...@@ -528,6 +531,7 @@ _copyGroup(Group *from) ...@@ -528,6 +531,7 @@ _copyGroup(Group *from)
COPY_SCALAR_FIELD(numCols); COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
return newnode; return newnode;
} }
...@@ -545,7 +549,10 @@ _copyAgg(Agg *from) ...@@ -545,7 +549,10 @@ _copyAgg(Agg *from)
COPY_SCALAR_FIELD(aggstrategy); COPY_SCALAR_FIELD(aggstrategy);
COPY_SCALAR_FIELD(numCols); COPY_SCALAR_FIELD(numCols);
if (from->numCols > 0) if (from->numCols > 0)
{
COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid));
}
COPY_SCALAR_FIELD(numGroups); COPY_SCALAR_FIELD(numGroups);
return newnode; return newnode;
...@@ -569,6 +576,7 @@ _copyUnique(Unique *from) ...@@ -569,6 +576,7 @@ _copyUnique(Unique *from)
*/ */
COPY_SCALAR_FIELD(numCols); COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(uniqColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(uniqColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(uniqOperators, from->numCols * sizeof(Oid));
return newnode; return newnode;
} }
...@@ -612,6 +620,7 @@ _copySetOp(SetOp *from) ...@@ -612,6 +620,7 @@ _copySetOp(SetOp *from)
COPY_SCALAR_FIELD(cmd); COPY_SCALAR_FIELD(cmd);
COPY_SCALAR_FIELD(numCols); COPY_SCALAR_FIELD(numCols);
COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid));
COPY_SCALAR_FIELD(flagColIdx); COPY_SCALAR_FIELD(flagColIdx);
return newnode; return newnode;
...@@ -1356,6 +1365,7 @@ _copyInClauseInfo(InClauseInfo *from) ...@@ -1356,6 +1365,7 @@ _copyInClauseInfo(InClauseInfo *from)
COPY_BITMAPSET_FIELD(lefthand); COPY_BITMAPSET_FIELD(lefthand);
COPY_BITMAPSET_FIELD(righthand); COPY_BITMAPSET_FIELD(righthand);
COPY_NODE_FIELD(sub_targetlist); COPY_NODE_FIELD(sub_targetlist);
COPY_NODE_FIELD(in_operators);
return newnode; return newnode;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -638,6 +638,7 @@ _equalInClauseInfo(InClauseInfo *a, InClauseInfo *b) ...@@ -638,6 +638,7 @@ _equalInClauseInfo(InClauseInfo *a, InClauseInfo *b)
COMPARE_BITMAPSET_FIELD(lefthand); COMPARE_BITMAPSET_FIELD(lefthand);
COMPARE_BITMAPSET_FIELD(righthand); COMPARE_BITMAPSET_FIELD(righthand);
COMPARE_NODE_FIELD(sub_targetlist); COMPARE_NODE_FIELD(sub_targetlist);
COMPARE_NODE_FIELD(in_operators);
return true; return true;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -437,13 +437,28 @@ _outNestLoop(StringInfo str, NestLoop *node) ...@@ -437,13 +437,28 @@ _outNestLoop(StringInfo str, NestLoop *node)
static void static void
_outMergeJoin(StringInfo str, MergeJoin *node) _outMergeJoin(StringInfo str, MergeJoin *node)
{ {
int numCols;
int i;
WRITE_NODE_TYPE("MERGEJOIN"); WRITE_NODE_TYPE("MERGEJOIN");
_outJoinPlanInfo(str, (Join *) node); _outJoinPlanInfo(str, (Join *) node);
WRITE_NODE_FIELD(mergeclauses); 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 static void
...@@ -482,6 +497,10 @@ _outGroup(StringInfo str, Group *node) ...@@ -482,6 +497,10 @@ _outGroup(StringInfo str, Group *node)
appendStringInfo(str, " :grpColIdx"); appendStringInfo(str, " :grpColIdx");
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %d", node->grpColIdx[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 static void
...@@ -530,6 +549,10 @@ _outUnique(StringInfo str, Unique *node) ...@@ -530,6 +549,10 @@ _outUnique(StringInfo str, Unique *node)
appendStringInfo(str, " :uniqColIdx"); appendStringInfo(str, " :uniqColIdx");
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %d", node->uniqColIdx[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 static void
...@@ -548,6 +571,10 @@ _outSetOp(StringInfo str, SetOp *node) ...@@ -548,6 +571,10 @@ _outSetOp(StringInfo str, SetOp *node)
for (i = 0; i < node->numCols; i++) for (i = 0; i < node->numCols; i++)
appendStringInfo(str, " %d", node->dupColIdx[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); WRITE_INT_FIELD(flagColIdx);
} }
...@@ -1169,13 +1196,29 @@ _outNestPath(StringInfo str, NestPath *node) ...@@ -1169,13 +1196,29 @@ _outNestPath(StringInfo str, NestPath *node)
static void static void
_outMergePath(StringInfo str, MergePath *node) _outMergePath(StringInfo str, MergePath *node)
{ {
int numCols;
int i;
WRITE_NODE_TYPE("MERGEPATH"); WRITE_NODE_TYPE("MERGEPATH");
_outJoinPathInfo(str, (JoinPath *) node); _outJoinPathInfo(str, (JoinPath *) node);
WRITE_NODE_FIELD(path_mergeclauses); 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(outersortkeys);
WRITE_NODE_FIELD(innersortkeys); WRITE_NODE_FIELD(innersortkeys);
} }
...@@ -1325,6 +1368,7 @@ _outInClauseInfo(StringInfo str, InClauseInfo *node) ...@@ -1325,6 +1368,7 @@ _outInClauseInfo(StringInfo str, InClauseInfo *node)
WRITE_BITMAPSET_FIELD(lefthand); WRITE_BITMAPSET_FIELD(lefthand);
WRITE_BITMAPSET_FIELD(righthand); WRITE_BITMAPSET_FIELD(righthand);
WRITE_NODE_FIELD(sub_targetlist); WRITE_NODE_FIELD(sub_targetlist);
WRITE_NODE_FIELD(in_operators);
} }
static void static void
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1258,8 +1258,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root) ...@@ -1258,8 +1258,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
Path *outer_path = path->jpath.outerjoinpath; Path *outer_path = path->jpath.outerjoinpath;
Path *inner_path = path->jpath.innerjoinpath; Path *inner_path = path->jpath.innerjoinpath;
List *mergeclauses = path->path_mergeclauses; List *mergeclauses = path->path_mergeclauses;
List *mergefamilies = path->path_mergefamilies; Oid *mergeFamilies = path->path_mergeFamilies;
List *mergestrategies = path->path_mergestrategies; int *mergeStrategies = path->path_mergeStrategies;
List *outersortkeys = path->outersortkeys; List *outersortkeys = path->outersortkeys;
List *innersortkeys = path->innersortkeys; List *innersortkeys = path->innersortkeys;
Cost startup_cost = 0; Cost startup_cost = 0;
...@@ -1357,8 +1357,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root) ...@@ -1357,8 +1357,8 @@ cost_mergejoin(MergePath *path, PlannerInfo *root)
firstclause = (RestrictInfo *) linitial(mergeclauses); firstclause = (RestrictInfo *) linitial(mergeclauses);
if (firstclause->left_mergescansel < 0) /* not computed yet? */ if (firstclause->left_mergescansel < 0) /* not computed yet? */
mergejoinscansel(root, (Node *) firstclause->clause, mergejoinscansel(root, (Node *) firstclause->clause,
linitial_oid(mergefamilies), mergeFamilies[0],
linitial_int(mergestrategies), mergeStrategies[0],
&firstclause->left_mergescansel, &firstclause->left_mergescansel,
&firstclause->right_mergescansel); &firstclause->right_mergescansel);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -40,8 +40,10 @@ static List *select_mergejoin_clauses(RelOptInfo *joinrel, ...@@ -40,8 +40,10 @@ static List *select_mergejoin_clauses(RelOptInfo *joinrel,
RelOptInfo *innerrel, RelOptInfo *innerrel,
List *restrictlist, List *restrictlist,
JoinType jointype); JoinType jointype);
static void build_mergejoin_strat_lists(List *mergeclauses, static void build_mergejoin_strat_arrays(List *mergeclauses,
List **mergefamilies, List **mergestrategies); Oid **mergefamilies,
int **mergestrategies,
bool **mergenullsfirst);
/* /*
...@@ -228,8 +230,9 @@ sort_inner_and_outer(PlannerInfo *root, ...@@ -228,8 +230,9 @@ sort_inner_and_outer(PlannerInfo *root,
List *front_pathkey = (List *) lfirst(l); List *front_pathkey = (List *) lfirst(l);
List *cur_pathkeys; List *cur_pathkeys;
List *cur_mergeclauses; List *cur_mergeclauses;
List *mergefamilies; Oid *mergefamilies;
List *mergestrategies; int *mergestrategies;
bool *mergenullsfirst;
List *outerkeys; List *outerkeys;
List *innerkeys; List *innerkeys;
List *merge_pathkeys; List *merge_pathkeys;
...@@ -275,8 +278,10 @@ sort_inner_and_outer(PlannerInfo *root, ...@@ -275,8 +278,10 @@ sort_inner_and_outer(PlannerInfo *root,
outerkeys); outerkeys);
/* Build opfamily info for execution */ /* Build opfamily info for execution */
build_mergejoin_strat_lists(cur_mergeclauses, build_mergejoin_strat_arrays(cur_mergeclauses,
&mergefamilies, &mergestrategies); &mergefamilies,
&mergestrategies,
&mergenullsfirst);
/* /*
* And now we can make the path. * And now we can make the path.
...@@ -292,6 +297,7 @@ sort_inner_and_outer(PlannerInfo *root, ...@@ -292,6 +297,7 @@ sort_inner_and_outer(PlannerInfo *root,
cur_mergeclauses, cur_mergeclauses,
mergefamilies, mergefamilies,
mergestrategies, mergestrategies,
mergenullsfirst,
outerkeys, outerkeys,
innerkeys)); innerkeys));
} }
...@@ -421,8 +427,9 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -421,8 +427,9 @@ match_unsorted_outer(PlannerInfo *root,
Path *outerpath = (Path *) lfirst(l); Path *outerpath = (Path *) lfirst(l);
List *merge_pathkeys; List *merge_pathkeys;
List *mergeclauses; List *mergeclauses;
List *mergefamilies; Oid *mergefamilies;
List *mergestrategies; int *mergestrategies;
bool *mergenullsfirst;
List *innersortkeys; List *innersortkeys;
List *trialsortkeys; List *trialsortkeys;
Path *cheapest_startup_inner; Path *cheapest_startup_inner;
...@@ -530,8 +537,10 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -530,8 +537,10 @@ match_unsorted_outer(PlannerInfo *root,
innerrel); innerrel);
/* Build opfamily info for execution */ /* Build opfamily info for execution */
build_mergejoin_strat_lists(mergeclauses, build_mergejoin_strat_arrays(mergeclauses,
&mergefamilies, &mergestrategies); &mergefamilies,
&mergestrategies,
&mergenullsfirst);
/* /*
* Generate a mergejoin on the basis of sorting the cheapest inner. * Generate a mergejoin on the basis of sorting the cheapest inner.
...@@ -550,6 +559,7 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -550,6 +559,7 @@ match_unsorted_outer(PlannerInfo *root,
mergeclauses, mergeclauses,
mergefamilies, mergefamilies,
mergestrategies, mergestrategies,
mergenullsfirst,
NIL, NIL,
innersortkeys)); innersortkeys));
...@@ -610,8 +620,10 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -610,8 +620,10 @@ match_unsorted_outer(PlannerInfo *root,
newclauses = mergeclauses; newclauses = mergeclauses;
/* Build opfamily info for execution */ /* Build opfamily info for execution */
build_mergejoin_strat_lists(newclauses, build_mergejoin_strat_arrays(newclauses,
&mergefamilies, &mergestrategies); &mergefamilies,
&mergestrategies,
&mergenullsfirst);
add_path(joinrel, (Path *) add_path(joinrel, (Path *)
create_mergejoin_path(root, create_mergejoin_path(root,
...@@ -624,6 +636,7 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -624,6 +636,7 @@ match_unsorted_outer(PlannerInfo *root,
newclauses, newclauses,
mergefamilies, mergefamilies,
mergestrategies, mergestrategies,
mergenullsfirst,
NIL, NIL,
NIL)); NIL));
cheapest_total_inner = innerpath; cheapest_total_inner = innerpath;
...@@ -661,8 +674,10 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -661,8 +674,10 @@ match_unsorted_outer(PlannerInfo *root,
} }
/* Build opfamily info for execution */ /* Build opfamily info for execution */
build_mergejoin_strat_lists(newclauses, build_mergejoin_strat_arrays(newclauses,
&mergefamilies, &mergestrategies); &mergefamilies,
&mergestrategies,
&mergenullsfirst);
add_path(joinrel, (Path *) add_path(joinrel, (Path *)
create_mergejoin_path(root, create_mergejoin_path(root,
...@@ -675,6 +690,7 @@ match_unsorted_outer(PlannerInfo *root, ...@@ -675,6 +690,7 @@ match_unsorted_outer(PlannerInfo *root,
newclauses, newclauses,
mergefamilies, mergefamilies,
mergestrategies, mergestrategies,
mergenullsfirst,
NIL, NIL,
NIL)); NIL));
} }
...@@ -981,20 +997,26 @@ select_mergejoin_clauses(RelOptInfo *joinrel, ...@@ -981,20 +997,26 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
} }
/* /*
* 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 * 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 * planning so that it can deal with multiple possible merge orders, but
* that's not done yet. * that's not done yet.
*/ */
static void static void
build_mergejoin_strat_lists(List *mergeclauses, build_mergejoin_strat_arrays(List *mergeclauses,
List **mergefamilies, List **mergestrategies) Oid **mergefamilies,
int **mergestrategies,
bool **mergenullsfirst)
{ {
int nClauses = list_length(mergeclauses);
int i;
ListCell *l; ListCell *l;
*mergefamilies = NIL; *mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid));
*mergestrategies = NIL; *mergestrategies = (int *) palloc(nClauses * sizeof(int));
*mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool));
i = 0;
foreach(l, mergeclauses) foreach(l, mergeclauses)
{ {
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l); RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
...@@ -1003,11 +1025,16 @@ build_mergejoin_strat_lists(List *mergeclauses, ...@@ -1003,11 +1025,16 @@ build_mergejoin_strat_lists(List *mergeclauses,
* We do not need to worry about whether the mergeclause will be * We do not need to worry about whether the mergeclause will be
* commuted at runtime --- it's the same opfamily either way. * 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 * For the moment, strategy must always be LessThan --- see
* hack version of get_op_mergejoin_info * 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++;
} }
} }
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -113,7 +113,10 @@ static HashJoin *make_hashjoin(List *tlist, ...@@ -113,7 +113,10 @@ static HashJoin *make_hashjoin(List *tlist,
static Hash *make_hash(Plan *lefttree); static Hash *make_hash(Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist, static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses, List *joinclauses, List *otherclauses,
List *mergeclauses, List *mergefamilies, List *mergestrategies, List *mergeclauses,
Oid *mergefamilies,
int *mergestrategies,
bool *mergenullsfirst,
Plan *lefttree, Plan *righttree, Plan *lefttree, Plan *righttree,
JoinType jointype); JoinType jointype);
static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
...@@ -595,6 +598,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -595,6 +598,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
Plan *plan; Plan *plan;
Plan *subplan; Plan *subplan;
List *uniq_exprs; List *uniq_exprs;
List *in_operators;
List *newtlist; List *newtlist;
int nextresno; int nextresno;
bool newitems; bool newitems;
...@@ -626,10 +630,12 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -626,10 +630,12 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
* To find the correct list of values to unique-ify, we look in the * 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 * information saved for IN expressions. If this code is ever used in
* other scenarios, some other way of finding what to unique-ify will * 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 */ uniq_exprs = NIL; /* just to keep compiler quiet */
in_operators = NIL;
foreach(l, root->in_info_list) foreach(l, root->in_info_list)
{ {
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l); InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
...@@ -637,6 +643,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -637,6 +643,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
if (bms_equal(ininfo->righthand, best_path->path.parent->relids)) if (bms_equal(ininfo->righthand, best_path->path.parent->relids))
{ {
uniq_exprs = ininfo->sub_targetlist; uniq_exprs = ininfo->sub_targetlist;
in_operators = ininfo->in_operators;
break; break;
} }
} }
...@@ -687,8 +694,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -687,8 +694,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
newtlist = subplan->targetlist; newtlist = subplan->targetlist;
numGroupCols = list_length(uniq_exprs); numGroupCols = list_length(uniq_exprs);
groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber)); groupColIdx = (AttrNumber *) palloc(numGroupCols * sizeof(AttrNumber));
groupColPos = 0;
groupColPos = 0;
foreach(l, uniq_exprs) foreach(l, uniq_exprs)
{ {
Node *uniqexpr = lfirst(l); Node *uniqexpr = lfirst(l);
...@@ -703,9 +710,30 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -703,9 +710,30 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
if (best_path->umethod == UNIQUE_PATH_HASH) if (best_path->umethod == UNIQUE_PATH_HASH)
{ {
long numGroups; long numGroups;
Oid *groupOperators;
numGroups = (long) Min(best_path->rows, (double) LONG_MAX); 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 * 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 * minimum output tlist, without any stuff we might have added to the
...@@ -717,6 +745,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -717,6 +745,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
AGG_HASHED, AGG_HASHED,
numGroupCols, numGroupCols,
groupColIdx, groupColIdx,
groupOperators,
numGroups, numGroups,
0, 0,
subplan); subplan);
...@@ -725,18 +754,29 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) ...@@ -725,18 +754,29 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
{ {
List *sortList = NIL; 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; 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, tle = get_tle_by_resno(subplan->targetlist,
groupColIdx[groupColPos]); groupColIdx[groupColPos]);
Assert(tle != NULL); Assert(tle != NULL);
sortList = addTargetToSortList(NULL, tle, sortcl = makeNode(SortClause);
sortList, subplan->targetlist, sortcl->tleSortGroupRef = assignSortGroupRef(tle,
SORTBY_DEFAULT, subplan->targetlist);
SORTBY_NULLS_DEFAULT, sortcl->sortop = sortop;
NIL, false); sortcl->nulls_first = false;
sortList = lappend(sortList, sortcl);
groupColPos++;
} }
plan = (Plan *) make_sort_from_sortclauses(root, sortList, subplan); plan = (Plan *) make_sort_from_sortclauses(root, sortList, subplan);
plan = (Plan *) make_unique(plan, sortList); plan = (Plan *) make_unique(plan, sortList);
...@@ -1542,8 +1582,9 @@ create_mergejoin_plan(PlannerInfo *root, ...@@ -1542,8 +1582,9 @@ create_mergejoin_plan(PlannerInfo *root,
joinclauses, joinclauses,
otherclauses, otherclauses,
mergeclauses, mergeclauses,
best_path->path_mergefamilies, best_path->path_mergeFamilies,
best_path->path_mergestrategies, best_path->path_mergeStrategies,
best_path->path_mergeNullsFirst,
outer_plan, outer_plan,
inner_plan, inner_plan,
best_path->jpath.jointype); best_path->jpath.jointype);
...@@ -2335,8 +2376,9 @@ make_mergejoin(List *tlist, ...@@ -2335,8 +2376,9 @@ make_mergejoin(List *tlist,
List *joinclauses, List *joinclauses,
List *otherclauses, List *otherclauses,
List *mergeclauses, List *mergeclauses,
List *mergefamilies, Oid *mergefamilies,
List *mergestrategies, int *mergestrategies,
bool *mergenullsfirst,
Plan *lefttree, Plan *lefttree,
Plan *righttree, Plan *righttree,
JoinType jointype) JoinType jointype)
...@@ -2350,8 +2392,9 @@ make_mergejoin(List *tlist, ...@@ -2350,8 +2392,9 @@ make_mergejoin(List *tlist,
plan->lefttree = lefttree; plan->lefttree = lefttree;
plan->righttree = righttree; plan->righttree = righttree;
node->mergeclauses = mergeclauses; node->mergeclauses = mergeclauses;
node->mergefamilies = mergefamilies; node->mergeFamilies = mergefamilies;
node->mergestrategies = mergestrategies; node->mergeStrategies = mergestrategies;
node->mergeNullsFirst = mergenullsfirst;
node->join.jointype = jointype; node->join.jointype = jointype;
node->join.joinqual = joinclauses; node->join.joinqual = joinclauses;
...@@ -2613,7 +2656,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) ...@@ -2613,7 +2656,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
* This might look like it could be merged with make_sort_from_sortclauses, * 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, * but presently we *must* use the grpColIdx[] array to locate sort columns,
* because the child plan's tlist is not marked with ressortgroupref info * 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. * is used from the GroupClause entries.
*/ */
Sort * Sort *
...@@ -2716,7 +2759,7 @@ materialize_finished_plan(Plan *subplan) ...@@ -2716,7 +2759,7 @@ materialize_finished_plan(Plan *subplan)
Agg * Agg *
make_agg(PlannerInfo *root, List *tlist, List *qual, make_agg(PlannerInfo *root, List *tlist, List *qual,
AggStrategy aggstrategy, AggStrategy aggstrategy,
int numGroupCols, AttrNumber *grpColIdx, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
long numGroups, int numAggs, long numGroups, int numAggs,
Plan *lefttree) Plan *lefttree)
{ {
...@@ -2728,6 +2771,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual, ...@@ -2728,6 +2771,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
node->aggstrategy = aggstrategy; node->aggstrategy = aggstrategy;
node->numCols = numGroupCols; node->numCols = numGroupCols;
node->grpColIdx = grpColIdx; node->grpColIdx = grpColIdx;
node->grpOperators = grpOperators;
node->numGroups = numGroups; node->numGroups = numGroups;
copy_plan_costsize(plan, lefttree); /* only care about copying size */ copy_plan_costsize(plan, lefttree); /* only care about copying size */
...@@ -2784,6 +2828,7 @@ make_group(PlannerInfo *root, ...@@ -2784,6 +2828,7 @@ make_group(PlannerInfo *root,
List *qual, List *qual,
int numGroupCols, int numGroupCols,
AttrNumber *grpColIdx, AttrNumber *grpColIdx,
Oid *grpOperators,
double numGroups, double numGroups,
Plan *lefttree) Plan *lefttree)
{ {
...@@ -2794,6 +2839,7 @@ make_group(PlannerInfo *root, ...@@ -2794,6 +2839,7 @@ make_group(PlannerInfo *root,
node->numCols = numGroupCols; node->numCols = numGroupCols;
node->grpColIdx = grpColIdx; node->grpColIdx = grpColIdx;
node->grpOperators = grpOperators;
copy_plan_costsize(plan, lefttree); /* only care about copying size */ copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_group(&group_path, root, cost_group(&group_path, root,
...@@ -2841,7 +2887,8 @@ make_group(PlannerInfo *root, ...@@ -2841,7 +2887,8 @@ make_group(PlannerInfo *root,
/* /*
* distinctList is a list of SortClauses, identifying the targetlist items * 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 * Unique *
make_unique(Plan *lefttree, List *distinctList) make_unique(Plan *lefttree, List *distinctList)
...@@ -2851,6 +2898,7 @@ make_unique(Plan *lefttree, List *distinctList) ...@@ -2851,6 +2898,7 @@ make_unique(Plan *lefttree, List *distinctList)
int numCols = list_length(distinctList); int numCols = list_length(distinctList);
int keyno = 0; int keyno = 0;
AttrNumber *uniqColIdx; AttrNumber *uniqColIdx;
Oid *uniqOperators;
ListCell *slitem; ListCell *slitem;
copy_plan_costsize(plan, lefttree); copy_plan_costsize(plan, lefttree);
...@@ -2874,28 +2922,37 @@ make_unique(Plan *lefttree, List *distinctList) ...@@ -2874,28 +2922,37 @@ make_unique(Plan *lefttree, List *distinctList)
plan->righttree = NULL; 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); Assert(numCols > 0);
uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); uniqColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
uniqOperators = (Oid *) palloc(sizeof(Oid) * numCols);
foreach(slitem, distinctList) foreach(slitem, distinctList)
{ {
SortClause *sortcl = (SortClause *) lfirst(slitem); SortClause *sortcl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist); 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->numCols = numCols;
node->uniqColIdx = uniqColIdx; node->uniqColIdx = uniqColIdx;
node->uniqOperators = uniqOperators;
return node; return node;
} }
/* /*
* distinctList is a list of SortClauses, identifying the targetlist items * 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 * SetOp *
make_setop(SetOpCmd cmd, Plan *lefttree, make_setop(SetOpCmd cmd, Plan *lefttree,
...@@ -2906,6 +2963,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree, ...@@ -2906,6 +2963,7 @@ make_setop(SetOpCmd cmd, Plan *lefttree,
int numCols = list_length(distinctList); int numCols = list_length(distinctList);
int keyno = 0; int keyno = 0;
AttrNumber *dupColIdx; AttrNumber *dupColIdx;
Oid *dupOperators;
ListCell *slitem; ListCell *slitem;
copy_plan_costsize(plan, lefttree); copy_plan_costsize(plan, lefttree);
...@@ -2930,22 +2988,30 @@ make_setop(SetOpCmd cmd, Plan *lefttree, ...@@ -2930,22 +2988,30 @@ make_setop(SetOpCmd cmd, Plan *lefttree,
plan->righttree = NULL; 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); Assert(numCols > 0);
dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols); dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
dupOperators = (Oid *) palloc(sizeof(Oid) * numCols);
foreach(slitem, distinctList) foreach(slitem, distinctList)
{ {
SortClause *sortcl = (SortClause *) lfirst(slitem); SortClause *sortcl = (SortClause *) lfirst(slitem);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, plan->targetlist); 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->cmd = cmd;
node->numCols = numCols; node->numCols = numCols;
node->dupColIdx = dupColIdx; node->dupColIdx = dupColIdx;
node->dupOperators = dupOperators;
node->flagColIdx = flagColIdx; node->flagColIdx = flagColIdx;
return node; return node;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_oper.h" #include "parser/parse_oper.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -62,10 +63,11 @@ static bool is_dummy_plan(Plan *plan); ...@@ -62,10 +63,11 @@ static bool is_dummy_plan(Plan *plan);
static double preprocess_limit(PlannerInfo *root, static double preprocess_limit(PlannerInfo *root,
double tuple_fraction, double tuple_fraction,
int64 *offset_est, int64 *count_est); int64 *offset_est, int64 *count_est);
static Oid *extract_grouping_ops(List *groupClause);
static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
Path *cheapest_path, Path *sorted_path, Path *cheapest_path, Path *sorted_path,
double dNumGroups, AggClauseCounts *agg_counts); Oid *groupOperators, double dNumGroups,
static bool hash_safe_grouping(PlannerInfo *root); AggClauseCounts *agg_counts);
static List *make_subplanTargetList(PlannerInfo *root, List *tlist, static List *make_subplanTargetList(PlannerInfo *root, List *tlist,
AttrNumber **groupColIdx, bool *need_tlist_eval); AttrNumber **groupColIdx, bool *need_tlist_eval);
static void locate_grouping_columns(PlannerInfo *root, static void locate_grouping_columns(PlannerInfo *root,
...@@ -750,6 +752,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -750,6 +752,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
List *sub_tlist; List *sub_tlist;
List *group_pathkeys; List *group_pathkeys;
AttrNumber *groupColIdx = NULL; AttrNumber *groupColIdx = NULL;
Oid *groupOperators = NULL;
bool need_tlist_eval = true; bool need_tlist_eval = true;
QualCost tlist_cost; QualCost tlist_cost;
Path *cheapest_path; Path *cheapest_path;
...@@ -829,14 +832,17 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -829,14 +832,17 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
sort_pathkeys = root->sort_pathkeys; 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) if (parse->groupClause)
{ {
groupOperators = extract_grouping_ops(parse->groupClause);
use_hashed_grouping = use_hashed_grouping =
choose_hashed_grouping(root, tuple_fraction, choose_hashed_grouping(root, tuple_fraction,
cheapest_path, sorted_path, cheapest_path, sorted_path,
dNumGroups, &agg_counts); groupOperators, dNumGroups,
&agg_counts);
/* Also convert # groups to long int --- but 'ware overflow! */ /* Also convert # groups to long int --- but 'ware overflow! */
numGroups = (long) Min(dNumGroups, (double) LONG_MAX); numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
...@@ -956,6 +962,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -956,6 +962,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
AGG_HASHED, AGG_HASHED,
numGroupCols, numGroupCols,
groupColIdx, groupColIdx,
groupOperators,
numGroups, numGroups,
agg_counts.numAggs, agg_counts.numAggs,
result_plan); result_plan);
...@@ -999,6 +1006,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -999,6 +1006,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
aggstrategy, aggstrategy,
numGroupCols, numGroupCols,
groupColIdx, groupColIdx,
groupOperators,
numGroups, numGroups,
agg_counts.numAggs, agg_counts.numAggs,
result_plan); result_plan);
...@@ -1027,6 +1035,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) ...@@ -1027,6 +1035,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
(List *) parse->havingQual, (List *) parse->havingQual,
numGroupCols, numGroupCols,
groupColIdx, groupColIdx,
groupOperators,
dNumGroups, dNumGroups,
result_plan); result_plan);
/* The Group node won't change sort ordering */ /* The Group node won't change sort ordering */
...@@ -1337,13 +1346,42 @@ preprocess_limit(PlannerInfo *root, double tuple_fraction, ...@@ -1337,13 +1346,42 @@ preprocess_limit(PlannerInfo *root, double tuple_fraction,
return tuple_fraction; 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? * choose_hashed_grouping - should we use hashed grouping?
*/ */
static bool static bool
choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
Path *cheapest_path, Path *sorted_path, 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); int numGroupCols = list_length(root->parse->groupClause);
double cheapest_path_rows; double cheapest_path_rows;
...@@ -1352,10 +1390,13 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, ...@@ -1352,10 +1390,13 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
List *current_pathkeys; List *current_pathkeys;
Path hashed_p; Path hashed_p;
Path sorted_p; Path sorted_p;
int i;
/* /*
* Check can't-do-it conditions, including whether the grouping operators * 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. * Executor doesn't support hashed aggregation with DISTINCT aggregates.
* (Doing so would imply storing *all* the input values in the hash table, * (Doing so would imply storing *all* the input values in the hash table,
...@@ -1365,8 +1406,11 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, ...@@ -1365,8 +1406,11 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
return false; return false;
if (agg_counts->numDistinctAggs != 0) if (agg_counts->numDistinctAggs != 0)
return false; return false;
if (!hash_safe_grouping(root)) for (i = 0; i < numGroupCols; i++)
return false; {
if (!op_hashjoinable(groupOperators[i]))
return false;
}
/* /*
* Don't do it if it doesn't look like the hashtable will fit into * Don't do it if it doesn't look like the hashtable will fit into
...@@ -1471,36 +1515,6 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, ...@@ -1471,36 +1515,6 @@ choose_hashed_grouping(PlannerInfo *root, double tuple_fraction,
return false; 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 * make_subplanTargetList
* Generate appropriate target list when grouping is required. * Generate appropriate target list when grouping is required.
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -671,11 +671,13 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -671,11 +671,13 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
{ {
Query *parse = root->parse; Query *parse = root->parse;
Query *subselect = (Query *) sublink->subselect; Query *subselect = (Query *) sublink->subselect;
List *in_operators;
Relids left_varnos; Relids left_varnos;
int rtindex; int rtindex;
RangeTblEntry *rte; RangeTblEntry *rte;
RangeTblRef *rtr; RangeTblRef *rtr;
InClauseInfo *ininfo; InClauseInfo *ininfo;
Node *result;
/* /*
* The sublink type must be "= ANY" --- that is, an IN operator. We * The sublink type must be "= ANY" --- that is, an IN operator. We
...@@ -689,15 +691,31 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -689,15 +691,31 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
return NULL; return NULL;
if (sublink->testexpr && IsA(sublink->testexpr, OpExpr)) if (sublink->testexpr && IsA(sublink->testexpr, OpExpr))
{ {
Oid opno = ((OpExpr *) sublink->testexpr)->opno;
List *opfamilies; List *opfamilies;
List *opstrats; List *opstrats;
get_op_btree_interpretation(((OpExpr *) sublink->testexpr)->opno, get_op_btree_interpretation(opno, &opfamilies, &opstrats);
&opfamilies, &opstrats);
if (!list_member_int(opstrats, ROWCOMPARE_EQ)) if (!list_member_int(opstrats, ROWCOMPARE_EQ))
return NULL; 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; return NULL;
/* /*
...@@ -745,16 +763,23 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink) ...@@ -745,16 +763,23 @@ convert_IN_to_join(PlannerInfo *root, SubLink *sublink)
ininfo = makeNode(InClauseInfo); ininfo = makeNode(InClauseInfo);
ininfo->lefthand = left_varnos; ininfo->lefthand = left_varnos;
ininfo->righthand = bms_make_singleton(rtindex); 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, * Build the result qual expression. As a side effect,
* ininfo->sub_targetlist is filled with a list of Vars representing the * ininfo->sub_targetlist is filled with a list of Vars representing the
* subselect outputs. * subselect outputs.
*/ */
return convert_testexpr(sublink->testexpr, result = convert_testexpr(sublink->testexpr,
rtindex, rtindex,
&ininfo->sub_targetlist); &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;
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -3995,6 +3995,7 @@ expression_tree_mutator(Node *node, ...@@ -3995,6 +3995,7 @@ expression_tree_mutator(Node *node,
FLATCOPY(newnode, ininfo, InClauseInfo); FLATCOPY(newnode, ininfo, InClauseInfo);
MUTATE(newnode->sub_targetlist, ininfo->sub_targetlist, List *); MUTATE(newnode->sub_targetlist, ininfo->sub_targetlist, List *);
/* Assume we need not make a copy of in_operators list */
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,12 +28,14 @@ ...@@ -28,12 +28,14 @@
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/selfuncs.h" #include "utils/selfuncs.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
static List *translate_sub_tlist(List *tlist, int relid); static List *translate_sub_tlist(List *tlist, int relid);
static bool query_is_distinct_for(Query *query, List *colnos); static bool query_is_distinct_for(Query *query, List *colnos, List *opids);
static bool hash_safe_tlist(List *tlist); static Oid distinct_col_search(int colno, List *colnos, List *opids);
static bool hash_safe_operators(List *opids);
/***************************************************************************** /*****************************************************************************
...@@ -733,6 +735,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath) ...@@ -733,6 +735,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath)
Path agg_path; /* dummy for result of cost_agg */ Path agg_path; /* dummy for result of cost_agg */
MemoryContext oldcontext; MemoryContext oldcontext;
List *sub_targetlist; List *sub_targetlist;
List *in_operators;
ListCell *l; ListCell *l;
int numCols; int numCols;
...@@ -769,9 +772,11 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath) ...@@ -769,9 +772,11 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath)
/* /*
* Try to identify the targetlist that will actually be unique-ified. In * 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, * 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; sub_targetlist = NIL;
in_operators = NIL;
foreach(l, root->in_info_list) foreach(l, root->in_info_list)
{ {
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l); InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
...@@ -779,6 +784,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath) ...@@ -779,6 +784,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath)
if (bms_equal(ininfo->righthand, rel->relids)) if (bms_equal(ininfo->righthand, rel->relids))
{ {
sub_targetlist = ininfo->sub_targetlist; sub_targetlist = ininfo->sub_targetlist;
in_operators = ininfo->in_operators;
break; break;
} }
} }
...@@ -801,7 +807,8 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath) ...@@ -801,7 +807,8 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath)
sub_tlist_colnos = translate_sub_tlist(sub_targetlist, rel->relid); sub_tlist_colnos = translate_sub_tlist(sub_targetlist, rel->relid);
if (sub_tlist_colnos && 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->umethod = UNIQUE_PATH_NOOP;
pathnode->rows = rel->rows; pathnode->rows = rel->rows;
...@@ -847,11 +854,11 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath) ...@@ -847,11 +854,11 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath)
/* /*
* Is it safe to use a hashed implementation? If so, estimate and compare * 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 * costs. We only try this if we know the IN operators, else we can't
* can't be sure about the datatypes involved). * check their hashability.
*/ */
pathnode->umethod = UNIQUE_PATH_SORT; 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 * Estimate the overhead per hashtable entry at 64 bytes (same as in
...@@ -924,16 +931,25 @@ translate_sub_tlist(List *tlist, int relid) ...@@ -924,16 +931,25 @@ translate_sub_tlist(List *tlist, int relid)
* *
* colnos is an integer list of output column numbers (resno's). We are * colnos is an integer list of output column numbers (resno's). We are
* interested in whether rows consisting of just these columns are certain * 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 static bool
query_is_distinct_for(Query *query, List *colnos) query_is_distinct_for(Query *query, List *colnos, List *opids)
{ {
ListCell *l; ListCell *l;
Oid opid;
Assert(list_length(colnos) == list_length(opids));
/* /*
* DISTINCT (including DISTINCT ON) guarantees uniqueness if all the * 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) if (query->distinctClause)
{ {
...@@ -943,7 +959,9 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -943,7 +959,9 @@ query_is_distinct_for(Query *query, List *colnos)
TargetEntry *tle = get_sortgroupclause_tle(scl, TargetEntry *tle = get_sortgroupclause_tle(scl,
query->targetList); 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 */ break; /* exit early if no match */
} }
if (l == NULL) /* had matches for all? */ if (l == NULL) /* had matches for all? */
...@@ -952,7 +970,7 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -952,7 +970,7 @@ query_is_distinct_for(Query *query, List *colnos)
/* /*
* Similarly, GROUP BY guarantees uniqueness if all the grouped columns * Similarly, GROUP BY guarantees uniqueness if all the grouped columns
* appear in colnos. * appear in colnos and operator semantics match.
*/ */
if (query->groupClause) if (query->groupClause)
{ {
...@@ -962,7 +980,9 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -962,7 +980,9 @@ query_is_distinct_for(Query *query, List *colnos)
TargetEntry *tle = get_sortgroupclause_tle(grpcl, TargetEntry *tle = get_sortgroupclause_tle(grpcl,
query->targetList); 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 */ break; /* exit early if no match */
} }
if (l == NULL) /* had matches for all? */ if (l == NULL) /* had matches for all? */
...@@ -972,7 +992,7 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -972,7 +992,7 @@ query_is_distinct_for(Query *query, List *colnos)
{ {
/* /*
* If we have no GROUP BY, but do have aggregates or HAVING, then the * 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) if (query->hasAggs || query->havingQual)
return true; return true;
...@@ -980,7 +1000,13 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -980,7 +1000,13 @@ query_is_distinct_for(Query *query, List *colnos)
/* /*
* UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row, * 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) if (query->setOperations)
{ {
...@@ -996,8 +1022,13 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -996,8 +1022,13 @@ query_is_distinct_for(Query *query, List *colnos)
{ {
TargetEntry *tle = (TargetEntry *) lfirst(l); TargetEntry *tle = (TargetEntry *) lfirst(l);
if (!tle->resjunk && if (tle->resjunk)
!list_member_int(colnos, tle->resno)) 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 */ break; /* exit early if no match */
} }
if (l == NULL) /* had matches for all? */ if (l == NULL) /* had matches for all? */
...@@ -1014,31 +1045,46 @@ query_is_distinct_for(Query *query, List *colnos) ...@@ -1014,31 +1045,46 @@ query_is_distinct_for(Query *query, List *colnos)
} }
/* /*
* 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 * If colno is in colnos, return the corresponding element of opids,
* is marked hashjoinable. * 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 * We assume hashed aggregation will work if each IN operator is marked
* in plan/planner.c. * 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 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); if (!op_hashjoinable(lfirst_oid(lc)))
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)
return false; return false;
} }
return true; return true;
...@@ -1156,6 +1202,7 @@ create_nestloop_path(PlannerInfo *root, ...@@ -1156,6 +1202,7 @@ create_nestloop_path(PlannerInfo *root,
* ordering for each merge clause * ordering for each merge clause
* 'mergestrategies' are the btree operator strategies identifying the merge * 'mergestrategies' are the btree operator strategies identifying the merge
* ordering for each merge clause * 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 * 'outersortkeys' are the sort varkeys for the outer relation
* 'innersortkeys' are the sort varkeys for the inner relation * 'innersortkeys' are the sort varkeys for the inner relation
*/ */
...@@ -1168,8 +1215,9 @@ create_mergejoin_path(PlannerInfo *root, ...@@ -1168,8 +1215,9 @@ create_mergejoin_path(PlannerInfo *root,
List *restrict_clauses, List *restrict_clauses,
List *pathkeys, List *pathkeys,
List *mergeclauses, List *mergeclauses,
List *mergefamilies, Oid *mergefamilies,
List *mergestrategies, int *mergestrategies,
bool *mergenullsfirst,
List *outersortkeys, List *outersortkeys,
List *innersortkeys) List *innersortkeys)
{ {
...@@ -1210,8 +1258,9 @@ create_mergejoin_path(PlannerInfo *root, ...@@ -1210,8 +1258,9 @@ create_mergejoin_path(PlannerInfo *root,
pathnode->jpath.joinrestrictinfo = restrict_clauses; pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->jpath.path.pathkeys = pathkeys; pathnode->jpath.path.pathkeys = pathkeys;
pathnode->path_mergeclauses = mergeclauses; pathnode->path_mergeclauses = mergeclauses;
pathnode->path_mergefamilies = mergefamilies; pathnode->path_mergeFamilies = mergefamilies;
pathnode->path_mergestrategies = mergestrategies; pathnode->path_mergeStrategies = mergestrategies;
pathnode->path_mergeNullsFirst = mergenullsfirst;
pathnode->outersortkeys = outersortkeys; pathnode->outersortkeys = outersortkeys;
pathnode->innersortkeys = innersortkeys; pathnode->innersortkeys = innersortkeys;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1676,7 +1676,8 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, ...@@ -1676,7 +1676,8 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
* Verify it's a valid ordering operator, and determine * Verify it's a valid ordering operator, and determine
* whether to consider it like ASC or DESC. * 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, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("operator %s is not a valid ordering operator", errmsg("operator %s is not a valid ordering operator",
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 * NOTES
* Eventually, the index information should go through here, too. * Eventually, the index information should go through here, too.
...@@ -286,7 +286,7 @@ get_op_mergejoin_info(Oid eq_op, Oid *left_sortop, ...@@ -286,7 +286,7 @@ get_op_mergejoin_info(Oid eq_op, Oid *left_sortop,
#endif #endif
/* /*
* get_op_compare_function * get_compare_function_for_ordering_op
* Get the OID of the datatype-specific btree comparison function * Get the OID of the datatype-specific btree comparison function
* associated with an ordering operator (a "<" or ">" operator). * associated with an ordering operator (a "<" or ">" operator).
* *
...@@ -298,7 +298,7 @@ get_op_mergejoin_info(Oid eq_op, Oid *left_sortop, ...@@ -298,7 +298,7 @@ get_op_mergejoin_info(Oid eq_op, Oid *left_sortop,
* (This indicates that the operator is not a valid ordering operator.) * (This indicates that the operator is not a valid ordering operator.)
*/ */
bool 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; bool result = false;
CatCList *catlist; CatCList *catlist;
...@@ -357,6 +357,177 @@ get_op_compare_function(Oid opno, Oid *cmpfunc, bool *reverse) ...@@ -357,6 +357,177 @@ get_op_compare_function(Oid opno, Oid *cmpfunc, bool *reverse)
return result; 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_op_hash_function
* Get the OID of the datatype-specific hash function associated with * Get the OID of the datatype-specific hash function associated with
...@@ -485,6 +656,45 @@ get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats) ...@@ -485,6 +656,45 @@ get_op_btree_interpretation(Oid opno, List **opfamilies, List **opstrats)
ReleaseSysCacheList(catlist); 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 ---------- */ /* ---------- AMPROC CACHES ---------- */
......
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -550,8 +550,8 @@ tuplesort_begin_heap(TupleDesc tupDesc, ...@@ -550,8 +550,8 @@ tuplesort_begin_heap(TupleDesc tupDesc,
AssertArg(attNums[i] != 0); AssertArg(attNums[i] != 0);
AssertArg(sortOperators[i] != 0); AssertArg(sortOperators[i] != 0);
if (!get_op_compare_function(sortOperators[i], if (!get_compare_function_for_ordering_op(sortOperators[i],
&sortFunction, &reverse)) &sortFunction, &reverse))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
sortOperators[i]); sortOperators[i]);
...@@ -643,8 +643,8 @@ tuplesort_begin_datum(Oid datumType, ...@@ -643,8 +643,8 @@ tuplesort_begin_datum(Oid datumType,
state->datumType = datumType; state->datumType = datumType;
/* lookup the ordering function */ /* lookup the ordering function */
if (!get_op_compare_function(sortOperator, if (!get_compare_function_for_ordering_op(sortOperator,
&sortFunction, &reverse)) &sortFunction, &reverse))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
sortOperator); sortOperator);
fmgr_info(sortFunction, &state->sortOpFn); fmgr_info(sortFunction, &state->sortOpFn);
...@@ -2106,8 +2106,8 @@ SelectSortFunction(Oid sortOperator, ...@@ -2106,8 +2106,8 @@ SelectSortFunction(Oid sortOperator,
{ {
bool reverse; bool reverse;
if (!get_op_compare_function(sortOperator, if (!get_compare_function_for_ordering_op(sortOperator,
sortFunction, &reverse)) sortFunction, &reverse))
elog(ERROR, "operator %u is not a valid ordering operator", elog(ERROR, "operator %u is not a valid ordering operator",
sortOperator); sortOperator);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -84,14 +84,12 @@ extern bool execTuplesUnequal(TupleTableSlot *slot1, ...@@ -84,14 +84,12 @@ extern bool execTuplesUnequal(TupleTableSlot *slot1,
AttrNumber *matchColIdx, AttrNumber *matchColIdx,
FmgrInfo *eqfunctions, FmgrInfo *eqfunctions,
MemoryContext evalContext); MemoryContext evalContext);
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc, extern FmgrInfo *execTuplesMatchPrepare(int numCols,
int numCols, Oid *eqOperators);
AttrNumber *matchColIdx); extern void execTuplesHashPrepare(int numCols,
extern void execTuplesHashPrepare(TupleDesc tupdesc, Oid *eqOperators,
int numCols, FmgrInfo **eqFunctions,
AttrNumber *matchColIdx, FmgrInfo **hashFunctions);
FmgrInfo **eqfunctions,
FmgrInfo **hashfunctions);
extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions, FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions, FmgrInfo *hashfunctions,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -346,14 +346,23 @@ typedef struct NestLoop ...@@ -346,14 +346,23 @@ typedef struct NestLoop
/* ---------------- /* ----------------
* merge join node * 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 typedef struct MergeJoin
{ {
Join join; Join join;
List *mergeclauses; /* mergeclauses as expression trees */ List *mergeclauses; /* mergeclauses as expression trees */
List *mergefamilies; /* OID list of btree opfamilies */ /* these are arrays, but have the same length as the mergeclauses list: */
List *mergestrategies; /* integer list of btree strategies */ Oid *mergeFamilies; /* per-clause OIDs of btree opfamilies */
int *mergeStrategies; /* per-clause ordering (ASC or DESC) */
bool *mergeNullsFirst; /* per-clause nulls ordering */
} MergeJoin; } MergeJoin;
/* ---------------- /* ----------------
...@@ -399,6 +408,7 @@ typedef struct Group ...@@ -399,6 +408,7 @@ typedef struct Group
Plan plan; Plan plan;
int numCols; /* number of grouping columns */ int numCols; /* number of grouping columns */
AttrNumber *grpColIdx; /* their indexes in the target list */ AttrNumber *grpColIdx; /* their indexes in the target list */
Oid *grpOperators; /* equality operators to compare with */
} Group; } Group;
/* --------------- /* ---------------
...@@ -428,6 +438,7 @@ typedef struct Agg ...@@ -428,6 +438,7 @@ typedef struct Agg
AggStrategy aggstrategy; AggStrategy aggstrategy;
int numCols; /* number of grouping columns */ int numCols; /* number of grouping columns */
AttrNumber *grpColIdx; /* their indexes in the target list */ AttrNumber *grpColIdx; /* their indexes in the target list */
Oid *grpOperators; /* equality operators to compare with */
long numGroups; /* estimated number of groups in input */ long numGroups; /* estimated number of groups in input */
} Agg; } Agg;
...@@ -439,7 +450,8 @@ typedef struct Unique ...@@ -439,7 +450,8 @@ typedef struct Unique
{ {
Plan plan; Plan plan;
int numCols; /* number of columns to check for uniqueness */ 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; } Unique;
/* ---------------- /* ----------------
...@@ -470,8 +482,9 @@ typedef struct SetOp ...@@ -470,8 +482,9 @@ typedef struct SetOp
SetOpCmd cmd; /* what to do */ SetOpCmd cmd; /* what to do */
int numCols; /* number of columns to check for int numCols; /* number of columns to check for
* duplicate-ness */ * duplicate-ness */
AttrNumber *dupColIdx; /* indexes into the target list */ AttrNumber *dupColIdx; /* their indexes in the target list */
AttrNumber flagColIdx; Oid *dupOperators; /* equality operators to compare with */
AttrNumber flagColIdx; /* where is the flag column, if any */
} SetOp; } SetOp;
/* ---------------- /* ----------------
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -618,9 +618,11 @@ typedef JoinPath NestPath; ...@@ -618,9 +618,11 @@ typedef JoinPath NestPath;
* A mergejoin path has these fields. * A mergejoin path has these fields.
* *
* path_mergeclauses lists the clauses (in the form of RestrictInfos) * path_mergeclauses lists the clauses (in the form of RestrictInfos)
* that will be used in the merge. The parallel lists path_mergefamilies * that will be used in the merge. The parallel arrays path_mergeFamilies,
* and path_mergestrategies specify the merge semantics for each clause * path_mergeStrategies, and path_mergeNullsFirst specify the merge semantics
* (in effect, defining the relevant sort ordering for each clause). * 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 * Note that the mergeclauses are a subset of the parent relation's
* restriction-clause list. Any join clauses that are not mergejoinable * restriction-clause list. Any join clauses that are not mergejoinable
...@@ -637,8 +639,10 @@ typedef struct MergePath ...@@ -637,8 +639,10 @@ typedef struct MergePath
{ {
JoinPath jpath; JoinPath jpath;
List *path_mergeclauses; /* join clauses to be used for merge */ List *path_mergeclauses; /* join clauses to be used for merge */
List *path_mergefamilies; /* OID list of btree opfamilies */ /* these are arrays, but have the same length as the mergeclauses list: */
List *path_mergestrategies; /* integer list of btree strategies */ 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 *outersortkeys; /* keys for explicit sort, if any */
List *innersortkeys; /* keys for explicit sort, if any */ List *innersortkeys; /* keys for explicit sort, if any */
} MergePath; } MergePath;
...@@ -885,6 +889,9 @@ typedef struct OuterJoinInfo ...@@ -885,6 +889,9 @@ typedef struct OuterJoinInfo
* the order of joining and use special join methods at some join points. * 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. * We record information about each such IN clause in an InClauseInfo struct.
* These structs are kept in the PlannerInfo node's in_info_list. * 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 typedef struct InClauseInfo
...@@ -893,11 +900,7 @@ typedef struct InClauseInfo ...@@ -893,11 +900,7 @@ typedef struct InClauseInfo
Relids lefthand; /* base relids in lefthand expressions */ Relids lefthand; /* base relids in lefthand expressions */
Relids righthand; /* base relids coming from the subselect */ Relids righthand; /* base relids coming from the subselect */
List *sub_targetlist; /* targetlist of original RHS subquery */ List *sub_targetlist; /* targetlist of original RHS subquery */
List *in_operators; /* OIDs of the IN's equality operator(s) */
/*
* Note: sub_targetlist is just a list of Vars or expressions; it does not
* contain TargetEntry nodes.
*/
} InClauseInfo; } InClauseInfo;
/* /*
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -71,8 +71,9 @@ extern MergePath *create_mergejoin_path(PlannerInfo *root, ...@@ -71,8 +71,9 @@ extern MergePath *create_mergejoin_path(PlannerInfo *root,
List *restrict_clauses, List *restrict_clauses,
List *pathkeys, List *pathkeys,
List *mergeclauses, List *mergeclauses,
List *mergefamilies, Oid *mergefamilies,
List *mergestrategies, int *mergestrategies,
bool *mergenullsfirst,
List *outersortkeys, List *outersortkeys,
List *innersortkeys); List *innersortkeys);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -44,11 +44,11 @@ extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls, ...@@ -44,11 +44,11 @@ extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls,
AttrNumber *grpColIdx, Plan *lefttree); AttrNumber *grpColIdx, Plan *lefttree);
extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual, extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual,
AggStrategy aggstrategy, AggStrategy aggstrategy,
int numGroupCols, AttrNumber *grpColIdx, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
long numGroups, int numAggs, long numGroups, int numAggs,
Plan *lefttree); Plan *lefttree);
extern Group *make_group(PlannerInfo *root, List *tlist, List *qual, extern Group *make_group(PlannerInfo *root, List *tlist, List *qual,
int numGroupCols, AttrNumber *grpColIdx, int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
double numGroups, double numGroups,
Plan *lefttree); Plan *lefttree);
extern Material *make_material(Plan *lefttree); extern Material *make_material(Plan *lefttree);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,10 +37,15 @@ extern Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, ...@@ -37,10 +37,15 @@ extern Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
int16 strategy); int16 strategy);
extern bool get_op_mergejoin_info(Oid eq_op, Oid *left_sortop, extern bool get_op_mergejoin_info(Oid eq_op, Oid *left_sortop,
Oid *right_sortop, Oid *opfamily); 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 Oid get_op_hash_function(Oid opno);
extern void get_op_btree_interpretation(Oid opno, extern void get_op_btree_interpretation(Oid opno,
List **opfamilies, List **opstrats); 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, extern Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype,
int16 procnum); int16 procnum);
extern char *get_attname(Oid relid, AttrNumber attnum); extern char *get_attname(Oid relid, AttrNumber attnum);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment