Commit ed5003c5 authored by Tom Lane's avatar Tom Lane

First cut at full support for OUTER JOINs. There are still a few loose

ends to clean up (see my message of same date to pghackers), but mostly
it works.  INITDB REQUIRED!
parent b5c0ab27
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.143 2000/09/12 04:49:06 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.144 2000/09/12 21:06:46 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1538,11 +1538,9 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin, ...@@ -1538,11 +1538,9 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
*/ */
rte = makeNode(RangeTblEntry); rte = makeNode(RangeTblEntry);
rte->relname = RelationGetRelationName(rel); rte->relname = RelationGetRelationName(rel);
#ifndef DISABLE_EREF
rte->ref = makeNode(Attr);
rte->ref->relname = RelationGetRelationName(rel);
#endif
rte->relid = RelationGetRelid(rel); rte->relid = RelationGetRelid(rel);
rte->eref = makeNode(Attr);
rte->eref->relname = RelationGetRelationName(rel);
rte->inh = false; rte->inh = false;
rte->inFromCl = true; rte->inFromCl = true;
rte->skipAcl = false; rte->skipAcl = false;
...@@ -1623,11 +1621,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin) ...@@ -1623,11 +1621,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
*/ */
rte = makeNode(RangeTblEntry); rte = makeNode(RangeTblEntry);
rte->relname = RelationGetRelationName(rel); rte->relname = RelationGetRelationName(rel);
#ifndef DISABLE_EREF
rte->ref = makeNode(Attr);
rte->ref->relname = RelationGetRelationName(rel);
#endif
rte->relid = RelationGetRelid(rel); rte->relid = RelationGetRelid(rel);
rte->eref = makeNode(Attr);
rte->eref->relname = RelationGetRelationName(rel);
rte->inh = false; rte->inh = false;
rte->inFromCl = true; rte->inFromCl = true;
rte->skipAcl = false; rte->skipAcl = false;
...@@ -1723,6 +1719,7 @@ AddRelationRawConstraints(Relation rel, ...@@ -1723,6 +1719,7 @@ AddRelationRawConstraints(Relation rel,
int numoldchecks; int numoldchecks;
ConstrCheck *oldchecks; ConstrCheck *oldchecks;
ParseState *pstate; ParseState *pstate;
RangeTblEntry *rte;
int numchecks; int numchecks;
List *listptr; List *listptr;
Relation relrel; Relation relrel;
...@@ -1752,7 +1749,8 @@ AddRelationRawConstraints(Relation rel, ...@@ -1752,7 +1749,8 @@ AddRelationRawConstraints(Relation rel,
*/ */
pstate = make_parsestate(NULL); pstate = make_parsestate(NULL);
makeRangeTable(pstate, NULL); makeRangeTable(pstate, NULL);
addRangeTableEntry(pstate, relname, makeAttr(relname, NULL), false, true, true); rte = addRangeTableEntry(pstate, relname, NULL, false, true);
addRTEtoJoinTree(pstate, rte);
/* /*
* Process column default expressions. * Process column default expressions.
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.63 2000/08/04 06:12:11 inoue Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.64 2000/09/12 21:06:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -259,7 +259,6 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno) ...@@ -259,7 +259,6 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno)
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
Assert(newattno != NULL);
if (var->varlevelsup == 0 && var->varno == 1) if (var->varlevelsup == 0 && var->varno == 1)
{ {
/* /*
...@@ -270,18 +269,19 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno) ...@@ -270,18 +269,19 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno)
*/ */
Assert(newattno[var->varattno - 1] > 0); Assert(newattno[var->varattno - 1] > 0);
var->varattno = newattno[var->varattno - 1]; var->varattno = newattno[var->varattno - 1];
return true;
} }
else return false;
return false;
} }
return expression_tree_walker(node, change_varattnos_walker, (void *)newattno); return expression_tree_walker(node, change_varattnos_walker,
(void *) newattno);
} }
static bool static bool
change_varattnos_of_a_node(Node *node, const AttrNumber *newattno) change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
{ {
return expression_tree_walker(node, change_varattnos_walker, (void *)newattno); return change_varattnos_walker(node, newattno);
} }
/* /*
* MergeAttributes * MergeAttributes
* Returns new schema given initial schema and supers. * Returns new schema given initial schema and supers.
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.57 2000/06/18 22:43:58 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.58 2000/09/12 21:06:47 tgl Exp $
* *
*/ */
...@@ -229,21 +229,21 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) ...@@ -229,21 +229,21 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
appendStringInfo(str, " on %s", appendStringInfo(str, " on %s",
stringStringInfo(rte->relname)); stringStringInfo(rte->relname));
if (rte->ref != NULL) if (rte->alias != NULL)
{ {
if ((strcmp(rte->ref->relname, rte->relname) != 0) if ((strcmp(rte->alias->relname, rte->relname) != 0)
|| (length(rte->ref->attrs) > 0)) || (length(rte->alias->attrs) > 0))
{ {
appendStringInfo(str, " %s", appendStringInfo(str, " %s",
stringStringInfo(rte->ref->relname)); stringStringInfo(rte->alias->relname));
if (length(rte->ref->attrs) > 0) if (length(rte->alias->attrs) > 0)
{ {
List *c; List *c;
int firstEntry = true; int firstEntry = true;
appendStringInfo(str, " ("); appendStringInfo(str, " (");
foreach(c, rte->ref->attrs) foreach(c, rte->alias->attrs)
{ {
if (!firstEntry) if (!firstEntry)
{ {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: view.c,v 1.47 2000/09/12 04:49:07 momjian Exp $ * $Id: view.c,v 1.48 2000/09/12 21:06:47 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -116,12 +116,14 @@ char * ...@@ -116,12 +116,14 @@ char *
MakeRetrieveViewRuleName(char *viewName) MakeRetrieveViewRuleName(char *viewName)
{ {
char *buf; char *buf;
#ifdef MULTIBYTE
int len;
#endif
buf = palloc(strlen(viewName) + 5); buf = palloc(strlen(viewName) + 5);
snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName); snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName);
#ifdef MULTIBYTE #ifdef MULTIBYTE
int len;
len = pg_mbcliplen(buf,strlen(buf),NAMEDATALEN-1); len = pg_mbcliplen(buf,strlen(buf),NAMEDATALEN-1);
buf[len] = '\0'; buf[len] = '\0';
#else #else
...@@ -203,6 +205,10 @@ DefineViewRules(char *viewName, Query *viewParse) ...@@ -203,6 +205,10 @@ DefineViewRules(char *viewName, Query *viewParse)
* Of course we must also increase the 'varnos' of all the Var nodes * Of course we must also increase the 'varnos' of all the Var nodes
* by 2... * by 2...
* *
* These extra RT entries are not actually used in the query, obviously.
* We add them so that views look the same as ON SELECT rules ---
* the rule rewriter assumes that ALL rules have OLD and NEW RTEs.
*
* NOTE: these are destructive changes. It would be difficult to * NOTE: these are destructive changes. It would be difficult to
* make a complete copy of the parse tree and make the changes * make a complete copy of the parse tree and make the changes
* in the copy. * in the copy.
...@@ -211,43 +217,32 @@ DefineViewRules(char *viewName, Query *viewParse) ...@@ -211,43 +217,32 @@ DefineViewRules(char *viewName, Query *viewParse)
static void static void
UpdateRangeTableOfViewParse(char *viewName, Query *viewParse) UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
{ {
List *old_rt;
List *new_rt; List *new_rt;
RangeTblEntry *rt_entry1, RangeTblEntry *rt_entry1,
*rt_entry2; *rt_entry2;
/*
* first offset all var nodes by 2
*/
OffsetVarNodes((Node *) viewParse->targetList, 2, 0);
OffsetVarNodes(viewParse->qual, 2, 0);
OffsetVarNodes(viewParse->havingQual, 2, 0);
/*
* find the old range table...
*/
old_rt = viewParse->rtable;
/* /*
* create the 2 new range table entries and form the new range * create the 2 new range table entries and form the new range
* table... OLD first, then NEW.... * table... OLD first, then NEW....
*/ */
rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, rt_entry1 = addRangeTableEntry(NULL, viewName,
makeAttr("*OLD*", NULL), makeAttr("*OLD*", NULL),
FALSE, FALSE, FALSE); false, false);
rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, rt_entry2 = addRangeTableEntry(NULL, viewName,
makeAttr("*NEW*", NULL), makeAttr("*NEW*", NULL),
FALSE, FALSE, FALSE); false, false);
new_rt = lcons(rt_entry2, old_rt); new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
new_rt = lcons(rt_entry1, new_rt);
/* /*
* Now the tricky part.... Update the range table in place... Be * Now the tricky part.... Update the range table in place... Be
* careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE! * careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
*/ */
viewParse->rtable = new_rt; viewParse->rtable = new_rt;
/*
* now offset all var nodes by 2, and jointree RT indexes too.
*/
OffsetVarNodes((Node *) viewParse, 2, 0);
} }
/*------------------------------------------------------------------- /*-------------------------------------------------------------------
...@@ -270,7 +265,7 @@ DefineView(char *viewName, Query *viewParse) ...@@ -270,7 +265,7 @@ DefineView(char *viewName, Query *viewParse)
viewTlist = viewParse->targetList; viewTlist = viewParse->targetList;
/* /*
* Create the "view" relation NOTE: if it already exists, the xaxt * Create the "view" relation NOTE: if it already exists, the xact
* will be aborted. * will be aborted.
*/ */
DefineVirtualRelation(viewName, viewTlist); DefineVirtualRelation(viewName, viewTlist);
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.126 2000/09/12 04:49:08 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.127 2000/09/12 21:06:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -399,16 +399,17 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan) ...@@ -399,16 +399,17 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
* If we have a result relation, determine whether the result rel is * If we have a result relation, determine whether the result rel is
* scanned or merely written. If scanned, we will insist on read * scanned or merely written. If scanned, we will insist on read
* permission as well as modify permission. * permission as well as modify permission.
*
* Note: it might look faster to apply rangeTableEntry_used(), but
* that's not correct since it will trigger on jointree references
* to the RTE. We only want to know about actual Var nodes.
*/ */
if (resultRelation > 0) if (resultRelation > 0)
{ {
List *qvars = pull_varnos(parseTree->qual); List *qvars = pull_varnos((Node *) parseTree);
List *tvars = pull_varnos((Node *) parseTree->targetList);
resultIsScanned = (intMember(resultRelation, qvars) || resultIsScanned = intMember(resultRelation, qvars);
intMember(resultRelation, tvars));
freeList(qvars); freeList(qvars);
freeList(tvars);
} }
/* /*
...@@ -571,8 +572,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation, ...@@ -571,8 +572,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
bool isResultRelation, bool resultIsScanned) bool isResultRelation, bool resultIsScanned)
{ {
char *relName; char *relName;
Oid userid;
int32 aclcheck_result; int32 aclcheck_result;
Oid userid;
if (rte->skipAcl) if (rte->skipAcl)
{ {
...@@ -703,13 +704,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -703,13 +704,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
*/ */
RelationInfo *resultRelationInfo; RelationInfo *resultRelationInfo;
Index resultRelationIndex; Index resultRelationIndex;
RangeTblEntry *rtentry;
Oid resultRelationOid; Oid resultRelationOid;
Relation resultRelationDesc; Relation resultRelationDesc;
resultRelationIndex = resultRelation; resultRelationIndex = resultRelation;
rtentry = rt_fetch(resultRelationIndex, rangeTable); resultRelationOid = getrelid(resultRelationIndex, rangeTable);
resultRelationOid = rtentry->relid;
resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock); resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE) if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
...@@ -770,7 +769,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -770,7 +769,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
if (!(rm->info & ROW_MARK_FOR_UPDATE)) if (!(rm->info & ROW_MARK_FOR_UPDATE))
continue; continue;
relid = rt_fetch(rm->rti, rangeTable)->relid; relid = getrelid(rm->rti, rangeTable);
relation = heap_open(relid, RowShareLock); relation = heap_open(relid, RowShareLock);
erm = (execRowMark *) palloc(sizeof(execRowMark)); erm = (execRowMark *) palloc(sizeof(execRowMark));
erm->relation = relation; erm->relation = relation;
...@@ -1623,10 +1622,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ...@@ -1623,10 +1622,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
rte = makeNode(RangeTblEntry); rte = makeNode(RangeTblEntry);
rte->relname = RelationGetRelationName(rel); rte->relname = RelationGetRelationName(rel);
rte->ref = makeNode(Attr);
rte->ref->relname = rte->relname;
rte->relid = RelationGetRelid(rel); rte->relid = RelationGetRelid(rel);
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */ rte->eref = makeNode(Attr);
rte->eref->relname = rte->relname;
/* inh, inFromCl, skipAcl won't be used, leave them zero */
/* Set up single-entry range table */ /* Set up single-entry range table */
econtext->ecxt_range_table = lcons(rte, NIL); econtext->ecxt_range_table = lcons(rte, NIL);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.38 2000/07/12 02:37:02 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.39 2000/09/12 21:06:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -46,11 +46,10 @@ ...@@ -46,11 +46,10 @@
* type of tuple in a slot * type of tuple in a slot
* *
* CONVENIENCE INITIALIZATION ROUTINES * CONVENIENCE INITIALIZATION ROUTINES
* ExecInitResultTupleSlot \ convience routines to initialize * ExecInitResultTupleSlot \ convenience routines to initialize
* ExecInitScanTupleSlot \ the various tuple slots for nodes * ExecInitScanTupleSlot \ the various tuple slots for nodes
* ExecInitMarkedTupleSlot / which store copies of tuples. * ExecInitExtraTupleSlot / which store copies of tuples.
* ExecInitOuterTupleSlot / * ExecInitNullTupleSlot /
* ExecInitHashTupleSlot /
* *
* old routines: * old routines:
* ExecGetTupType - get type of tuple returned by this node * ExecGetTupType - get type of tuple returned by this node
...@@ -560,10 +559,11 @@ ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */ ...@@ -560,10 +559,11 @@ ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* -------------------------------- /* --------------------------------
* ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot * ExecInit{Result,Scan,Extra}TupleSlot
* *
* These are convenience routines to initialize the specfied slot * These are convenience routines to initialize the specified slot
* in nodes inheriting the appropriate state. * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
* is used for initializing special-purpose slots.
* -------------------------------- * --------------------------------
*/ */
#define INIT_SLOT_DEFS \ #define INIT_SLOT_DEFS \
...@@ -583,7 +583,7 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate) ...@@ -583,7 +583,7 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
{ {
INIT_SLOT_DEFS; INIT_SLOT_DEFS;
INIT_SLOT_ALLOC; INIT_SLOT_ALLOC;
commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot; commonstate->cs_ResultTupleSlot = slot;
} }
/* ---------------- /* ----------------
...@@ -595,50 +595,51 @@ ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate) ...@@ -595,50 +595,51 @@ ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
{ {
INIT_SLOT_DEFS; INIT_SLOT_DEFS;
INIT_SLOT_ALLOC; INIT_SLOT_ALLOC;
commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot; commonscanstate->css_ScanTupleSlot = slot;
} }
#ifdef NOT_USED
/* ---------------- /* ----------------
* ExecInitMarkedTupleSlot * ExecInitExtraTupleSlot
* ---------------- * ----------------
*/ */
void TupleTableSlot *
ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate) ExecInitExtraTupleSlot(EState *estate)
{ {
INIT_SLOT_DEFS; INIT_SLOT_DEFS;
INIT_SLOT_ALLOC; INIT_SLOT_ALLOC;
mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot; return slot;
} }
#endif
/* ---------------- /* ----------------
* ExecInitOuterTupleSlot * ExecInitNullTupleSlot
*
* Build a slot containing an all-nulls tuple of the given type.
* This is used as a substitute for an input tuple when performing an
* outer join.
* ---------------- * ----------------
*/ */
void TupleTableSlot *
ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate) ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
{ {
INIT_SLOT_DEFS; TupleTableSlot* slot = ExecInitExtraTupleSlot(estate);
INIT_SLOT_ALLOC; /*
hashstate->hj_OuterTupleSlot = slot; * Since heap_getattr() will treat attributes beyond a tuple's t_natts
} * as being NULL, we can make an all-nulls tuple just by making it be of
* zero length. However, the slot descriptor must match the real tupType.
*/
HeapTuple nullTuple;
Datum values[1];
char nulls[1];
static struct tupleDesc NullTupleDesc; /* we assume this inits to
* zeroes */
/* ---------------- ExecSetSlotDescriptor(slot, tupType);
* ExecInitHashTupleSlot
* ---------------- nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
*/
#ifdef NOT_USED return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
void
ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
{
INIT_SLOT_DEFS;
INIT_SLOT_ALLOC;
hashstate->hj_HashTupleSlot = slot;
} }
#endif
static TupleTableSlot * static TupleTableSlot *
NodeGetResultTupleSlot(Plan *node) NodeGetResultTupleSlot(Plan *node)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.65 2000/08/22 04:06:19 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.66 2000/09/12 21:06:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -275,53 +275,17 @@ void ...@@ -275,53 +275,17 @@ void
ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate) ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
{ {
List *targetList; List *targetList;
int i; TupleDesc tupDesc;
int len; int len;
List *tl;
TargetEntry *tle;
List *fjtl;
TupleDesc origTupDesc;
targetList = node->targetlist; targetList = node->targetlist;
origTupDesc = ExecTypeFromTL(targetList); tupDesc = ExecTypeFromTL(targetList);
len = ExecTargetListLength(targetList); len = ExecTargetListLength(targetList);
fjtl = NIL;
tl = targetList;
i = 0;
while (tl != NIL || fjtl != NIL)
{
if (fjtl != NIL)
{
tle = lfirst(fjtl);
fjtl = lnext(fjtl);
}
else
{
tle = lfirst(tl);
tl = lnext(tl);
}
#ifdef SETS_FIXED
if (!tl_is_resdom(tle))
{
Fjoin *fj = (Fjoin *) lfirst(tle);
/* it is a FJoin */
fjtl = lnext(tle);
tle = fj->fj_innerNode;
}
#endif
i++;
}
if (len > 0) if (len > 0)
{ ExecAssignResultType(commonstate, tupDesc);
ExecAssignResultType(commonstate,
origTupDesc);
}
else else
ExecAssignResultType(commonstate, ExecAssignResultType(commonstate, (TupleDesc) NULL);
(TupleDesc) NULL);
} }
/* ---------------- /* ----------------
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.33 2000/08/24 03:29:03 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.34 2000/09/12 21:06:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,7 +50,8 @@ ExecHashJoin(HashJoin *node) ...@@ -50,7 +50,8 @@ ExecHashJoin(HashJoin *node)
Hash *hashNode; Hash *hashNode;
List *hjclauses; List *hjclauses;
Expr *clause; Expr *clause;
List *qual; List *joinqual;
List *otherqual;
ScanDirection dir; ScanDirection dir;
TupleTableSlot *inntuple; TupleTableSlot *inntuple;
Node *outerVar; Node *outerVar;
...@@ -70,11 +71,12 @@ ExecHashJoin(HashJoin *node) ...@@ -70,11 +71,12 @@ ExecHashJoin(HashJoin *node)
hjstate = node->hashjoinstate; hjstate = node->hashjoinstate;
hjclauses = node->hashclauses; hjclauses = node->hashclauses;
clause = lfirst(hjclauses); clause = lfirst(hjclauses);
estate = node->join.state; estate = node->join.plan.state;
qual = node->join.qual; joinqual = node->join.joinqual;
otherqual = node->join.plan.qual;
hashNode = (Hash *) innerPlan(node); hashNode = (Hash *) innerPlan(node);
outerNode = outerPlan(node); outerNode = outerPlan(node);
hashPhaseDone = node->hashdone; hashPhaseDone = hjstate->hj_hashdone;
dir = estate->es_direction; dir = estate->es_direction;
/* ----------------- /* -----------------
...@@ -132,7 +134,7 @@ ExecHashJoin(HashJoin *node) ...@@ -132,7 +134,7 @@ ExecHashJoin(HashJoin *node)
hashNode->hashstate->hashtable = hashtable; hashNode->hashstate->hashtable = hashtable;
innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node); innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
} }
node->hashdone = true; hjstate->hj_hashdone = true;
/* ---------------- /* ----------------
* Open temp files for outer batches, if needed. * Open temp files for outer batches, if needed.
* Note that file buffers are palloc'd in regular executor context. * Note that file buffers are palloc'd in regular executor context.
...@@ -153,11 +155,10 @@ ExecHashJoin(HashJoin *node) ...@@ -153,11 +155,10 @@ ExecHashJoin(HashJoin *node)
for (;;) for (;;)
{ {
/* /*
* if the current outer tuple is nil, get a new one * If we don't have an outer tuple, get the next one
*/ */
if (TupIsNull(outerTupleSlot)) if (hjstate->hj_NeedNewOuter)
{ {
outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode, outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
(Plan *) node, (Plan *) node,
...@@ -173,11 +174,15 @@ ExecHashJoin(HashJoin *node) ...@@ -173,11 +174,15 @@ ExecHashJoin(HashJoin *node)
return NULL; return NULL;
} }
hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
econtext->ecxt_outertuple = outerTupleSlot;
hjstate->hj_NeedNewOuter = false;
hjstate->hj_MatchedOuter = false;
/* /*
* now we have an outer tuple, find the corresponding bucket * now we have an outer tuple, find the corresponding bucket
* for this tuple from the hash table * for this tuple from the hash table
*/ */
econtext->ecxt_outertuple = outerTupleSlot;
hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext, hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
outerVar); outerVar);
hjstate->hj_CurTuple = NULL; hjstate->hj_CurTuple = NULL;
...@@ -205,7 +210,7 @@ ExecHashJoin(HashJoin *node) ...@@ -205,7 +210,7 @@ ExecHashJoin(HashJoin *node)
hashtable->outerBatchSize[batchno]++; hashtable->outerBatchSize[batchno]++;
ExecHashJoinSaveTuple(outerTupleSlot->val, ExecHashJoinSaveTuple(outerTupleSlot->val,
hashtable->outerBatchFile[batchno]); hashtable->outerBatchFile[batchno]);
ExecClearTuple(outerTupleSlot); hjstate->hj_NeedNewOuter = true;
continue; /* loop around for a new outer tuple */ continue; /* loop around for a new outer tuple */
} }
} }
...@@ -223,7 +228,7 @@ ExecHashJoin(HashJoin *node) ...@@ -223,7 +228,7 @@ ExecHashJoin(HashJoin *node)
break; /* out of matches */ break; /* out of matches */
/* /*
* we've got a match, but still need to test qpqual * we've got a match, but still need to test non-hashed quals
*/ */
inntuple = ExecStoreTuple(curtuple, inntuple = ExecStoreTuple(curtuple,
hjstate->hj_HashTupleSlot, hjstate->hj_HashTupleSlot,
...@@ -231,35 +236,77 @@ ExecHashJoin(HashJoin *node) ...@@ -231,35 +236,77 @@ ExecHashJoin(HashJoin *node)
false); /* don't pfree this tuple */ false); /* don't pfree this tuple */
econtext->ecxt_innertuple = inntuple; econtext->ecxt_innertuple = inntuple;
/* reset temp memory each time to avoid leaks from qpqual */ /* reset temp memory each time to avoid leaks from qual expr */
ResetExprContext(econtext); ResetExprContext(econtext);
/* ---------------- /* ----------------
* if we pass the qual, then save state for next call and * if we pass the qual, then save state for next call and
* have ExecProject form the projection, store it * have ExecProject form the projection, store it
* in the tuple table, and return the slot. * in the tuple table, and return the slot.
*
* Only the joinquals determine MatchedOuter status,
* but all quals must pass to actually return the tuple.
* ---------------- * ----------------
*/ */
if (ExecQual(qual, econtext, false)) if (ExecQual(joinqual, econtext, false))
{ {
TupleTableSlot *result; hjstate->hj_MatchedOuter = true;
hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; if (otherqual == NIL || ExecQual(otherqual, econtext, false))
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{ {
hjstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult); TupleTableSlot *result;
return result;
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
hjstate->jstate.cs_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
} }
} }
} }
/* ---------------- /* ----------------
* Now the current outer tuple has run out of matches, * Now the current outer tuple has run out of matches,
* so we free it and loop around to get a new outer tuple. * so check whether to emit a dummy outer-join tuple.
* If not, loop around to get a new outer tuple.
* ---------------- * ----------------
*/ */
ExecClearTuple(outerTupleSlot); hjstate->hj_NeedNewOuter = true;
if (! hjstate->hj_MatchedOuter &&
node->join.jointype == JOIN_LEFT)
{
/*
* We are doing an outer join and there were no join matches
* for this outer tuple. Generate a fake join tuple with
* nulls for the inner tuple, and return it if it passes
* the non-join quals.
*/
econtext->ecxt_innertuple = hjstate->hj_NullInnerTupleSlot;
if (ExecQual(otherqual, econtext, false))
{
/* ----------------
* qualification was satisfied so we project and
* return the slot containing the result tuple
* using ExecProject().
* ----------------
*/
TupleTableSlot *result;
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
hjstate->jstate.cs_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
}
} }
} }
...@@ -280,14 +327,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) ...@@ -280,14 +327,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
* assign the node's execution state * assign the node's execution state
* ---------------- * ----------------
*/ */
node->join.state = estate; node->join.plan.state = estate;
/* ---------------- /* ----------------
* create state structure * create state structure
* ---------------- * ----------------
*/ */
hjstate = makeNode(HashJoinState); hjstate = makeNode(HashJoinState);
node->hashjoinstate = hjstate; node->hashjoinstate = hjstate;
/* ---------------- /* ----------------
...@@ -298,14 +344,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) ...@@ -298,14 +344,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
*/ */
ExecAssignExprContext(estate, &hjstate->jstate); ExecAssignExprContext(estate, &hjstate->jstate);
#define HASHJOIN_NSLOTS 2
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &hjstate->jstate);
ExecInitOuterTupleSlot(estate, hjstate);
/* ---------------- /* ----------------
* initializes child nodes * initializes child nodes
* ---------------- * ----------------
...@@ -316,6 +354,28 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) ...@@ -316,6 +354,28 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
ExecInitNode(outerNode, estate, (Plan *) node); ExecInitNode(outerNode, estate, (Plan *) node);
ExecInitNode((Plan *) hashNode, estate, (Plan *) node); ExecInitNode((Plan *) hashNode, estate, (Plan *) node);
#define HASHJOIN_NSLOTS 3
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &hjstate->jstate);
hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
switch (node->join.jointype)
{
case JOIN_INNER:
break;
case JOIN_LEFT:
hjstate->hj_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetTupType((Plan *) hashNode));
break;
default:
elog(ERROR, "ExecInitHashJoin: unsupported join type %d",
(int) node->join.jointype);
}
/* ---------------- /* ----------------
* now for some voodoo. our temporary tuple slot * now for some voodoo. our temporary tuple slot
* is actually the result tuple slot of the Hash node * is actually the result tuple slot of the Hash node
...@@ -331,11 +391,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) ...@@ -331,11 +391,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
hjstate->hj_HashTupleSlot = slot; hjstate->hj_HashTupleSlot = slot;
} }
hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
/*
hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
*/
/* ---------------- /* ----------------
* initialize tuple type and projection info * initialize tuple type and projection info
...@@ -344,20 +399,25 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) ...@@ -344,20 +399,25 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate); ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate); ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
ExecGetTupType(outerNode));
/* ---------------- /* ----------------
* initialize hash-specific info * initialize hash-specific info
* ---------------- * ----------------
*/ */
node->hashdone = false; hjstate->hj_hashdone = false;
hjstate->hj_HashTable = (HashJoinTable) NULL; hjstate->hj_HashTable = (HashJoinTable) NULL;
hjstate->hj_CurBucketNo = 0; hjstate->hj_CurBucketNo = 0;
hjstate->hj_CurTuple = (HashJoinTuple) NULL; hjstate->hj_CurTuple = (HashJoinTuple) NULL;
hjstate->hj_InnerHashKey = (Node *) NULL; hjstate->hj_InnerHashKey = (Node *) NULL;
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL; hjstate->jstate.cs_OuterTupleSlot = NULL;
hjstate->jstate.cs_TupFromTlist = false; hjstate->jstate.cs_TupFromTlist = false;
hjstate->hj_NeedNewOuter = true;
hjstate->hj_MatchedOuter = false;
return TRUE; return TRUE;
} }
...@@ -646,10 +706,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent) ...@@ -646,10 +706,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
{ {
HashJoinState *hjstate = node->hashjoinstate; HashJoinState *hjstate = node->hashjoinstate;
if (!node->hashdone) if (!hjstate->hj_hashdone)
return; return;
node->hashdone = false; hjstate->hj_hashdone = false;
/* /*
* Unfortunately, currently we have to destroy hashtable in all * Unfortunately, currently we have to destroy hashtable in all
...@@ -667,6 +727,8 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent) ...@@ -667,6 +727,8 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL; hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
hjstate->jstate.cs_TupFromTlist = false; hjstate->jstate.cs_TupFromTlist = false;
hjstate->hj_NeedNewOuter = true;
hjstate->hj_MatchedOuter = false;
/* /*
* if chgParam of subnodes is not null then plans will be re-scanned * if chgParam of subnodes is not null then plans will be re-scanned
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.20 2000/08/24 03:29:03 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.21 2000/09/12 21:06:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,10 +62,10 @@ ExecNestLoop(NestLoop *node) ...@@ -62,10 +62,10 @@ ExecNestLoop(NestLoop *node)
NestLoopState *nlstate; NestLoopState *nlstate;
Plan *innerPlan; Plan *innerPlan;
Plan *outerPlan; Plan *outerPlan;
bool needNewOuterTuple;
TupleTableSlot *outerTupleSlot; TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot; TupleTableSlot *innerTupleSlot;
List *qual; List *joinqual;
List *otherqual;
ExprContext *econtext; ExprContext *econtext;
/* ---------------- /* ----------------
...@@ -75,9 +75,10 @@ ExecNestLoop(NestLoop *node) ...@@ -75,9 +75,10 @@ ExecNestLoop(NestLoop *node)
ENL1_printf("getting info from node"); ENL1_printf("getting info from node");
nlstate = node->nlstate; nlstate = node->nlstate;
qual = node->join.qual; joinqual = node->join.joinqual;
outerPlan = outerPlan(&node->join); otherqual = node->join.plan.qual;
innerPlan = innerPlan(&node->join); outerPlan = outerPlan((Plan *) node);
innerPlan = innerPlan((Plan *) node);
econtext = nlstate->jstate.cs_ExprContext; econtext = nlstate->jstate.cs_ExprContext;
/* ---------------- /* ----------------
...@@ -115,7 +116,7 @@ ExecNestLoop(NestLoop *node) ...@@ -115,7 +116,7 @@ ExecNestLoop(NestLoop *node)
/* ---------------- /* ----------------
* Ok, everything is setup for the join so now loop until * Ok, everything is setup for the join so now loop until
* we return a qualifying join tuple.. * we return a qualifying join tuple.
* ---------------- * ----------------
*/ */
ENL1_printf("entering main loop"); ENL1_printf("entering main loop");
...@@ -123,44 +124,14 @@ ExecNestLoop(NestLoop *node) ...@@ -123,44 +124,14 @@ ExecNestLoop(NestLoop *node)
for (;;) for (;;)
{ {
/* ---------------- /* ----------------
* The essential idea now is to get the next inner tuple * If we don't have an outer tuple, get the next one and
* and join it with the current outer tuple. * reset the inner scan.
* ---------------- * ----------------
*/ */
needNewOuterTuple = TupIsNull(outerTupleSlot); if (nlstate->nl_NeedNewOuter)
/* ----------------
* if we have an outerTuple, try to get the next inner tuple.
* ----------------
*/
if (!needNewOuterTuple)
{
ENL1_printf("getting new inner tuple");
innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
econtext->ecxt_innertuple = innerTupleSlot;
if (TupIsNull(innerTupleSlot))
{
ENL1_printf("no inner tuple, need new outer tuple");
needNewOuterTuple = true;
}
}
/* ----------------
* loop until we have a new outer tuple and a new
* inner tuple.
* ----------------
*/
while (needNewOuterTuple)
{ {
/* ----------------
* now try to get the next outer tuple
* ----------------
*/
ENL1_printf("getting new outer tuple"); ENL1_printf("getting new outer tuple");
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
econtext->ecxt_outertuple = outerTupleSlot;
/* ---------------- /* ----------------
* if there are no more outer tuples, then the join * if there are no more outer tuples, then the join
...@@ -175,12 +146,14 @@ ExecNestLoop(NestLoop *node) ...@@ -175,12 +146,14 @@ ExecNestLoop(NestLoop *node)
ENL1_printf("saving new outer tuple information"); ENL1_printf("saving new outer tuple information");
nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
econtext->ecxt_outertuple = outerTupleSlot;
nlstate->nl_NeedNewOuter = false;
nlstate->nl_MatchedOuter = false;
/* ---------------- /* ----------------
* now rescan the inner plan and get a new inner tuple * now rescan the inner plan
* ---------------- * ----------------
*/ */
ENL1_printf("rescanning inner plan"); ENL1_printf("rescanning inner plan");
/* /*
...@@ -189,48 +162,101 @@ ExecNestLoop(NestLoop *node) ...@@ -189,48 +162,101 @@ ExecNestLoop(NestLoop *node)
* expr context. * expr context.
*/ */
ExecReScan(innerPlan, econtext, (Plan *) node); ExecReScan(innerPlan, econtext, (Plan *) node);
}
/* ----------------
* we have an outerTuple, try to get the next inner tuple.
* ----------------
*/
ENL1_printf("getting new inner tuple");
innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
econtext->ecxt_innertuple = innerTupleSlot;
ENL1_printf("getting new inner tuple"); if (TupIsNull(innerTupleSlot))
{
ENL1_printf("no inner tuple, need new outer tuple");
innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); nlstate->nl_NeedNewOuter = true;
econtext->ecxt_innertuple = innerTupleSlot;
if (TupIsNull(innerTupleSlot)) if (! nlstate->nl_MatchedOuter &&
ENL1_printf("couldn't get inner tuple - need new outer tuple"); node->join.jointype == JOIN_LEFT)
else
{ {
ENL1_printf("got inner and outer tuples"); /*
needNewOuterTuple = false; * We are doing an outer join and there were no join matches
* for this outer tuple. Generate a fake join tuple with
* nulls for the inner tuple, and return it if it passes
* the non-join quals.
*/
econtext->ecxt_innertuple = nlstate->nl_NullInnerTupleSlot;
ENL1_printf("testing qualification for outer-join tuple");
if (ExecQual(otherqual, econtext, false))
{
/* ----------------
* qualification was satisfied so we project and
* return the slot containing the result tuple
* using ExecProject().
* ----------------
*/
TupleTableSlot *result;
ExprDoneCond isDone;
ENL1_printf("qualification succeeded, projecting tuple");
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
nlstate->jstate.cs_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
} }
} /* while (needNewOuterTuple) */ /*
* Otherwise just return to top of loop for a new outer tuple.
*/
continue;
}
/* ---------------- /* ----------------
* at this point we have a new pair of inner and outer * at this point we have a new pair of inner and outer
* tuples so we test the inner and outer tuples to see * tuples so we test the inner and outer tuples to see
* if they satisify the node's qualification. * if they satisfy the node's qualification.
*
* Only the joinquals determine MatchedOuter status,
* but all quals must pass to actually return the tuple.
* ---------------- * ----------------
*/ */
ENL1_printf("testing qualification"); ENL1_printf("testing qualification");
if (ExecQual((List *) qual, econtext, false)) if (ExecQual(joinqual, econtext, false))
{ {
/* ---------------- nlstate->nl_MatchedOuter = true;
* qualification was satisified so we project and
* return the slot containing the result tuple
* using ExecProject().
* ----------------
*/
TupleTableSlot *result;
ExprDoneCond isDone;
ENL1_printf("qualification succeeded, projecting tuple"); if (otherqual == NIL || ExecQual(otherqual, econtext, false))
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{ {
nlstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult); /* ----------------
return result; * qualification was satisfied so we project and
* return the slot containing the result tuple
* using ExecProject().
* ----------------
*/
TupleTableSlot *result;
ExprDoneCond isDone;
ENL1_printf("qualification succeeded, projecting tuple");
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
nlstate->jstate.cs_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
} }
} }
...@@ -264,7 +290,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent) ...@@ -264,7 +290,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
* assign execution state to node * assign execution state to node
* ---------------- * ----------------
*/ */
node->join.state = estate; node->join.plan.state = estate;
/* ---------------- /* ----------------
* create new nest loop state * create new nest loop state
...@@ -281,19 +307,33 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent) ...@@ -281,19 +307,33 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
*/ */
ExecAssignExprContext(estate, &nlstate->jstate); ExecAssignExprContext(estate, &nlstate->jstate);
#define NESTLOOP_NSLOTS 1
/* ---------------- /* ----------------
* tuple table initialization * now initialize children
* ---------------- * ----------------
*/ */
ExecInitResultTupleSlot(estate, &nlstate->jstate); ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
#define NESTLOOP_NSLOTS 2
/* ---------------- /* ----------------
* now initialize children * tuple table initialization
* ---------------- * ----------------
*/ */
ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); ExecInitResultTupleSlot(estate, &nlstate->jstate);
ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
switch (node->join.jointype)
{
case JOIN_INNER:
break;
case JOIN_LEFT:
nlstate->nl_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetTupType(innerPlan((Plan*) node)));
break;
default:
elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
(int) node->join.jointype);
}
/* ---------------- /* ----------------
* initialize tuple type and projection info * initialize tuple type and projection info
...@@ -308,6 +348,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent) ...@@ -308,6 +348,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
*/ */
nlstate->jstate.cs_OuterTupleSlot = NULL; nlstate->jstate.cs_OuterTupleSlot = NULL;
nlstate->jstate.cs_TupFromTlist = false; nlstate->jstate.cs_TupFromTlist = false;
nlstate->nl_NeedNewOuter = true;
nlstate->nl_MatchedOuter = false;
NL1_printf("ExecInitNestLoop: %s\n", NL1_printf("ExecInitNestLoop: %s\n",
"node initialized"); "node initialized");
...@@ -394,4 +436,6 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent) ...@@ -394,4 +436,6 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
/* let outerPlan to free its result tuple ... */ /* let outerPlan to free its result tuple ... */
nlstate->jstate.cs_OuterTupleSlot = NULL; nlstate->jstate.cs_OuterTupleSlot = NULL;
nlstate->jstate.cs_TupFromTlist = false; nlstate->jstate.cs_TupFromTlist = false;
nlstate->nl_NeedNewOuter = true;
nlstate->nl_MatchedOuter = false;
} }
...@@ -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
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.120 2000/08/11 23:45:31 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.121 2000/09/12 21:06:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -311,8 +311,12 @@ _copyTidScan(TidScan *from) ...@@ -311,8 +311,12 @@ _copyTidScan(TidScan *from)
static void static void
CopyJoinFields(Join *from, Join *newnode) CopyJoinFields(Join *from, Join *newnode)
{ {
/* nothing extra */ newnode->jointype = from->jointype;
return; Node_Copy(from, newnode, joinqual);
/* subPlan list must point to subplans in the new subtree, not the old */
if (from->plan.subPlan != NIL)
newnode->plan.subPlan = nconc(newnode->plan.subPlan,
pull_subplans((Node *) newnode->joinqual));
} }
...@@ -381,8 +385,8 @@ _copyMergeJoin(MergeJoin *from) ...@@ -381,8 +385,8 @@ _copyMergeJoin(MergeJoin *from)
/* /*
* We must add subplans in mergeclauses to the new plan's subPlan list * We must add subplans in mergeclauses to the new plan's subPlan list
*/ */
if (from->join.subPlan != NIL) if (from->join.plan.subPlan != NIL)
newnode->join.subPlan = nconc(newnode->join.subPlan, newnode->join.plan.subPlan = nconc(newnode->join.plan.subPlan,
pull_subplans((Node *) newnode->mergeclauses)); pull_subplans((Node *) newnode->mergeclauses));
return newnode; return newnode;
...@@ -414,8 +418,8 @@ _copyHashJoin(HashJoin *from) ...@@ -414,8 +418,8 @@ _copyHashJoin(HashJoin *from)
/* /*
* We must add subplans in hashclauses to the new plan's subPlan list * We must add subplans in hashclauses to the new plan's subPlan list
*/ */
if (from->join.subPlan != NIL) if (from->join.plan.subPlan != NIL)
newnode->join.subPlan = nconc(newnode->join.subPlan, newnode->join.plan.subPlan = nconc(newnode->join.plan.subPlan,
pull_subplans((Node *) newnode->hashclauses)); pull_subplans((Node *) newnode->hashclauses));
return newnode; return newnode;
...@@ -510,21 +514,6 @@ _copyGroupClause(GroupClause *from) ...@@ -510,21 +514,6 @@ _copyGroupClause(GroupClause *from)
return newnode; return newnode;
} }
static JoinExpr *
_copyJoinExpr(JoinExpr *from)
{
JoinExpr *newnode = makeNode(JoinExpr);
newnode->jointype = from->jointype;
newnode->isNatural = from->isNatural;
Node_Copy(from, newnode, larg);
Node_Copy(from, newnode, rarg);
Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, quals);
return newnode;
}
/* ---------------- /* ----------------
* _copyUnique * _copyUnique
* ---------------- * ----------------
...@@ -914,6 +903,34 @@ _copyRelabelType(RelabelType *from) ...@@ -914,6 +903,34 @@ _copyRelabelType(RelabelType *from)
return newnode; return newnode;
} }
static RangeTblRef *
_copyRangeTblRef(RangeTblRef *from)
{
RangeTblRef *newnode = makeNode(RangeTblRef);
newnode->rtindex = from->rtindex;
return newnode;
}
static JoinExpr *
_copyJoinExpr(JoinExpr *from)
{
JoinExpr *newnode = makeNode(JoinExpr);
newnode->jointype = from->jointype;
newnode->isNatural = from->isNatural;
Node_Copy(from, newnode, larg);
Node_Copy(from, newnode, rarg);
Node_Copy(from, newnode, using);
Node_Copy(from, newnode, quals);
Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, colnames);
Node_Copy(from, newnode, colvars);
return newnode;
}
/* ---------------- /* ----------------
* _copyCaseExpr * _copyCaseExpr
* ---------------- * ----------------
...@@ -1014,6 +1031,7 @@ _copyRelOptInfo(RelOptInfo *from) ...@@ -1014,6 +1031,7 @@ _copyRelOptInfo(RelOptInfo *from)
Node_Copy(from, newnode, baserestrictinfo); Node_Copy(from, newnode, baserestrictinfo);
newnode->baserestrictcost = from->baserestrictcost; newnode->baserestrictcost = from->baserestrictcost;
newnode->outerjoinset = listCopy(from->outerjoinset);
Node_Copy(from, newnode, joininfo); Node_Copy(from, newnode, joininfo);
Node_Copy(from, newnode, innerjoin); Node_Copy(from, newnode, innerjoin);
...@@ -1137,6 +1155,7 @@ _copyIndexPath(IndexPath *from) ...@@ -1137,6 +1155,7 @@ _copyIndexPath(IndexPath *from)
Node_Copy(from, newnode, indexqual); Node_Copy(from, newnode, indexqual);
newnode->indexscandir = from->indexscandir; newnode->indexscandir = from->indexscandir;
newnode->joinrelids = listCopy(from->joinrelids); newnode->joinrelids = listCopy(from->joinrelids);
newnode->alljoinquals = from->alljoinquals;
newnode->rows = from->rows; newnode->rows = from->rows;
return newnode; return newnode;
...@@ -1177,6 +1196,7 @@ _copyTidPath(TidPath *from) ...@@ -1177,6 +1196,7 @@ _copyTidPath(TidPath *from)
static void static void
CopyJoinPathFields(JoinPath *from, JoinPath *newnode) CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
{ {
newnode->jointype = from->jointype;
Node_Copy(from, newnode, outerjoinpath); Node_Copy(from, newnode, outerjoinpath);
Node_Copy(from, newnode, innerjoinpath); Node_Copy(from, newnode, innerjoinpath);
Node_Copy(from, newnode, joinrestrictinfo); Node_Copy(from, newnode, joinrestrictinfo);
...@@ -1286,6 +1306,7 @@ _copyRestrictInfo(RestrictInfo *from) ...@@ -1286,6 +1306,7 @@ _copyRestrictInfo(RestrictInfo *from)
* ---------------- * ----------------
*/ */
Node_Copy(from, newnode, clause); Node_Copy(from, newnode, clause);
newnode->isjoinqual = from->isjoinqual;
Node_Copy(from, newnode, subclauseindices); Node_Copy(from, newnode, subclauseindices);
newnode->mergejoinoperator = from->mergejoinoperator; newnode->mergejoinoperator = from->mergejoinoperator;
newnode->left_sortop = from->left_sortop; newnode->left_sortop = from->left_sortop;
...@@ -1370,12 +1391,11 @@ _copyRangeTblEntry(RangeTblEntry *from) ...@@ -1370,12 +1391,11 @@ _copyRangeTblEntry(RangeTblEntry *from)
if (from->relname) if (from->relname)
newnode->relname = pstrdup(from->relname); newnode->relname = pstrdup(from->relname);
Node_Copy(from, newnode, ref);
Node_Copy(from, newnode, eref);
newnode->relid = from->relid; newnode->relid = from->relid;
Node_Copy(from, newnode, alias);
Node_Copy(from, newnode, eref);
newnode->inh = from->inh; newnode->inh = from->inh;
newnode->inFromCl = from->inFromCl; newnode->inFromCl = from->inFromCl;
newnode->inJoinSet = from->inJoinSet;
newnode->skipAcl = from->skipAcl; newnode->skipAcl = from->skipAcl;
return newnode; return newnode;
...@@ -1526,18 +1546,6 @@ _copyTypeName(TypeName *from) ...@@ -1526,18 +1546,6 @@ _copyTypeName(TypeName *from)
return newnode; return newnode;
} }
static RelExpr *
_copyRelExpr(RelExpr *from)
{
RelExpr *newnode = makeNode(RelExpr);
if (from->relname)
newnode->relname = pstrdup(from->relname);
newnode->inh = from->inh;
return newnode;
}
static SortGroupBy * static SortGroupBy *
_copySortGroupBy(SortGroupBy *from) _copySortGroupBy(SortGroupBy *from)
{ {
...@@ -1555,7 +1563,20 @@ _copyRangeVar(RangeVar *from) ...@@ -1555,7 +1563,20 @@ _copyRangeVar(RangeVar *from)
{ {
RangeVar *newnode = makeNode(RangeVar); RangeVar *newnode = makeNode(RangeVar);
Node_Copy(from, newnode, relExpr); if (from->relname)
newnode->relname = pstrdup(from->relname);
newnode->inh = from->inh;
Node_Copy(from, newnode, name);
return newnode;
}
static RangeSubselect *
_copyRangeSubselect(RangeSubselect *from)
{
RangeSubselect *newnode = makeNode(RangeSubselect);
Node_Copy(from, newnode, subquery);
Node_Copy(from, newnode, name); Node_Copy(from, newnode, name);
return newnode; return newnode;
...@@ -1650,6 +1671,8 @@ _copyQuery(Query *from) ...@@ -1650,6 +1671,8 @@ _copyQuery(Query *from)
newnode->hasSubLinks = from->hasSubLinks; newnode->hasSubLinks = from->hasSubLinks;
Node_Copy(from, newnode, rtable); Node_Copy(from, newnode, rtable);
Node_Copy(from, newnode, jointree);
Node_Copy(from, newnode, targetList); Node_Copy(from, newnode, targetList);
Node_Copy(from, newnode, qual); Node_Copy(from, newnode, qual);
Node_Copy(from, newnode, rowMark); Node_Copy(from, newnode, rowMark);
...@@ -2548,6 +2571,12 @@ copyObject(void *from) ...@@ -2548,6 +2571,12 @@ copyObject(void *from)
case T_RelabelType: case T_RelabelType:
retval = _copyRelabelType(from); retval = _copyRelabelType(from);
break; break;
case T_RangeTblRef:
retval = _copyRangeTblRef(from);
break;
case T_JoinExpr:
retval = _copyJoinExpr(from);
break;
/* /*
* RELATION NODES * RELATION NODES
...@@ -2809,15 +2838,15 @@ copyObject(void *from) ...@@ -2809,15 +2838,15 @@ copyObject(void *from)
case T_TypeCast: case T_TypeCast:
retval = _copyTypeCast(from); retval = _copyTypeCast(from);
break; break;
case T_RelExpr:
retval = _copyRelExpr(from);
break;
case T_SortGroupBy: case T_SortGroupBy:
retval = _copySortGroupBy(from); retval = _copySortGroupBy(from);
break; break;
case T_RangeVar: case T_RangeVar:
retval = _copyRangeVar(from); retval = _copyRangeVar(from);
break; break;
case T_RangeSubselect:
retval = _copyRangeSubselect(from);
break;
case T_TypeName: case T_TypeName:
retval = _copyTypeName(from); retval = _copyTypeName(from);
break; break;
...@@ -2845,9 +2874,6 @@ copyObject(void *from) ...@@ -2845,9 +2874,6 @@ copyObject(void *from)
case T_GroupClause: case T_GroupClause:
retval = _copyGroupClause(from); retval = _copyGroupClause(from);
break; break;
case T_JoinExpr:
retval = _copyJoinExpr(from);
break;
case T_CaseExpr: case T_CaseExpr:
retval = _copyCaseExpr(from); retval = _copyCaseExpr(from);
break; break;
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.72 2000/08/11 23:45:31 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.73 2000/09/12 21:06:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -256,6 +256,26 @@ _equalSubLink(SubLink *a, SubLink *b) ...@@ -256,6 +256,26 @@ _equalSubLink(SubLink *a, SubLink *b)
return true; return true;
} }
static bool
_equalArrayRef(ArrayRef *a, ArrayRef *b)
{
if (a->refelemtype != b->refelemtype)
return false;
if (a->refattrlength != b->refattrlength)
return false;
if (a->refelemlength != b->refelemlength)
return false;
if (a->refelembyval != b->refelembyval)
return false;
if (!equal(a->refupperindexpr, b->refupperindexpr))
return false;
if (!equal(a->reflowerindexpr, b->reflowerindexpr))
return false;
if (!equal(a->refexpr, b->refexpr))
return false;
return equal(a->refassgnexpr, b->refassgnexpr);
}
static bool static bool
_equalFieldSelect(FieldSelect *a, FieldSelect *b) _equalFieldSelect(FieldSelect *a, FieldSelect *b)
{ {
...@@ -283,23 +303,37 @@ _equalRelabelType(RelabelType *a, RelabelType *b) ...@@ -283,23 +303,37 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
} }
static bool static bool
_equalArrayRef(ArrayRef *a, ArrayRef *b) _equalRangeTblRef(RangeTblRef *a, RangeTblRef *b)
{ {
if (a->refelemtype != b->refelemtype) if (a->rtindex != b->rtindex)
return false; return false;
if (a->refattrlength != b->refattrlength)
return true;
}
static bool
_equalJoinExpr(JoinExpr *a, JoinExpr *b)
{
if (a->jointype != b->jointype)
return false; return false;
if (a->refelemlength != b->refelemlength) if (a->isNatural != b->isNatural)
return false; return false;
if (a->refelembyval != b->refelembyval) if (!equal(a->larg, b->larg))
return false; return false;
if (!equal(a->refupperindexpr, b->refupperindexpr)) if (!equal(a->rarg, b->rarg))
return false; return false;
if (!equal(a->reflowerindexpr, b->reflowerindexpr)) if (!equal(a->using, b->using))
return false; return false;
if (!equal(a->refexpr, b->refexpr)) if (!equal(a->quals, b->quals))
return false; return false;
return equal(a->refassgnexpr, b->refassgnexpr); if (!equal(a->alias, b->alias))
return false;
if (!equal(a->colnames, b->colnames))
return false;
if (!equal(a->colvars, b->colvars))
return false;
return true;
} }
/* /*
...@@ -370,6 +404,8 @@ _equalIndexPath(IndexPath *a, IndexPath *b) ...@@ -370,6 +404,8 @@ _equalIndexPath(IndexPath *a, IndexPath *b)
return false; return false;
if (!equali(a->joinrelids, b->joinrelids)) if (!equali(a->joinrelids, b->joinrelids))
return false; return false;
if (a->alljoinquals != b->alljoinquals)
return false;
/* /*
* Skip 'rows' because of possibility of floating-point roundoff * Skip 'rows' because of possibility of floating-point roundoff
...@@ -395,6 +431,8 @@ _equalJoinPath(JoinPath *a, JoinPath *b) ...@@ -395,6 +431,8 @@ _equalJoinPath(JoinPath *a, JoinPath *b)
{ {
if (!_equalPath((Path *) a, (Path *) b)) if (!_equalPath((Path *) a, (Path *) b))
return false; return false;
if (a->jointype != b->jointype)
return false;
if (!equal(a->outerjoinpath, b->outerjoinpath)) if (!equal(a->outerjoinpath, b->outerjoinpath))
return false; return false;
if (!equal(a->innerjoinpath, b->innerjoinpath)) if (!equal(a->innerjoinpath, b->innerjoinpath))
...@@ -457,6 +495,8 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b) ...@@ -457,6 +495,8 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
{ {
if (!equal(a->clause, b->clause)) if (!equal(a->clause, b->clause))
return false; return false;
if (a->isjoinqual != b->isjoinqual)
return false;
if (!equal(a->subclauseindices, b->subclauseindices)) if (!equal(a->subclauseindices, b->subclauseindices))
return false; return false;
if (a->mergejoinoperator != b->mergejoinoperator) if (a->mergejoinoperator != b->mergejoinoperator)
...@@ -557,6 +597,8 @@ _equalQuery(Query *a, Query *b) ...@@ -557,6 +597,8 @@ _equalQuery(Query *a, Query *b)
return false; return false;
if (!equal(a->rtable, b->rtable)) if (!equal(a->rtable, b->rtable))
return false; return false;
if (!equal(a->jointree, b->jointree))
return false;
if (!equal(a->targetList, b->targetList)) if (!equal(a->targetList, b->targetList))
return false; return false;
if (!equal(a->qual, b->qual)) if (!equal(a->qual, b->qual))
...@@ -1476,31 +1518,33 @@ _equalTypeCast(TypeCast *a, TypeCast *b) ...@@ -1476,31 +1518,33 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
} }
static bool static bool
_equalRelExpr(RelExpr *a, RelExpr *b) _equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
{ {
if (!equalstr(a->relname, b->relname)) if (!equalstr(a->useOp, b->useOp))
return false; return false;
if (a->inh != b->inh) if (!equal(a->node, b->node))
return false; return false;
return true; return true;
} }
static bool static bool
_equalSortGroupBy(SortGroupBy *a, SortGroupBy *b) _equalRangeVar(RangeVar *a, RangeVar *b)
{ {
if (!equalstr(a->useOp, b->useOp)) if (!equalstr(a->relname, b->relname))
return false; return false;
if (!equal(a->node, b->node)) if (a->inh != b->inh)
return false;
if (!equal(a->name, b->name))
return false; return false;
return true; return true;
} }
static bool static bool
_equalRangeVar(RangeVar *a, RangeVar *b) _equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
{ {
if (!equal(a->relExpr, b->relExpr)) if (!equal(a->subquery, b->subquery))
return false; return false;
if (!equal(a->name, b->name)) if (!equal(a->name, b->name))
return false; return false;
...@@ -1605,17 +1649,16 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) ...@@ -1605,17 +1649,16 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
{ {
if (!equalstr(a->relname, b->relname)) if (!equalstr(a->relname, b->relname))
return false; return false;
if (!equal(a->ref, b->ref))
return false;
/* XXX what about eref? */
if (a->relid != b->relid) if (a->relid != b->relid)
return false; return false;
if (!equal(a->alias, b->alias))
return false;
if (!equal(a->eref, b->eref))
return false;
if (a->inh != b->inh) if (a->inh != b->inh)
return false; return false;
if (a->inFromCl != b->inFromCl) if (a->inFromCl != b->inFromCl)
return false; return false;
if (a->inJoinSet != b->inJoinSet)
return false;
if (a->skipAcl != b->skipAcl) if (a->skipAcl != b->skipAcl)
return false; return false;
...@@ -1644,25 +1687,6 @@ _equalRowMark(RowMark *a, RowMark *b) ...@@ -1644,25 +1687,6 @@ _equalRowMark(RowMark *a, RowMark *b)
return true; return true;
} }
static bool
_equalJoinExpr(JoinExpr *a, JoinExpr *b)
{
if (a->jointype != b->jointype)
return false;
if (a->isNatural != b->isNatural)
return false;
if (!equal(a->larg, b->larg))
return false;
if (!equal(a->rarg, b->rarg))
return false;
if (!equal(a->alias, b->alias))
return false;
if (!equal(a->quals, b->quals))
return false;
return true;
}
static bool static bool
_equalFkConstraint(FkConstraint *a, FkConstraint *b) _equalFkConstraint(FkConstraint *a, FkConstraint *b)
{ {
...@@ -1808,6 +1832,12 @@ equal(void *a, void *b) ...@@ -1808,6 +1832,12 @@ equal(void *a, void *b)
case T_RelabelType: case T_RelabelType:
retval = _equalRelabelType(a, b); retval = _equalRelabelType(a, b);
break; break;
case T_RangeTblRef:
retval = _equalRangeTblRef(a, b);
break;
case T_JoinExpr:
retval = _equalJoinExpr(a, b);
break;
case T_RelOptInfo: case T_RelOptInfo:
retval = _equalRelOptInfo(a, b); retval = _equalRelOptInfo(a, b);
...@@ -2067,15 +2097,15 @@ equal(void *a, void *b) ...@@ -2067,15 +2097,15 @@ equal(void *a, void *b)
case T_TypeCast: case T_TypeCast:
retval = _equalTypeCast(a, b); retval = _equalTypeCast(a, b);
break; break;
case T_RelExpr:
retval = _equalRelExpr(a, b);
break;
case T_SortGroupBy: case T_SortGroupBy:
retval = _equalSortGroupBy(a, b); retval = _equalSortGroupBy(a, b);
break; break;
case T_RangeVar: case T_RangeVar:
retval = _equalRangeVar(a, b); retval = _equalRangeVar(a, b);
break; break;
case T_RangeSubselect:
retval = _equalRangeSubselect(a, b);
break;
case T_TypeName: case T_TypeName:
retval = _equalTypeName(a, b); retval = _equalTypeName(a, b);
break; break;
...@@ -2104,9 +2134,6 @@ equal(void *a, void *b) ...@@ -2104,9 +2134,6 @@ equal(void *a, void *b)
/* GroupClause is equivalent to SortClause */ /* GroupClause is equivalent to SortClause */
retval = _equalSortClause(a, b); retval = _equalSortClause(a, b);
break; break;
case T_JoinExpr:
retval = _equalJoinExpr(a, b);
break;
case T_CaseExpr: case T_CaseExpr:
retval = _equalCaseExpr(a, b); retval = _equalCaseExpr(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.32 2000/06/09 01:44:12 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.33 2000/09/12 21:06:49 tgl Exp $
* *
* NOTES * NOTES
* XXX a few of the following functions are duplicated to handle * XXX a few of the following functions are duplicated to handle
...@@ -351,6 +351,25 @@ member(void *l1, List *l2) ...@@ -351,6 +351,25 @@ member(void *l1, List *l2)
return false; return false;
} }
/*
* like member(), but use when pointer-equality comparison is sufficient
*/
bool
ptrMember(void *l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == ((void *) lfirst(i)))
return true;
}
return false;
}
/*
* membership test for integer lists
*/
bool bool
intMember(int l1, List *l2) intMember(int l1, List *l2)
{ {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.125 2000/08/08 15:41:26 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.126 2000/09/12 21:06:49 tgl Exp $
* *
* NOTES * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -268,6 +268,9 @@ _outQuery(StringInfo str, Query *node) ...@@ -268,6 +268,9 @@ _outQuery(StringInfo str, Query *node)
appendStringInfo(str, " :rtable "); appendStringInfo(str, " :rtable ");
_outNode(str, node->rtable); _outNode(str, node->rtable);
appendStringInfo(str, " :jointree ");
_outNode(str, node->jointree);
appendStringInfo(str, " :targetlist "); appendStringInfo(str, " :targetlist ");
_outNode(str, node->targetList); _outNode(str, node->targetList);
...@@ -389,7 +392,6 @@ _outAppend(StringInfo str, Append *node) ...@@ -389,7 +392,6 @@ _outAppend(StringInfo str, Append *node)
" :inheritrelid %u :inheritrtable ", " :inheritrelid %u :inheritrtable ",
node->inheritrelid); node->inheritrelid);
_outNode(str, node->inheritrtable); _outNode(str, node->inheritrtable);
} }
/* /*
...@@ -400,7 +402,9 @@ _outJoin(StringInfo str, Join *node) ...@@ -400,7 +402,9 @@ _outJoin(StringInfo str, Join *node)
{ {
appendStringInfo(str, " JOIN "); appendStringInfo(str, " JOIN ");
_outPlanInfo(str, (Plan *) node); _outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->jointype);
_outNode(str, node->joinqual);
} }
/* /*
...@@ -411,6 +415,9 @@ _outNestLoop(StringInfo str, NestLoop *node) ...@@ -411,6 +415,9 @@ _outNestLoop(StringInfo str, NestLoop *node)
{ {
appendStringInfo(str, " NESTLOOP "); appendStringInfo(str, " NESTLOOP ");
_outPlanInfo(str, (Plan *) node); _outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
} }
/* /*
...@@ -421,6 +428,9 @@ _outMergeJoin(StringInfo str, MergeJoin *node) ...@@ -421,6 +428,9 @@ _outMergeJoin(StringInfo str, MergeJoin *node)
{ {
appendStringInfo(str, " MERGEJOIN "); appendStringInfo(str, " MERGEJOIN ");
_outPlanInfo(str, (Plan *) node); _outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
appendStringInfo(str, " :mergeclauses "); appendStringInfo(str, " :mergeclauses ");
_outNode(str, node->mergeclauses); _outNode(str, node->mergeclauses);
...@@ -434,17 +444,14 @@ _outHashJoin(StringInfo str, HashJoin *node) ...@@ -434,17 +444,14 @@ _outHashJoin(StringInfo str, HashJoin *node)
{ {
appendStringInfo(str, " HASHJOIN "); appendStringInfo(str, " HASHJOIN ");
_outPlanInfo(str, (Plan *) node); _outPlanInfo(str, (Plan *) node);
appendStringInfo(str, " :jointype %d :joinqual ",
(int) node->join.jointype);
_outNode(str, node->join.joinqual);
appendStringInfo(str, " :hashclauses "); appendStringInfo(str, " :hashclauses ");
_outNode(str, node->hashclauses); _outNode(str, node->hashclauses);
appendStringInfo(str, " :hashjoinop %u ",
appendStringInfo(str,
" :hashjoinop %u ",
node->hashjoinop); node->hashjoinop);
appendStringInfo(str,
" :hashdone %d ",
node->hashdone);
} }
static void static void
...@@ -757,32 +764,6 @@ _outSubLink(StringInfo str, SubLink *node) ...@@ -757,32 +764,6 @@ _outSubLink(StringInfo str, SubLink *node)
_outNode(str, node->subselect); _outNode(str, node->subselect);
} }
/*
* FieldSelect
*/
static void
_outFieldSelect(StringInfo str, FieldSelect *node)
{
appendStringInfo(str, " FIELDSELECT :arg ");
_outNode(str, node->arg);
appendStringInfo(str, " :fieldnum %d :resulttype %u :resulttypmod %d ",
node->fieldnum, node->resulttype, node->resulttypmod);
}
/*
* RelabelType
*/
static void
_outRelabelType(StringInfo str, RelabelType *node)
{
appendStringInfo(str, " RELABELTYPE :arg ");
_outNode(str, node->arg);
appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
node->resulttype, node->resulttypmod);
}
/* /*
* ArrayRef is a subclass of Expr * ArrayRef is a subclass of Expr
*/ */
...@@ -846,6 +827,66 @@ _outParam(StringInfo str, Param *node) ...@@ -846,6 +827,66 @@ _outParam(StringInfo str, Param *node)
appendStringInfo(str, " :paramtype %u ", node->paramtype); appendStringInfo(str, " :paramtype %u ", node->paramtype);
} }
/*
* FieldSelect
*/
static void
_outFieldSelect(StringInfo str, FieldSelect *node)
{
appendStringInfo(str, " FIELDSELECT :arg ");
_outNode(str, node->arg);
appendStringInfo(str, " :fieldnum %d :resulttype %u :resulttypmod %d ",
node->fieldnum, node->resulttype, node->resulttypmod);
}
/*
* RelabelType
*/
static void
_outRelabelType(StringInfo str, RelabelType *node)
{
appendStringInfo(str, " RELABELTYPE :arg ");
_outNode(str, node->arg);
appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
node->resulttype, node->resulttypmod);
}
/*
* RangeTblRef
*/
static void
_outRangeTblRef(StringInfo str, RangeTblRef *node)
{
appendStringInfo(str, " RANGETBLREF %d ",
node->rtindex);
}
/*
* JoinExpr
*/
static void
_outJoinExpr(StringInfo str, JoinExpr *node)
{
appendStringInfo(str, " JOINEXPR :jointype %d :isNatural %s :larg ",
(int) node->jointype,
node->isNatural ? "true" : "false");
_outNode(str, node->larg);
appendStringInfo(str, " :rarg ");
_outNode(str, node->rarg);
appendStringInfo(str, " :using ");
_outNode(str, node->using);
appendStringInfo(str, " :quals ");
_outNode(str, node->quals);
appendStringInfo(str, " :alias ");
_outNode(str, node->alias);
appendStringInfo(str, " :colnames ");
_outNode(str, node->colnames);
appendStringInfo(str, " :colvars ");
_outNode(str, node->colvars);
}
/* /*
* Stuff from execnodes.h * Stuff from execnodes.h
*/ */
...@@ -897,6 +938,11 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) ...@@ -897,6 +938,11 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
node->pruneable ? "true" : "false"); node->pruneable ? "true" : "false");
_outNode(str, node->baserestrictinfo); _outNode(str, node->baserestrictinfo);
appendStringInfo(str,
" :baserestrictcost %.2f :outerjoinset ",
node->baserestrictcost);
_outIntList(str, node->outerjoinset);
appendStringInfo(str, " :joininfo "); appendStringInfo(str, " :joininfo ");
_outNode(str, node->joininfo); _outNode(str, node->joininfo);
...@@ -931,14 +977,14 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) ...@@ -931,14 +977,14 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
{ {
appendStringInfo(str, " RTE :relname "); appendStringInfo(str, " RTE :relname ");
_outToken(str, node->relname); _outToken(str, node->relname);
appendStringInfo(str, " :ref "); appendStringInfo(str, " :relid %u :alias ",
_outNode(str, node->ref); node->relid);
appendStringInfo(str, _outNode(str, node->alias);
" :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s", appendStringInfo(str, " :eref ");
node->relid, _outNode(str, node->eref);
appendStringInfo(str, " :inh %s :inFromCl %s :skipAcl %s",
node->inh ? "true" : "false", node->inh ? "true" : "false",
node->inFromCl ? "true" : "false", node->inFromCl ? "true" : "false",
node->inJoinSet ? "true" : "false",
node->skipAcl ? "true" : "false"); node->skipAcl ? "true" : "false");
} }
...@@ -985,7 +1031,8 @@ _outIndexPath(StringInfo str, IndexPath *node) ...@@ -985,7 +1031,8 @@ _outIndexPath(StringInfo str, IndexPath *node)
(int) node->indexscandir); (int) node->indexscandir);
_outIntList(str, node->joinrelids); _outIntList(str, node->joinrelids);
appendStringInfo(str, " :rows %.2f ", appendStringInfo(str, " :alljoinquals %s :rows %.2f ",
node->alljoinquals ? "true" : "false",
node->rows); node->rows);
} }
...@@ -1021,7 +1068,8 @@ _outNestPath(StringInfo str, NestPath *node) ...@@ -1021,7 +1068,8 @@ _outNestPath(StringInfo str, NestPath *node)
node->path.startup_cost, node->path.startup_cost,
node->path.total_cost); node->path.total_cost);
_outNode(str, node->path.pathkeys); _outNode(str, node->path.pathkeys);
appendStringInfo(str, " :outerjoinpath "); appendStringInfo(str, " :jointype %d :outerjoinpath ",
(int) node->jointype);
_outNode(str, node->outerjoinpath); _outNode(str, node->outerjoinpath);
appendStringInfo(str, " :innerjoinpath "); appendStringInfo(str, " :innerjoinpath ");
_outNode(str, node->innerjoinpath); _outNode(str, node->innerjoinpath);
...@@ -1041,7 +1089,8 @@ _outMergePath(StringInfo str, MergePath *node) ...@@ -1041,7 +1089,8 @@ _outMergePath(StringInfo str, MergePath *node)
node->jpath.path.startup_cost, node->jpath.path.startup_cost,
node->jpath.path.total_cost); node->jpath.path.total_cost);
_outNode(str, node->jpath.path.pathkeys); _outNode(str, node->jpath.path.pathkeys);
appendStringInfo(str, " :outerjoinpath "); appendStringInfo(str, " :jointype %d :outerjoinpath ",
(int) node->jpath.jointype);
_outNode(str, node->jpath.outerjoinpath); _outNode(str, node->jpath.outerjoinpath);
appendStringInfo(str, " :innerjoinpath "); appendStringInfo(str, " :innerjoinpath ");
_outNode(str, node->jpath.innerjoinpath); _outNode(str, node->jpath.innerjoinpath);
...@@ -1070,7 +1119,8 @@ _outHashPath(StringInfo str, HashPath *node) ...@@ -1070,7 +1119,8 @@ _outHashPath(StringInfo str, HashPath *node)
node->jpath.path.startup_cost, node->jpath.path.startup_cost,
node->jpath.path.total_cost); node->jpath.path.total_cost);
_outNode(str, node->jpath.path.pathkeys); _outNode(str, node->jpath.path.pathkeys);
appendStringInfo(str, " :outerjoinpath "); appendStringInfo(str, " :jointype %d :outerjoinpath ",
(int) node->jpath.jointype);
_outNode(str, node->jpath.outerjoinpath); _outNode(str, node->jpath.outerjoinpath);
appendStringInfo(str, " :innerjoinpath "); appendStringInfo(str, " :innerjoinpath ");
_outNode(str, node->jpath.innerjoinpath); _outNode(str, node->jpath.innerjoinpath);
...@@ -1101,7 +1151,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node) ...@@ -1101,7 +1151,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
appendStringInfo(str, " RESTRICTINFO :clause "); appendStringInfo(str, " RESTRICTINFO :clause ");
_outNode(str, node->clause); _outNode(str, node->clause);
appendStringInfo(str, " :subclauseindices "); appendStringInfo(str, " :isjoinqual %s :subclauseindices ",
node->isjoinqual ? "true" : "false");
_outNode(str, node->subclauseindices); _outNode(str, node->subclauseindices);
appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator); appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator);
...@@ -1483,12 +1534,6 @@ _outNode(StringInfo str, void *obj) ...@@ -1483,12 +1534,6 @@ _outNode(StringInfo str, void *obj)
case T_SubLink: case T_SubLink:
_outSubLink(str, obj); _outSubLink(str, obj);
break; break;
case T_FieldSelect:
_outFieldSelect(str, obj);
break;
case T_RelabelType:
_outRelabelType(str, obj);
break;
case T_ArrayRef: case T_ArrayRef:
_outArrayRef(str, obj); _outArrayRef(str, obj);
break; break;
...@@ -1501,6 +1546,18 @@ _outNode(StringInfo str, void *obj) ...@@ -1501,6 +1546,18 @@ _outNode(StringInfo str, void *obj)
case T_Param: case T_Param:
_outParam(str, obj); _outParam(str, obj);
break; break;
case T_FieldSelect:
_outFieldSelect(str, obj);
break;
case T_RelabelType:
_outRelabelType(str, obj);
break;
case T_RangeTblRef:
_outRangeTblRef(str, obj);
break;
case T_JoinExpr:
_outJoinExpr(str, obj);
break;
case T_EState: case T_EState:
_outEState(str, obj); _outEState(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.39 2000/06/18 22:44:05 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.40 2000/09/12 21:06:49 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -133,7 +133,7 @@ print_rt(List *rtable) ...@@ -133,7 +133,7 @@ print_rt(List *rtable)
RangeTblEntry *rte = lfirst(l); RangeTblEntry *rte = lfirst(l);
printf("%d\t%s(%s)\t%u\t%d\t%s\n", printf("%d\t%s(%s)\t%u\t%d\t%s\n",
i, rte->relname, rte->ref->relname, rte->relid, i, rte->relname, rte->eref->relname, rte->relid,
rte->inFromCl, rte->inFromCl,
(rte->inh ? "inh" : "")); (rte->inh ? "inh" : ""));
i++; i++;
...@@ -157,7 +157,6 @@ print_expr(Node *expr, List *rtable) ...@@ -157,7 +157,6 @@ print_expr(Node *expr, List *rtable)
if (IsA(expr, Var)) if (IsA(expr, Var))
{ {
Var *var = (Var *) expr; Var *var = (Var *) expr;
RangeTblEntry *rt;
char *relname, char *relname,
*attname; *attname;
...@@ -173,10 +172,10 @@ print_expr(Node *expr, List *rtable) ...@@ -173,10 +172,10 @@ print_expr(Node *expr, List *rtable)
break; break;
default: default:
{ {
RangeTblEntry *rt;
rt = rt_fetch(var->varno, rtable); rt = rt_fetch(var->varno, rtable);
relname = rt->relname; relname = rt->eref->relname;
if (rt->ref && rt->ref->relname)
relname = rt->ref->relname; /* table renamed */
attname = get_attname(rt->relid, var->varattno); attname = get_attname(rt->relid, var->varattno);
} }
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.95 2000/08/08 15:41:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.96 2000/09/12 21:06:49 tgl Exp $
* *
* NOTES * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -119,6 +119,9 @@ _readQuery() ...@@ -119,6 +119,9 @@ _readQuery()
token = lsptok(NULL, &length); /* skip :rtable */ token = lsptok(NULL, &length); /* skip :rtable */
local_node->rtable = nodeRead(true); local_node->rtable = nodeRead(true);
token = lsptok(NULL, &length); /* skip :jointree */
local_node->jointree = nodeRead(true);
token = lsptok(NULL, &length); /* skip :targetlist */ token = lsptok(NULL, &length); /* skip :targetlist */
local_node->targetList = nodeRead(true); local_node->targetList = nodeRead(true);
...@@ -335,14 +338,22 @@ _readAppend() ...@@ -335,14 +338,22 @@ _readAppend()
/* ---------------- /* ----------------
* _getJoin * _getJoin
*
* In case Join is not the same structure as Plan someday.
* ---------------- * ----------------
*/ */
static void static void
_getJoin(Join *node) _getJoin(Join *node)
{ {
char *token;
int length;
_getPlan((Plan *) node); _getPlan((Plan *) node);
token = lsptok(NULL, &length); /* skip the :jointype */
token = lsptok(NULL, &length); /* get the jointype */
node->jointype = (JoinType) atoi(token);
token = lsptok(NULL, &length); /* skip the :joinqual */
node->joinqual = nodeRead(true); /* get the joinqual */
} }
...@@ -399,6 +410,7 @@ _readMergeJoin() ...@@ -399,6 +410,7 @@ _readMergeJoin()
local_node = makeNode(MergeJoin); local_node = makeNode(MergeJoin);
_getJoin((Join *) local_node); _getJoin((Join *) local_node);
token = lsptok(NULL, &length); /* eat :mergeclauses */ token = lsptok(NULL, &length); /* eat :mergeclauses */
local_node->mergeclauses = nodeRead(true); /* now read it */ local_node->mergeclauses = nodeRead(true); /* now read it */
...@@ -429,19 +441,13 @@ _readHashJoin() ...@@ -429,19 +441,13 @@ _readHashJoin()
token = lsptok(NULL, &length); /* get hashjoinop */ token = lsptok(NULL, &length); /* get hashjoinop */
local_node->hashjoinop = strtoul(token, NULL, 10); local_node->hashjoinop = strtoul(token, NULL, 10);
token = lsptok(NULL, &length); /* eat :hashdone */
token = lsptok(NULL, &length); /* eat hashdone */
local_node->hashdone = false;
return local_node; return local_node;
} }
/* ---------------- /* ----------------
* _getScan * _getScan
* *
* Scan is a subclass of Node * Scan is a subclass of Plan.
* (Actually, according to the plannodes.h include file, it is a
* subclass of Plan. This is why _getPlan is used here.)
* *
* Scan gets its own get function since stuff inherits it. * Scan gets its own get function since stuff inherits it.
* ---------------- * ----------------
...@@ -462,7 +468,7 @@ _getScan(Scan *node) ...@@ -462,7 +468,7 @@ _getScan(Scan *node)
/* ---------------- /* ----------------
* _readScan * _readScan
* *
* Scan is a subclass of Plan (Not Node, see above). * Scan is a subclass of Plan.
* ---------------- * ----------------
*/ */
static Scan * static Scan *
...@@ -1154,6 +1160,74 @@ _readRelabelType() ...@@ -1154,6 +1160,74 @@ _readRelabelType()
return local_node; return local_node;
} }
/* ----------------
* _readRangeTblRef
*
* RangeTblRef is a subclass of Node
* ----------------
*/
static RangeTblRef *
_readRangeTblRef()
{
RangeTblRef *local_node;
char *token;
int length;
local_node = makeNode(RangeTblRef);
token = lsptok(NULL, &length); /* get rtindex */
local_node->rtindex = atoi(token);
return local_node;
}
/* ----------------
* _readJoinExpr
*
* JoinExpr is a subclass of Node
* ----------------
*/
static JoinExpr *
_readJoinExpr()
{
JoinExpr *local_node;
char *token;
int length;
local_node = makeNode(JoinExpr);
token = lsptok(NULL, &length); /* eat :jointype */
token = lsptok(NULL, &length); /* get jointype */
local_node->jointype = (JoinType) atoi(token);
token = lsptok(NULL, &length); /* eat :isNatural */
token = lsptok(NULL, &length); /* get :isNatural */
local_node->isNatural = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* eat :larg */
local_node->larg = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :rarg */
local_node->rarg = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :using */
local_node->using = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :quals */
local_node->quals = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :alias */
local_node->alias = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :colnames */
local_node->colnames = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :colvars */
local_node->colvars = nodeRead(true); /* now read it */
return local_node;
}
/* /*
* Stuff from execnodes.h * Stuff from execnodes.h
*/ */
...@@ -1252,7 +1326,14 @@ _readRelOptInfo() ...@@ -1252,7 +1326,14 @@ _readRelOptInfo()
local_node->pruneable = (token[0] == 't') ? true : false; local_node->pruneable = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* get :baserestrictinfo */ token = lsptok(NULL, &length); /* get :baserestrictinfo */
local_node->baserestrictinfo = nodeRead(true); /* now read it */ local_node->baserestrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :baserestrictcost */
token = lsptok(NULL, &length); /* now read it */
local_node->baserestrictcost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :outerjoinset */
local_node->outerjoinset = toIntList(nodeRead(true)); /* now read it */
token = lsptok(NULL, &length); /* get :joininfo */ token = lsptok(NULL, &length); /* get :joininfo */
local_node->joininfo = nodeRead(true); /* now read it */ local_node->joininfo = nodeRead(true); /* now read it */
...@@ -1324,13 +1405,16 @@ _readRangeTblEntry() ...@@ -1324,13 +1405,16 @@ _readRangeTblEntry()
else else
local_node->relname = debackslash(token, length); local_node->relname = debackslash(token, length);
token = lsptok(NULL, &length); /* eat :ref */
local_node->ref = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :relid */ token = lsptok(NULL, &length); /* eat :relid */
token = lsptok(NULL, &length); /* get :relid */ token = lsptok(NULL, &length); /* get :relid */
local_node->relid = strtoul(token, NULL, 10); local_node->relid = strtoul(token, NULL, 10);
token = lsptok(NULL, &length); /* eat :alias */
local_node->alias = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :eref */
local_node->eref = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :inh */ token = lsptok(NULL, &length); /* eat :inh */
token = lsptok(NULL, &length); /* get :inh */ token = lsptok(NULL, &length); /* get :inh */
local_node->inh = (token[0] == 't') ? true : false; local_node->inh = (token[0] == 't') ? true : false;
...@@ -1339,10 +1423,6 @@ _readRangeTblEntry() ...@@ -1339,10 +1423,6 @@ _readRangeTblEntry()
token = lsptok(NULL, &length); /* get :inFromCl */ token = lsptok(NULL, &length); /* get :inFromCl */
local_node->inFromCl = (token[0] == 't') ? true : false; local_node->inFromCl = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* eat :inJoinSet */
token = lsptok(NULL, &length); /* get :inJoinSet */
local_node->inJoinSet = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* eat :skipAcl */ token = lsptok(NULL, &length); /* eat :skipAcl */
token = lsptok(NULL, &length); /* get :skipAcl */ token = lsptok(NULL, &length); /* get :skipAcl */
local_node->skipAcl = (token[0] == 't') ? true : false; local_node->skipAcl = (token[0] == 't') ? true : false;
...@@ -1444,6 +1524,10 @@ _readIndexPath() ...@@ -1444,6 +1524,10 @@ _readIndexPath()
token = lsptok(NULL, &length); /* get :joinrelids */ token = lsptok(NULL, &length); /* get :joinrelids */
local_node->joinrelids = toIntList(nodeRead(true)); local_node->joinrelids = toIntList(nodeRead(true));
token = lsptok(NULL, &length); /* get :alljoinquals */
token = lsptok(NULL, &length); /* now read it */
local_node->alljoinquals = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* get :rows */ token = lsptok(NULL, &length); /* get :rows */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->rows = atof(token); local_node->rows = atof(token);
...@@ -1520,6 +1604,10 @@ _readNestPath() ...@@ -1520,6 +1604,10 @@ _readNestPath()
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */ local_node->path.pathkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :jointype */
token = lsptok(NULL, &length); /* now read it */
local_node->jointype = (JoinType) atoi(token);
token = lsptok(NULL, &length); /* get :outerjoinpath */ token = lsptok(NULL, &length); /* get :outerjoinpath */
local_node->outerjoinpath = nodeRead(true); /* now read it */ local_node->outerjoinpath = nodeRead(true); /* now read it */
...@@ -1527,7 +1615,7 @@ _readNestPath() ...@@ -1527,7 +1615,7 @@ _readNestPath()
local_node->innerjoinpath = nodeRead(true); /* now read it */ local_node->innerjoinpath = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :joinrestrictinfo */ token = lsptok(NULL, &length); /* get :joinrestrictinfo */
local_node->joinrestrictinfo = nodeRead(true); /* now read it */ local_node->joinrestrictinfo = nodeRead(true); /* now read it */
return local_node; return local_node;
} }
...@@ -1562,6 +1650,10 @@ _readMergePath() ...@@ -1562,6 +1650,10 @@ _readMergePath()
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */ local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :jointype */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.jointype = (JoinType) atoi(token);
token = lsptok(NULL, &length); /* get :outerjoinpath */ token = lsptok(NULL, &length); /* get :outerjoinpath */
local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */ local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */
...@@ -1569,7 +1661,7 @@ _readMergePath() ...@@ -1569,7 +1661,7 @@ _readMergePath()
local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */ local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :joinrestrictinfo */ token = lsptok(NULL, &length); /* get :joinrestrictinfo */
local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */ local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :path_mergeclauses */ token = lsptok(NULL, &length); /* get :path_mergeclauses */
local_node->path_mergeclauses = nodeRead(true); /* now read it */ local_node->path_mergeclauses = nodeRead(true); /* now read it */
...@@ -1613,6 +1705,10 @@ _readHashPath() ...@@ -1613,6 +1705,10 @@ _readHashPath()
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */ local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :jointype */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.jointype = (JoinType) atoi(token);
token = lsptok(NULL, &length); /* get :outerjoinpath */ token = lsptok(NULL, &length); /* get :outerjoinpath */
local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */ local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */
...@@ -1620,7 +1716,7 @@ _readHashPath() ...@@ -1620,7 +1716,7 @@ _readHashPath()
local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */ local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :joinrestrictinfo */ token = lsptok(NULL, &length); /* get :joinrestrictinfo */
local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */ local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :path_hashclauses */ token = lsptok(NULL, &length); /* get :path_hashclauses */
local_node->path_hashclauses = nodeRead(true); /* now read it */ local_node->path_hashclauses = nodeRead(true); /* now read it */
...@@ -1672,6 +1768,10 @@ _readRestrictInfo() ...@@ -1672,6 +1768,10 @@ _readRestrictInfo()
token = lsptok(NULL, &length); /* get :clause */ token = lsptok(NULL, &length); /* get :clause */
local_node->clause = nodeRead(true); /* now read it */ local_node->clause = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :isjoinqual */
token = lsptok(NULL, &length); /* now read it */
local_node->isjoinqual = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* get :subclauseindices */ token = lsptok(NULL, &length); /* get :subclauseindices */
local_node->subclauseindices = nodeRead(true); /* now read it */ local_node->subclauseindices = nodeRead(true); /* now read it */
...@@ -1789,6 +1889,10 @@ parsePlanString(void) ...@@ -1789,6 +1889,10 @@ parsePlanString(void)
return_value = _readFieldSelect(); return_value = _readFieldSelect();
else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0) else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0)
return_value = _readRelabelType(); return_value = _readRelabelType();
else if (length == 11 && strncmp(token, "RANGETBLREF", length) == 0)
return_value = _readRangeTblRef();
else if (length == 8 && strncmp(token, "JOINEXPR", length) == 0)
return_value = _readJoinExpr();
else if (length == 3 && strncmp(token, "AGG", length) == 0) else if (length == 3 && strncmp(token, "AGG", length) == 0)
return_value = _readAgg(); return_value = _readAgg();
else if (length == 4 && strncmp(token, "HASH", length) == 0) else if (length == 4 && strncmp(token, "HASH", length) == 0)
......
...@@ -35,10 +35,10 @@ RelOptInfo.pathlist. (Actually, we discard Paths that are obviously ...@@ -35,10 +35,10 @@ RelOptInfo.pathlist. (Actually, we discard Paths that are obviously
inferior alternatives before they ever get into the pathlist --- what inferior alternatives before they ever get into the pathlist --- what
ends up in the pathlist is the cheapest way of generating each potentially ends up in the pathlist is the cheapest way of generating each potentially
useful sort ordering of the relation.) Also create RelOptInfo.joininfo useful sort ordering of the relation.) Also create RelOptInfo.joininfo
nodes that list all the joins that involve this relation. For example, nodes that list all the join clauses that involve this relation. For
the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo for tab1 example, the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo
listing tab2 as an unjoined relation, and also one for tab2 showing tab1 for tab1 listing tab2 as an unjoined relation, and also one for tab2
as an unjoined relation. showing tab1 as an unjoined relation.
If we have only a single base relation in the query, we are done now. If we have only a single base relation in the query, we are done now.
Otherwise we have to figure out how to join the base relations into a Otherwise we have to figure out how to join the base relations into a
...@@ -128,6 +128,19 @@ Once we have built the final join rel, we use either the cheapest path ...@@ -128,6 +128,19 @@ Once we have built the final join rel, we use either the cheapest path
for it or the cheapest path with the desired ordering (if that's cheaper for it or the cheapest path with the desired ordering (if that's cheaper
than applying a sort to the cheapest other path). than applying a sort to the cheapest other path).
The above dynamic-programming search is only conducted for simple cross
joins (ie, SELECT FROM tab1, tab2, ...). When the FROM clause contains
explicit JOIN clauses, we join rels in exactly the order implied by the
join tree. Searching for the best possible join order is done only at
the top implicit-cross-join level. For example, in
SELECT FROM tab1, tab2, (tab3 NATURAL JOIN tab4)
we will always join tab3 to tab4 and then consider all ways to join that
result to tab1 and tab2. Note that the JOIN syntax only constrains the
order of joining --- we will still consider all available Paths and
join methods for each JOIN operator. We also consider both sides of
the JOIN operator as inner or outer (so that we can transform RIGHT JOIN
into LEFT JOIN).
Optimizer Functions Optimizer Functions
------------------- -------------------
...@@ -158,13 +171,12 @@ planner() ...@@ -158,13 +171,12 @@ planner()
get a target list that only contains column names, no expressions get a target list that only contains column names, no expressions
if none, then return if none, then return
---subplanner() ---subplanner()
make list of relations in target make list of base relations used in query
make list of relations in where clause
split up the qual into restrictions (a=1) and joins (b=c) split up the qual into restrictions (a=1) and joins (b=c)
find relation clauses can do merge sort and hash joins find relation clauses that can do merge sort and hash joins
----make_one_rel() ----make_one_rel()
set_base_rel_pathlist() set_base_rel_pathlist()
find scan and all index paths for each relation find scan and all index paths for each base relation
find selectivity of columns used in joins find selectivity of columns used in joins
-----make_one_rel_by_joins() -----make_one_rel_by_joins()
jump to geqo if needed jump to geqo if needed
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: geqo_eval.c,v 1.53 2000/07/28 02:13:16 tgl Exp $ * $Id: geqo_eval.c,v 1.54 2000/09/12 21:06:50 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -93,11 +93,11 @@ geqo_eval(Query *root, Gene *tour, int num_gene) ...@@ -93,11 +93,11 @@ geqo_eval(Query *root, Gene *tour, int num_gene)
* Returns a new join relation incorporating all joins in a left-sided tree. * Returns a new join relation incorporating all joins in a left-sided tree.
*/ */
RelOptInfo * RelOptInfo *
gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old_rel) gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene,
RelOptInfo *old_rel)
{ {
RelOptInfo *inner_rel; /* current relation */ RelOptInfo *inner_rel; /* current relation */
int base_rel_index; int base_rel_index;
RelOptInfo *new_rel;
if (rel_count < num_gene) if (rel_count < num_gene)
{ /* tree not yet finished */ { /* tree not yet finished */
...@@ -116,16 +116,22 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old ...@@ -116,16 +116,22 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
else else
{ /* tree main part */ { /* tree main part */
List *acceptable_rels = lcons(inner_rel, NIL); List *acceptable_rels = lcons(inner_rel, NIL);
List *new_rels;
new_rel = make_rels_by_clause_joins(root, old_rel, RelOptInfo *new_rel;
acceptable_rels);
if (!new_rel) new_rels = make_rels_by_clause_joins(root, old_rel,
acceptable_rels);
/* Shouldn't get more than one result */
Assert(length(new_rels) <= 1);
if (new_rels == NIL)
{ {
new_rel = make_rels_by_clauseless_joins(root, old_rel, new_rels = make_rels_by_clauseless_joins(root, old_rel,
acceptable_rels); acceptable_rels);
if (!new_rel) Assert(length(new_rels) <= 1);
if (new_rels == NIL)
elog(ERROR, "gimme_tree: failed to construct join rel"); elog(ERROR, "gimme_tree: failed to construct join rel");
} }
new_rel = (RelOptInfo *) lfirst(new_rels);
rel_count++; rel_count++;
Assert(length(new_rel->relids) == rel_count); Assert(length(new_rel->relids) == rel_count);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.62 2000/05/31 00:28:22 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.63 2000/09/12 21:06:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,7 +26,9 @@ int geqo_rels = DEFAULT_GEQO_RELS; ...@@ -26,7 +26,9 @@ int geqo_rels = DEFAULT_GEQO_RELS;
static void set_base_rel_pathlist(Query *root); static void set_base_rel_pathlist(Query *root);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed); static List *build_jointree_rels(Query *root);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
#ifdef OPTIMIZER_DEBUG #ifdef OPTIMIZER_DEBUG
static void debug_print_rel(Query *root, RelOptInfo *rel); static void debug_print_rel(Query *root, RelOptInfo *rel);
...@@ -43,27 +45,38 @@ RelOptInfo * ...@@ -43,27 +45,38 @@ RelOptInfo *
make_one_rel(Query *root) make_one_rel(Query *root)
{ {
int levels_needed; int levels_needed;
List *initial_rels;
/* /*
* Set the number of join (not nesting) levels yet to be processed. * Count the number of top-level jointree nodes. This is the depth
* of the dynamic-programming algorithm we must employ to consider
* all ways of joining the top-level nodes. Currently, we build
* JoinExpr joins in exactly the order implied by the join expression,
* so no dynamic-programming search is needed within a JoinExpr.
*/ */
levels_needed = length(root->base_rel_list); levels_needed = length(root->jointree);
if (levels_needed <= 0) if (levels_needed <= 0)
return NULL; return NULL; /* nothing to do? */
/* /*
* Generate access paths for the base rels. * Generate access paths for the base rels.
*/ */
set_base_rel_pathlist(root); set_base_rel_pathlist(root);
/*
* Construct a list of rels corresponding to the toplevel jointree nodes.
* This may contain both base rels and rels constructed according to
* explicit JOIN directives.
*/
initial_rels = build_jointree_rels(root);
if (levels_needed == 1) if (levels_needed == 1)
{ {
/* /*
* Single relation, no more processing is required. * Single jointree node, so we're done.
*/ */
return (RelOptInfo *) lfirst(root->base_rel_list); return (RelOptInfo *) lfirst(initial_rels);
} }
else else
{ {
...@@ -71,7 +84,7 @@ make_one_rel(Query *root) ...@@ -71,7 +84,7 @@ make_one_rel(Query *root)
/* /*
* Generate join tree. * Generate join tree.
*/ */
return make_one_rel_by_joins(root, levels_needed); return make_one_rel_by_joins(root, levels_needed, initial_rels);
} }
} }
...@@ -125,20 +138,47 @@ set_base_rel_pathlist(Query *root) ...@@ -125,20 +138,47 @@ set_base_rel_pathlist(Query *root)
} }
} }
/*
* build_jointree_rels
* Construct a RelOptInfo for each item in the query's jointree.
*
* At present, we handle explicit joins in the FROM clause exactly as
* specified, with no search for other join orders. Only the cross-product
* joins at the top level are involved in the dynamic-programming search.
*/
static List *
build_jointree_rels(Query *root)
{
List *rels = NIL;
List *jt;
foreach(jt, root->jointree)
{
Node *jtnode = (Node *) lfirst(jt);
rels = lappend(rels, make_rel_from_jointree(root, jtnode));
}
return rels;
}
/* /*
* make_one_rel_by_joins * make_one_rel_by_joins
* Find all possible joinpaths for a query by successively finding ways * Find all possible joinpaths for a query by successively finding ways
* to join component relations into join relations. * to join component relations into join relations.
* *
* 'levels_needed' is the number of iterations needed, ie, the number of * 'levels_needed' is the number of iterations needed, ie, the number of
* base relations present in the query * independent jointree items in the query. This is > 1.
*
* 'initial_rels' is a list of RelOptInfo nodes for each independent
* jointree item. These are the components to be joined together.
* *
* Returns the final level of join relations, i.e., the relation that is * Returns the final level of join relations, i.e., the relation that is
* the result of joining all the original relations together. * the result of joining all the original relations together.
*/ */
static RelOptInfo * static RelOptInfo *
make_one_rel_by_joins(Query *root, int levels_needed) make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
{ {
List **joinitems;
int lev; int lev;
RelOptInfo *rel; RelOptInfo *rel;
...@@ -152,34 +192,35 @@ make_one_rel_by_joins(Query *root, int levels_needed) ...@@ -152,34 +192,35 @@ make_one_rel_by_joins(Query *root, int levels_needed)
/* /*
* We employ a simple "dynamic programming" algorithm: we first find * We employ a simple "dynamic programming" algorithm: we first find
* all ways to build joins of two base relations, then all ways to * all ways to build joins of two jointree items, then all ways to
* build joins of three base relations (from two-base-rel joins and * build joins of three items (from two-item joins and single items),
* other base rels), then four-base-rel joins, and so on until we have * then four-item joins, and so on until we have considered all ways
* considered all ways to join all N relations into one rel. * to join all the items into one rel.
*
* joinitems[j] is a list of all the j-item rels. Initially we set
* joinitems[1] to represent all the single-jointree-item relations.
*/ */
joinitems = (List **) palloc((levels_needed+1) * sizeof(List *));
MemSet(joinitems, 0, (levels_needed+1) * sizeof(List *));
joinitems[1] = initial_rels;
for (lev = 2; lev <= levels_needed; lev++) for (lev = 2; lev <= levels_needed; lev++)
{ {
List *first_old_rel = root->join_rel_list;
List *x; List *x;
/* /*
* Determine all possible pairs of relations to be joined at this * Determine all possible pairs of relations to be joined at this
* level, and build paths for making each one from every available * level, and build paths for making each one from every available
* pair of lower-level relations. Results are prepended to * pair of lower-level relations.
* root->join_rel_list.
*/ */
make_rels_by_joins(root, lev); joinitems[lev] = make_rels_by_joins(root, lev, joinitems);
/* /*
* The relations created at the current level will appear at the * Do cleanup work on each just-processed rel.
* front of root->join_rel_list.
*/ */
foreach(x, root->join_rel_list) foreach(x, joinitems[lev])
{ {
if (x == first_old_rel)
break; /* no more rels added at this level */
rel = (RelOptInfo *) lfirst(x); rel = (RelOptInfo *) lfirst(x);
#ifdef NOT_USED #ifdef NOT_USED
...@@ -202,14 +243,12 @@ make_one_rel_by_joins(Query *root, int levels_needed) ...@@ -202,14 +243,12 @@ make_one_rel_by_joins(Query *root, int levels_needed)
} }
/* /*
* Now, the front of the join_rel_list should be the single rel * We should have a single rel at the final level,
* representing the join of all the base rels. * representing the join of all the base rels.
*/ */
Assert(length(root->join_rel_list) > 0); Assert(length(joinitems[levels_needed]) == 1);
rel = (RelOptInfo *) lfirst(root->join_rel_list); rel = (RelOptInfo *) lfirst(joinitems[levels_needed]);
Assert(length(rel->relids) == levels_needed); Assert(length(rel->relids) == length(root->base_rel_list));
Assert(length(root->join_rel_list) == 1 ||
length(((RelOptInfo *) lsecond(root->join_rel_list))->relids) < levels_needed);
return rel; return rel;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.94 2000/08/24 03:29:04 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.95 2000/09/12 21:06:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1482,7 +1482,9 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, ...@@ -1482,7 +1482,9 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
{ {
List *clausegroup = lfirst(i); List *clausegroup = lfirst(i);
IndexPath *pathnode = makeNode(IndexPath); IndexPath *pathnode = makeNode(IndexPath);
List *indexquals; List *indexquals = NIL;
bool alljoinquals = true;
List *temp;
/* XXX this code ought to be merged with create_index_path? */ /* XXX this code ought to be merged with create_index_path? */
...@@ -1496,7 +1498,16 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, ...@@ -1496,7 +1498,16 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
*/ */
pathnode->path.pathkeys = NIL; pathnode->path.pathkeys = NIL;
indexquals = get_actual_clauses(clausegroup); /* extract bare indexqual clauses, check whether all from JOIN/ON */
foreach(temp, clausegroup)
{
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
indexquals = lappend(indexquals, clause->clause);
if (! clause->isjoinqual)
alljoinquals = false;
}
/* expand special operators to indexquals the executor can handle */ /* expand special operators to indexquals the executor can handle */
indexquals = expand_indexqual_conditions(indexquals); indexquals = expand_indexqual_conditions(indexquals);
...@@ -1514,6 +1525,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, ...@@ -1514,6 +1525,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
/* joinrelids saves the rels needed on the outer side of the join */ /* joinrelids saves the rels needed on the outer side of the join */
pathnode->joinrelids = lfirst(outerrelids_list); pathnode->joinrelids = lfirst(outerrelids_list);
pathnode->alljoinquals = alljoinquals;
/* /*
* We must compute the estimated number of output rows for the * We must compute the estimated number of output rows for the
* indexscan. This is less than rel->rows because of the * indexscan. This is less than rel->rows because of the
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.40 2000/05/30 00:49:47 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.41 2000/09/12 21:06:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,6 +101,7 @@ create_or_index_paths(Query *root, ...@@ -101,6 +101,7 @@ create_or_index_paths(Query *root,
/* This isn't a nestloop innerjoin, so: */ /* This isn't a nestloop innerjoin, so: */
pathnode->joinrelids = NIL; /* no join clauses here */ pathnode->joinrelids = NIL; /* no join clauses here */
pathnode->alljoinquals = false;
pathnode->rows = rel->rows; pathnode->rows = rel->rows;
best_or_subclause_indices(root, best_or_subclause_indices(root,
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.24 2000/08/08 15:41:31 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.25 2000/09/12 21:06:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -694,8 +694,8 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) ...@@ -694,8 +694,8 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
* *
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses * 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
* that will be used in a merge join. * that will be used in a merge join.
* 'tlist' is a relation target list for either the inner or outer * 'rel' is the relation the pathkeys will apply to (ie, either the inner
* side of the proposed join rel. (Not actually needed anymore) * or outer side of the proposed join rel).
* *
* Returns a pathkeys list that can be applied to the indicated relation. * Returns a pathkeys list that can be applied to the indicated relation.
* *
...@@ -706,7 +706,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos) ...@@ -706,7 +706,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
List * List *
make_pathkeys_for_mergeclauses(Query *root, make_pathkeys_for_mergeclauses(Query *root,
List *mergeclauses, List *mergeclauses,
List *tlist) RelOptInfo *rel)
{ {
List *pathkeys = NIL; List *pathkeys = NIL;
List *i; List *i;
...@@ -722,30 +722,37 @@ make_pathkeys_for_mergeclauses(Query *root, ...@@ -722,30 +722,37 @@ make_pathkeys_for_mergeclauses(Query *root,
Assert(restrictinfo->mergejoinoperator != InvalidOid); Assert(restrictinfo->mergejoinoperator != InvalidOid);
/* /*
* Find the key and sortop needed for this mergeclause. * Which key and sortop is needed for this relation?
*
* Both sides of the mergeclause should appear in one of the query's
* pathkey equivalence classes, so it doesn't matter which one we
* use here.
*/ */
key = (Node *) get_leftop(restrictinfo->clause); key = (Node *) get_leftop(restrictinfo->clause);
sortop = restrictinfo->left_sortop; sortop = restrictinfo->left_sortop;
if (!IsA(key, Var) ||
!intMember(((Var *) key)->varno, rel->relids))
{
key = (Node *) get_rightop(restrictinfo->clause);
sortop = restrictinfo->right_sortop;
if (!IsA(key, Var) ||
!intMember(((Var *) key)->varno, rel->relids))
elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use");
}
/* /*
* Find pathkey sublist for this sort item. We expect to find the * Find or create canonical pathkey sublist for this sort item.
* canonical set including the mergeclause's left and right sides;
* if we get back just the one item, something is rotten.
*/ */
item = makePathKeyItem(key, sortop); item = makePathKeyItem(key, sortop);
pathkey = make_canonical_pathkey(root, item); pathkey = make_canonical_pathkey(root, item);
Assert(length(pathkey) > 1);
/* /*
* Since the item we just made is not in the returned canonical * Most of the time we will get back a canonical pathkey set
* set, we can free it --- this saves a useful amount of storage * including both the mergeclause's left and right sides (the only
* in a big join tree. * case where we don't is if the mergeclause appeared in an OUTER
* JOIN, which causes us not to generate an equijoin set from it).
* Therefore, most of the time the item we just made is not part
* of the returned structure, and we can free it. This check
* saves a useful amount of storage in a big join tree.
*/ */
pfree(item); if (item != (PathKeyItem *) lfirst(pathkey))
pfree(item);
pathkeys = lappend(pathkeys, pathkey); pathkeys = lappend(pathkeys, pathkey);
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.58 2000/08/13 02:50:07 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.59 2000/09/12 21:06:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/memutils.h" #include "utils/memutils.h"
...@@ -41,11 +42,8 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual, ...@@ -41,11 +42,8 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
* not any fancier features. * not any fancier features.
* *
* tlist is the target list the query should produce (NOT root->targetList!) * tlist is the target list the query should produce (NOT root->targetList!)
* qual is the qualification of the query (likewise!)
* tuple_fraction is the fraction of tuples we expect will be retrieved * tuple_fraction is the fraction of tuples we expect will be retrieved
* *
* qual must already have been converted to implicit-AND form.
*
* Note: the Query node now also includes a query_pathkeys field, which * Note: the Query node now also includes a query_pathkeys field, which
* is both an input and an output of query_planner(). The input value * is both an input and an output of query_planner(). The input value
* signals query_planner that the indicated sort order is wanted in the * signals query_planner that the indicated sort order is wanted in the
...@@ -75,9 +73,9 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual, ...@@ -75,9 +73,9 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
Plan * Plan *
query_planner(Query *root, query_planner(Query *root,
List *tlist, List *tlist,
List *qual,
double tuple_fraction) double tuple_fraction)
{ {
List *normal_qual;
List *noncachable_qual; List *noncachable_qual;
List *constant_qual; List *constant_qual;
List *var_only_tlist; List *var_only_tlist;
...@@ -96,7 +94,7 @@ query_planner(Query *root, ...@@ -96,7 +94,7 @@ query_planner(Query *root,
root->query_pathkeys = NIL; /* signal unordered result */ root->query_pathkeys = NIL; /* signal unordered result */
/* Make childless Result node to evaluate given tlist. */ /* Make childless Result node to evaluate given tlist. */
return (Plan *) make_result(tlist, (Node *) qual, (Plan *) NULL); return (Plan *) make_result(tlist, root->qual, (Plan *) NULL);
} }
/* /*
...@@ -111,10 +109,12 @@ query_planner(Query *root, ...@@ -111,10 +109,12 @@ query_planner(Query *root,
* noncachable functions but no vars, such as "WHERE random() < 0.5". * noncachable functions but no vars, such as "WHERE random() < 0.5".
* These cannot be treated as normal restriction or join quals, but * These cannot be treated as normal restriction or join quals, but
* they're not constants either. Instead, attach them to the qpqual * they're not constants either. Instead, attach them to the qpqual
* of the top-level plan, so that they get evaluated once per potential * of the top plan, so that they get evaluated once per potential
* output tuple. * output tuple.
*/ */
qual = pull_constant_clauses(qual, &noncachable_qual, &constant_qual); normal_qual = pull_constant_clauses((List *) root->qual,
&noncachable_qual,
&constant_qual);
/* /*
* Create a target list that consists solely of (resdom var) target * Create a target list that consists solely of (resdom var) target
...@@ -132,7 +132,7 @@ query_planner(Query *root, ...@@ -132,7 +132,7 @@ query_planner(Query *root,
/* /*
* Choose the best access path and build a plan for it. * Choose the best access path and build a plan for it.
*/ */
subplan = subplanner(root, var_only_tlist, qual, tuple_fraction); subplan = subplanner(root, var_only_tlist, normal_qual, tuple_fraction);
/* /*
* Handle the noncachable quals. * Handle the noncachable quals.
...@@ -188,6 +188,8 @@ subplanner(Query *root, ...@@ -188,6 +188,8 @@ subplanner(Query *root,
List *qual, List *qual,
double tuple_fraction) double tuple_fraction)
{ {
List *joined_rels;
List *brel;
RelOptInfo *final_rel; RelOptInfo *final_rel;
Plan *resultplan; Plan *resultplan;
MemoryContext mycontext; MemoryContext mycontext;
...@@ -196,7 +198,7 @@ subplanner(Query *root, ...@@ -196,7 +198,7 @@ subplanner(Query *root,
Path *presortedpath; Path *presortedpath;
/* /*
* Initialize the targetlist and qualification, adding entries to * Examine the targetlist and qualifications, adding entries to
* base_rel_list as relation references are found (e.g., in the * base_rel_list as relation references are found (e.g., in the
* qualification, the targetlist, etc.). Restrict and join clauses * qualification, the targetlist, etc.). Restrict and join clauses
* are added to appropriate lists belonging to the mentioned * are added to appropriate lists belonging to the mentioned
...@@ -207,13 +209,29 @@ subplanner(Query *root, ...@@ -207,13 +209,29 @@ subplanner(Query *root,
root->join_rel_list = NIL; root->join_rel_list = NIL;
root->equi_key_list = NIL; root->equi_key_list = NIL;
make_var_only_tlist(root, flat_tlist); build_base_rel_tlists(root, flat_tlist);
(void) add_join_quals_to_rels(root, (Node *) root->jointree);
/* this must happen after add_join_quals_to_rels: */
add_restrict_and_join_to_rels(root, qual); add_restrict_and_join_to_rels(root, qual);
/* /*
* Make sure we have RelOptInfo nodes for all relations used. * Make sure we have RelOptInfo nodes for all relations to be joined.
*/
joined_rels = add_missing_rels_to_query(root, (Node *) root->jointree);
/*
* Check that the join tree includes all the base relations used in
* the query --- otherwise, the parser or rewriter messed up.
*/ */
add_missing_rels_to_query(root); foreach(brel, root->base_rel_list)
{
RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
int relid = lfirsti(baserel->relids);
if (! ptrMember(baserel, joined_rels))
elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
rt_fetch(relid, root->rtable)->eref->relname, relid);
}
/* /*
* Use the completed lists of equijoined keys to deduce any implied * Use the completed lists of equijoined keys to deduce any implied
...@@ -258,12 +276,11 @@ subplanner(Query *root, ...@@ -258,12 +276,11 @@ subplanner(Query *root,
* We expect to end up here for a trivial INSERT ... VALUES query * We expect to end up here for a trivial INSERT ... VALUES query
* (which will have a target relation, so it gets past * (which will have a target relation, so it gets past
* query_planner's check for empty range table; but the target rel * query_planner's check for empty range table; but the target rel
* is unreferenced and not marked inJoinSet, so we find there is * is not in the join tree, so we find there is nothing to join).
* nothing to join).
* *
* It's also possible to get here if the query was rewritten by the * It's also possible to get here if the query was rewritten by the
* rule processor (creating rangetable entries not marked * rule processor (creating dummy rangetable entries that are not in
* inJoinSet) but the rules either did nothing or were simplified * the join tree) but the rules either did nothing or were simplified
* to nothing by constant-expression folding. So, don't complain. * to nothing by constant-expression folding. So, don't complain.
*/ */
root->query_pathkeys = NIL; /* signal unordered result */ root->query_pathkeys = NIL; /* signal unordered result */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.88 2000/08/21 20:55:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.89 2000/09/12 21:06:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static void preprocess_join_conditions(Query *parse, Node *jtnode);
static List *make_subplanTargetList(Query *parse, List *tlist, static List *make_subplanTargetList(Query *parse, List *tlist,
AttrNumber **groupColIdx); AttrNumber **groupColIdx);
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup, static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
...@@ -163,6 +164,7 @@ subquery_planner(Query *parse, double tuple_fraction) ...@@ -163,6 +164,7 @@ subquery_planner(Query *parse, double tuple_fraction)
* canonicalize_qual? * canonicalize_qual?
*/ */
parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true); parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true);
#ifdef OPTIMIZER_DEBUG #ifdef OPTIMIZER_DEBUG
printf("After canonicalize_qual()\n"); printf("After canonicalize_qual()\n");
pprint(parse->qual); pprint(parse->qual);
...@@ -211,6 +213,9 @@ subquery_planner(Query *parse, double tuple_fraction) ...@@ -211,6 +213,9 @@ subquery_planner(Query *parse, double tuple_fraction)
parse->havingQual = SS_replace_correlation_vars(parse->havingQual); parse->havingQual = SS_replace_correlation_vars(parse->havingQual);
} }
/* Do all the above for each qual condition (ON clause) in the join tree */
preprocess_join_conditions(parse, (Node *) parse->jointree);
/* Do the main planning (potentially recursive) */ /* Do the main planning (potentially recursive) */
return union_planner(parse, tuple_fraction); return union_planner(parse, tuple_fraction);
...@@ -224,6 +229,58 @@ subquery_planner(Query *parse, double tuple_fraction) ...@@ -224,6 +229,58 @@ subquery_planner(Query *parse, double tuple_fraction)
*/ */
} }
/*
* preprocess_join_conditions
* Recursively scan the query's jointree and do subquery_planner's
* qual preprocessing work on each ON condition found therein.
*/
static void
preprocess_join_conditions(Query *parse, Node *jtnode)
{
if (jtnode == NULL)
return;
if (IsA(jtnode, List))
{
List *l;
foreach(l, (List *) jtnode)
preprocess_join_conditions(parse, lfirst(l));
}
else if (IsA(jtnode, RangeTblRef))
{
/* nothing to do here */
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
preprocess_join_conditions(parse, j->larg);
preprocess_join_conditions(parse, j->rarg);
/* Simplify constant expressions */
j->quals = eval_const_expressions(j->quals);
/* Canonicalize the qual, and convert it to implicit-AND format */
j->quals = (Node *) canonicalize_qual((Expr *) j->quals, true);
/* Expand SubLinks to SubPlans */
if (parse->hasSubLinks)
{
j->quals = SS_process_sublinks(j->quals);
/*
* ON conditions, like WHERE clauses, are evaluated pre-GROUP;
* so we allow ungrouped vars in them.
*/
}
/* Replace uplevel vars with Param nodes */
if (PlannerQueryLevel > 1)
j->quals = SS_replace_correlation_vars(j->quals);
}
else
elog(ERROR, "preprocess_join_conditions: unexpected node type %d",
nodeTag(jtnode));
}
/*-------------------- /*--------------------
* union_planner * union_planner
...@@ -542,7 +599,6 @@ union_planner(Query *parse, ...@@ -542,7 +599,6 @@ union_planner(Query *parse,
/* Generate the (sub) plan */ /* Generate the (sub) plan */
result_plan = query_planner(parse, result_plan = query_planner(parse,
sub_tlist, sub_tlist,
(List *) parse->qual,
tuple_fraction); tuple_fraction);
/* /*
......
This diff is collapsed.
This diff is collapsed.
...@@ -107,6 +107,7 @@ transformKeySetQuery(Query *origNode) ...@@ -107,6 +107,7 @@ transformKeySetQuery(Query *origNode)
Node_Copy(origNode, unionNode, distinctClause); Node_Copy(origNode, unionNode, distinctClause);
Node_Copy(origNode, unionNode, sortClause); Node_Copy(origNode, unionNode, sortClause);
Node_Copy(origNode, unionNode, rtable); Node_Copy(origNode, unionNode, rtable);
Node_Copy(origNode, unionNode, jointree);
Node_Copy(origNode, unionNode, targetList); Node_Copy(origNode, unionNode, targetList);
origNode->unionClause = lappend(origNode->unionClause, unionNode); origNode->unionClause = lappend(origNode->unionClause, unionNode);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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