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!
This commit is contained in:
parent
b5c0ab278b
commit
ed5003c584
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
||||
|
@ -1538,11 +1538,9 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
|
|||
*/
|
||||
rte = makeNode(RangeTblEntry);
|
||||
rte->relname = RelationGetRelationName(rel);
|
||||
#ifndef DISABLE_EREF
|
||||
rte->ref = makeNode(Attr);
|
||||
rte->ref->relname = RelationGetRelationName(rel);
|
||||
#endif
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->eref = makeNode(Attr);
|
||||
rte->eref->relname = RelationGetRelationName(rel);
|
||||
rte->inh = false;
|
||||
rte->inFromCl = true;
|
||||
rte->skipAcl = false;
|
||||
|
@ -1623,11 +1621,9 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
|
|||
*/
|
||||
rte = makeNode(RangeTblEntry);
|
||||
rte->relname = RelationGetRelationName(rel);
|
||||
#ifndef DISABLE_EREF
|
||||
rte->ref = makeNode(Attr);
|
||||
rte->ref->relname = RelationGetRelationName(rel);
|
||||
#endif
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->eref = makeNode(Attr);
|
||||
rte->eref->relname = RelationGetRelationName(rel);
|
||||
rte->inh = false;
|
||||
rte->inFromCl = true;
|
||||
rte->skipAcl = false;
|
||||
|
@ -1723,6 +1719,7 @@ AddRelationRawConstraints(Relation rel,
|
|||
int numoldchecks;
|
||||
ConstrCheck *oldchecks;
|
||||
ParseState *pstate;
|
||||
RangeTblEntry *rte;
|
||||
int numchecks;
|
||||
List *listptr;
|
||||
Relation relrel;
|
||||
|
@ -1752,7 +1749,8 @@ AddRelationRawConstraints(Relation rel,
|
|||
*/
|
||||
pstate = make_parsestate(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.
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.102 2000/09/12 05:09:43 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.103 2000/09/12 21:06:47 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The PerformAddAttribute() code, like most of the relation
|
||||
|
@ -61,8 +61,6 @@ static bool is_viewr(char *relname);
|
|||
static bool is_view(Relation rel);
|
||||
|
||||
|
||||
|
||||
|
||||
/* --------------------------------
|
||||
* PortalCleanup
|
||||
* --------------------------------
|
||||
|
@ -536,7 +534,6 @@ AlterTableAlterColumn(const char *relationName,
|
|||
rel = heap_openr(relationName, AccessExclusiveLock);
|
||||
if ( rel->rd_rel->relkind == RELKIND_VIEW )
|
||||
elog(ERROR, "ALTER TABLE: %s is a view", relationName);
|
||||
|
||||
myrelid = RelationGetRelid(rel);
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
|
@ -782,7 +779,7 @@ systable_getnext(void *scan)
|
|||
* find a specified attribute in a node entry
|
||||
*/
|
||||
static bool
|
||||
find_attribute_walker(Node *node, int attnum)
|
||||
find_attribute_walker(Node *node, int *attnump)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
|
@ -791,16 +788,17 @@ find_attribute_walker(Node *node, int attnum)
|
|||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == 0 && var->varno == 1 &&
|
||||
var->varattno == attnum)
|
||||
var->varattno == *attnump)
|
||||
return true;
|
||||
}
|
||||
return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
|
||||
return expression_tree_walker(node, find_attribute_walker,
|
||||
(void *) attnump);
|
||||
}
|
||||
|
||||
static bool
|
||||
find_attribute_in_node(Node *node, int attnum)
|
||||
{
|
||||
return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
|
||||
return find_attribute_walker(node, &attnum);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1096,7 +1094,6 @@ void
|
|||
AlterTableAddConstraint(char *relationName,
|
||||
bool inh, Node *newConstraint)
|
||||
{
|
||||
|
||||
if (newConstraint == NULL)
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint.");
|
||||
|
||||
|
@ -1108,328 +1105,330 @@ AlterTableAddConstraint(char *relationName,
|
|||
/* check to see if the table to be constrained is a view. */
|
||||
if (is_viewr(relationName))
|
||||
elog(ERROR, "ALTER TABLE: Cannot add constraints to views.");
|
||||
|
||||
|
||||
switch (nodeTag(newConstraint))
|
||||
{
|
||||
case T_Constraint:
|
||||
{
|
||||
Constraint *constr = (Constraint *) newConstraint;
|
||||
|
||||
switch (constr->contype)
|
||||
{
|
||||
Constraint *constr=(Constraint *)newConstraint;
|
||||
switch (constr->contype) {
|
||||
case CONSTR_CHECK:
|
||||
case CONSTR_CHECK:
|
||||
{
|
||||
ParseState *pstate;
|
||||
bool successful = TRUE;
|
||||
HeapScanDesc scan;
|
||||
ExprContext *econtext;
|
||||
TupleTableSlot *slot = makeNode(TupleTableSlot);
|
||||
HeapTuple tuple;
|
||||
RangeTblEntry *rte;
|
||||
List *rtlist;
|
||||
List *qual;
|
||||
List *constlist;
|
||||
Relation rel;
|
||||
Node *expr;
|
||||
char *name;
|
||||
|
||||
if (constr->name)
|
||||
name=constr->name;
|
||||
else
|
||||
name="<unnamed>";
|
||||
|
||||
constlist=lcons(constr, NIL);
|
||||
|
||||
rel = heap_openr(relationName, AccessExclusiveLock);
|
||||
|
||||
/* make sure it is not a view */
|
||||
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "ALTER TABLE: cannot add constraint to a view");
|
||||
|
||||
/*
|
||||
* Scan all of the rows, looking for a false match
|
||||
*/
|
||||
scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
|
||||
AssertState(scan != NULL);
|
||||
|
||||
/*
|
||||
* We need to make a parse state and range table to allow
|
||||
* us to transformExpr and fix_opids to get a version of
|
||||
* the expression we can pass to ExecQual
|
||||
*/
|
||||
pstate = make_parsestate(NULL);
|
||||
makeRangeTable(pstate, NULL);
|
||||
rte = addRangeTableEntry(pstate, relationName, NULL,
|
||||
false, true);
|
||||
addRTEtoJoinTree(pstate, rte);
|
||||
|
||||
/* Convert the A_EXPR in raw_expr into an EXPR */
|
||||
expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST);
|
||||
|
||||
/*
|
||||
* Make sure it yields a boolean result.
|
||||
*/
|
||||
if (exprType(expr) != BOOLOID)
|
||||
elog(ERROR, "CHECK '%s' does not yield boolean result",
|
||||
name);
|
||||
|
||||
/*
|
||||
* Make sure no outside relations are referred to.
|
||||
*/
|
||||
if (length(pstate->p_rtable) != 1)
|
||||
elog(ERROR, "Only relation '%s' can be referenced in CHECK",
|
||||
relationName);
|
||||
|
||||
/*
|
||||
* Might as well try to reduce any constant expressions.
|
||||
*/
|
||||
expr = eval_const_expressions(expr);
|
||||
|
||||
/* And fix the opids */
|
||||
fix_opids(expr);
|
||||
|
||||
qual = lcons(expr, NIL);
|
||||
|
||||
rte = makeNode(RangeTblEntry);
|
||||
rte->relname = relationName;
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->eref = makeNode(Attr);
|
||||
rte->eref->relname = relationName;
|
||||
rtlist = lcons(rte, NIL);
|
||||
|
||||
/*
|
||||
* Scan through the rows now, making the necessary things
|
||||
* for ExecQual, and then call it to evaluate the
|
||||
* expression.
|
||||
*/
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
ParseState *pstate;
|
||||
bool successful=TRUE;
|
||||
HeapScanDesc scan;
|
||||
ExprContext *econtext;
|
||||
TupleTableSlot *slot = makeNode(TupleTableSlot);
|
||||
HeapTuple tuple;
|
||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||
List *rtlist;
|
||||
List *qual;
|
||||
List *constlist;
|
||||
Relation rel;
|
||||
Node *expr;
|
||||
char *name;
|
||||
if (constr->name)
|
||||
name=constr->name;
|
||||
else
|
||||
name="<unnamed>";
|
||||
slot->val = tuple;
|
||||
slot->ttc_shouldFree = false;
|
||||
slot->ttc_descIsNew = true;
|
||||
slot->ttc_tupleDescriptor = rel->rd_att;
|
||||
slot->ttc_buffer = InvalidBuffer;
|
||||
slot->ttc_whichplan = -1;
|
||||
|
||||
rel = heap_openr(relationName, AccessExclusiveLock);
|
||||
|
||||
/* make sure it is not a view */
|
||||
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "ALTER TABLE: cannot add constraint to a view");
|
||||
|
||||
/*
|
||||
* Scan all of the rows, looking for a false match
|
||||
*/
|
||||
scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
|
||||
AssertState(scan != NULL);
|
||||
|
||||
/*
|
||||
*We need to make a parse state and range table to allow us
|
||||
* to transformExpr and fix_opids to get a version of the
|
||||
* expression we can pass to ExecQual
|
||||
*/
|
||||
pstate = make_parsestate(NULL);
|
||||
makeRangeTable(pstate, NULL);
|
||||
addRangeTableEntry(pstate, relationName,
|
||||
makeAttr(relationName, NULL), false, true,true);
|
||||
constlist=lcons(constr, NIL);
|
||||
|
||||
/* Convert the A_EXPR in raw_expr into an EXPR */
|
||||
expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST);
|
||||
|
||||
/*
|
||||
* Make sure it yields a boolean result.
|
||||
*/
|
||||
if (exprType(expr) != BOOLOID)
|
||||
elog(ERROR, "CHECK '%s' does not yield boolean result",
|
||||
name);
|
||||
|
||||
/*
|
||||
* Make sure no outside relations are referred to.
|
||||
*/
|
||||
if (length(pstate->p_rtable) != 1)
|
||||
elog(ERROR, "Only relation '%s' can be referenced in CHECK",
|
||||
relationName);
|
||||
|
||||
/*
|
||||
* Might as well try to reduce any constant expressions.
|
||||
*/
|
||||
expr = eval_const_expressions(expr);
|
||||
|
||||
/* And fix the opids */
|
||||
fix_opids(expr);
|
||||
|
||||
qual = lcons(expr, NIL);
|
||||
rte->relname = relationName;
|
||||
rte->ref = makeNode(Attr);
|
||||
rte->ref->relname = rte->relname;
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rtlist = lcons(rte, NIL);
|
||||
|
||||
/*
|
||||
* Scan through the rows now, making the necessary things for
|
||||
* ExecQual, and then call it to evaluate the expression.
|
||||
*/
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
econtext = MakeExprContext(slot, CurrentMemoryContext);
|
||||
econtext->ecxt_range_table = rtlist; /* range table */
|
||||
if (!ExecQual(qual, econtext, true))
|
||||
{
|
||||
slot->val = tuple;
|
||||
slot->ttc_shouldFree = false;
|
||||
slot->ttc_descIsNew = true;
|
||||
slot->ttc_tupleDescriptor = rel->rd_att;
|
||||
slot->ttc_buffer = InvalidBuffer;
|
||||
slot->ttc_whichplan = -1;
|
||||
|
||||
econtext = MakeExprContext(slot, CurrentMemoryContext);
|
||||
econtext->ecxt_range_table = rtlist; /* range table */
|
||||
if (!ExecQual(qual, econtext, true)) {
|
||||
successful=false;
|
||||
break;
|
||||
}
|
||||
FreeExprContext(econtext);
|
||||
successful=false;
|
||||
break;
|
||||
}
|
||||
|
||||
pfree(slot);
|
||||
pfree(rtlist);
|
||||
pfree(rte);
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
if (!successful)
|
||||
{
|
||||
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
|
||||
}
|
||||
/*
|
||||
* Call AddRelationRawConstraints to do the real adding -- It duplicates some
|
||||
* of the above, but does not check the validity of the constraint against
|
||||
* tuples already in the table.
|
||||
*/
|
||||
AddRelationRawConstraints(rel, NIL, constlist);
|
||||
pfree(constlist);
|
||||
|
||||
break;
|
||||
FreeExprContext(econtext);
|
||||
}
|
||||
default:
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
|
||||
|
||||
pfree(slot);
|
||||
pfree(rtlist);
|
||||
pfree(rte);
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
if (!successful)
|
||||
{
|
||||
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
|
||||
}
|
||||
/*
|
||||
* Call AddRelationRawConstraints to do the real adding --
|
||||
* It duplicates some of the above, but does not check the
|
||||
* validity of the constraint against tuples already in
|
||||
* the table.
|
||||
*/
|
||||
AddRelationRawConstraints(rel, NIL, constlist);
|
||||
pfree(constlist);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case T_FkConstraint:
|
||||
{
|
||||
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
|
||||
Relation rel, pkrel;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
Trigger trig;
|
||||
List *list;
|
||||
int count;
|
||||
List *indexoidlist,
|
||||
*indexoidscan;
|
||||
Form_pg_index indexStruct = NULL;
|
||||
Form_pg_attribute *rel_attrs = NULL;
|
||||
int i;
|
||||
int found=0;
|
||||
{
|
||||
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
|
||||
Relation rel, pkrel;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
Trigger trig;
|
||||
List *list;
|
||||
int count;
|
||||
List *indexoidlist,
|
||||
*indexoidscan;
|
||||
Form_pg_index indexStruct = NULL;
|
||||
Form_pg_attribute *rel_attrs = NULL;
|
||||
int i;
|
||||
int found=0;
|
||||
|
||||
if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL &&
|
||||
get_temp_rel_by_username(relationName)==NULL) {
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
|
||||
}
|
||||
if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL &&
|
||||
get_temp_rel_by_username(relationName)==NULL) {
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the pk table, so that someone
|
||||
* doesn't delete rows out from under us.
|
||||
*/
|
||||
/*
|
||||
* Grab an exclusive lock on the pk table, so that someone
|
||||
* doesn't delete rows out from under us.
|
||||
*/
|
||||
|
||||
pkrel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock);
|
||||
if (pkrel == NULL)
|
||||
elog(ERROR, "referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
pkrel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock);
|
||||
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "referenced table \"%s\" not a relation",
|
||||
fkconstraint->pktable_name);
|
||||
|
||||
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "referenced table \"%s\" not a relation",
|
||||
fkconstraint->pktable_name);
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the fk table, and then scan
|
||||
* through each tuple, calling the RI_FKey_Match_Ins
|
||||
* (insert trigger) as if that tuple had just been
|
||||
* inserted. If any of those fail, it should elog(ERROR)
|
||||
* and that's that.
|
||||
*/
|
||||
rel = heap_openr(relationName, AccessExclusiveLock);
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "referencing table \"%s\" not a relation",
|
||||
relationName);
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the fk table, and then scan
|
||||
* through each tuple, calling the RI_FKey_Match_Ins
|
||||
* (insert trigger) as if that tuple had just been
|
||||
* inserted. If any of those fail, it should elog(ERROR)
|
||||
* and that's that.
|
||||
*/
|
||||
rel = heap_openr(relationName, AccessExclusiveLock);
|
||||
if (rel == NULL)
|
||||
elog(ERROR, "table \"%s\" not found",
|
||||
relationName);
|
||||
/* First we check for limited correctness of the constraint */
|
||||
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "referencing table \"%s\" not a relation", relationName);
|
||||
rel_attrs = pkrel->rd_att->attrs;
|
||||
indexoidlist = RelationGetIndexList(pkrel);
|
||||
|
||||
/* First we check for limited correctness of the constraint */
|
||||
foreach(indexoidscan, indexoidlist)
|
||||
{
|
||||
Oid indexoid = lfirsti(indexoidscan);
|
||||
HeapTuple indexTuple;
|
||||
List *attrl;
|
||||
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
||||
ObjectIdGetDatum(indexoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indexTuple))
|
||||
elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
|
||||
indexoid);
|
||||
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
|
||||
rel_attrs = pkrel->rd_att->attrs;
|
||||
indexoidlist = RelationGetIndexList(pkrel);
|
||||
|
||||
foreach(indexoidscan, indexoidlist)
|
||||
{
|
||||
Oid indexoid = lfirsti(indexoidscan);
|
||||
HeapTuple indexTuple;
|
||||
List *attrl;
|
||||
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
||||
ObjectIdGetDatum(indexoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indexTuple))
|
||||
elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
|
||||
indexoid);
|
||||
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
|
||||
if (indexStruct->indisunique) {
|
||||
/* go through the fkconstraint->pk_attrs list */
|
||||
foreach(attrl, fkconstraint->pk_attrs) {
|
||||
Ident *attr=lfirst(attrl);
|
||||
found=0;
|
||||
for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
|
||||
{
|
||||
int pkattno = indexStruct->indkey[i];
|
||||
if (indexStruct->indisunique) {
|
||||
/* go through the fkconstraint->pk_attrs list */
|
||||
foreach(attrl, fkconstraint->pk_attrs) {
|
||||
Ident *attr=lfirst(attrl);
|
||||
found=0;
|
||||
for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
|
||||
{
|
||||
int pkattno = indexStruct->indkey[i];
|
||||
if (pkattno>0) {
|
||||
char *name = NameStr(rel_attrs[pkattno-1]->attname);
|
||||
if (strcmp(name, attr->name)==0) {
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
if (strcmp(name, attr->name)==0) {
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
indexStruct = NULL;
|
||||
}
|
||||
if (!found)
|
||||
elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
indexStruct = NULL;
|
||||
}
|
||||
if (!found)
|
||||
elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
|
||||
fkconstraint->pktable_name);
|
||||
|
||||
freeList(indexoidlist);
|
||||
heap_close(pkrel, NoLock);
|
||||
freeList(indexoidlist);
|
||||
heap_close(pkrel, NoLock);
|
||||
|
||||
rel_attrs = rel->rd_att->attrs;
|
||||
if (fkconstraint->fk_attrs!=NIL) {
|
||||
int found=0;
|
||||
List *fkattrs;
|
||||
Ident *fkattr;
|
||||
foreach(fkattrs, fkconstraint->fk_attrs) {
|
||||
int count=0;
|
||||
found=0;
|
||||
fkattr=lfirst(fkattrs);
|
||||
for (; count < rel->rd_att->natts; count++) {
|
||||
char *name = NameStr(rel->rd_att->attrs[count]->attname);
|
||||
if (strcmp(name, fkattr->name)==0) {
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
elog(ERROR, "columns referenced in foreign key constraint not found.");
|
||||
}
|
||||
rel_attrs = rel->rd_att->attrs;
|
||||
if (fkconstraint->fk_attrs!=NIL) {
|
||||
int found=0;
|
||||
List *fkattrs;
|
||||
Ident *fkattr;
|
||||
foreach(fkattrs, fkconstraint->fk_attrs) {
|
||||
int count=0;
|
||||
found=0;
|
||||
fkattr=lfirst(fkattrs);
|
||||
for (; count < rel->rd_att->natts; count++) {
|
||||
char *name = NameStr(rel->rd_att->attrs[count]->attname);
|
||||
if (strcmp(name, fkattr->name)==0) {
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
elog(ERROR, "columns referenced in foreign key constraint not found.");
|
||||
}
|
||||
|
||||
trig.tgoid = 0;
|
||||
if (fkconstraint->constr_name)
|
||||
trig.tgname = fkconstraint->constr_name;
|
||||
else
|
||||
trig.tgname = "<unknown>";
|
||||
trig.tgfoid = 0;
|
||||
trig.tgtype = 0;
|
||||
trig.tgenabled = TRUE;
|
||||
trig.tgisconstraint = TRUE;
|
||||
trig.tginitdeferred = FALSE;
|
||||
trig.tgdeferrable = FALSE;
|
||||
trig.tgoid = 0;
|
||||
if (fkconstraint->constr_name)
|
||||
trig.tgname = fkconstraint->constr_name;
|
||||
else
|
||||
trig.tgname = "<unknown>";
|
||||
trig.tgfoid = 0;
|
||||
trig.tgtype = 0;
|
||||
trig.tgenabled = TRUE;
|
||||
trig.tgisconstraint = TRUE;
|
||||
trig.tginitdeferred = FALSE;
|
||||
trig.tgdeferrable = FALSE;
|
||||
|
||||
trig.tgargs = (char **) palloc(
|
||||
sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
|
||||
+ length(fkconstraint->pk_attrs)));
|
||||
trig.tgargs = (char **) palloc(
|
||||
sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
|
||||
+ length(fkconstraint->pk_attrs)));
|
||||
|
||||
if (fkconstraint->constr_name)
|
||||
trig.tgargs[0] = fkconstraint->constr_name;
|
||||
else
|
||||
trig.tgargs[0] = "<unknown>";
|
||||
trig.tgargs[1] = (char *) relationName;
|
||||
trig.tgargs[2] = fkconstraint->pktable_name;
|
||||
trig.tgargs[3] = fkconstraint->match_type;
|
||||
count = 4;
|
||||
foreach(list, fkconstraint->fk_attrs)
|
||||
if (fkconstraint->constr_name)
|
||||
trig.tgargs[0] = fkconstraint->constr_name;
|
||||
else
|
||||
trig.tgargs[0] = "<unknown>";
|
||||
trig.tgargs[1] = (char *) relationName;
|
||||
trig.tgargs[2] = fkconstraint->pktable_name;
|
||||
trig.tgargs[3] = fkconstraint->match_type;
|
||||
count = 4;
|
||||
foreach(list, fkconstraint->fk_attrs)
|
||||
{
|
||||
Ident *fk_at = lfirst(list);
|
||||
|
||||
trig.tgargs[count++] = fk_at->name;
|
||||
}
|
||||
foreach(list, fkconstraint->pk_attrs)
|
||||
foreach(list, fkconstraint->pk_attrs)
|
||||
{
|
||||
Ident *pk_at = lfirst(list);
|
||||
|
||||
trig.tgargs[count++] = pk_at->name;
|
||||
}
|
||||
trig.tgnargs = count;
|
||||
trig.tgnargs = count;
|
||||
|
||||
scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
|
||||
AssertState(scan != NULL);
|
||||
scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
|
||||
AssertState(scan != NULL);
|
||||
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
/* Make a call to the check function */
|
||||
/* No parameters are passed, but we do set a context */
|
||||
FunctionCallInfoData fcinfo;
|
||||
TriggerData trigdata;
|
||||
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
|
||||
{
|
||||
/* Make a call to the check function */
|
||||
/* No parameters are passed, but we do set a context */
|
||||
FunctionCallInfoData fcinfo;
|
||||
TriggerData trigdata;
|
||||
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
/* We assume RI_FKey_check_ins won't look at flinfo... */
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
/* We assume RI_FKey_check_ins won't look at flinfo... */
|
||||
|
||||
trigdata.type = T_TriggerData;
|
||||
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
|
||||
trigdata.tg_relation = rel;
|
||||
trigdata.tg_trigtuple = tuple;
|
||||
trigdata.tg_newtuple = NULL;
|
||||
trigdata.tg_trigger = &trig;
|
||||
trigdata.type = T_TriggerData;
|
||||
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
|
||||
trigdata.tg_relation = rel;
|
||||
trigdata.tg_trigtuple = tuple;
|
||||
trigdata.tg_newtuple = NULL;
|
||||
trigdata.tg_trigger = &trig;
|
||||
|
||||
fcinfo.context = (Node *) &trigdata;
|
||||
fcinfo.context = (Node *) &trigdata;
|
||||
|
||||
RI_FKey_check_ins(&fcinfo);
|
||||
}
|
||||
heap_endscan(scan);
|
||||
heap_close(rel, NoLock); /* close rel but keep
|
||||
* lock! */
|
||||
|
||||
pfree(trig.tgargs);
|
||||
RI_FKey_check_ins(&fcinfo);
|
||||
}
|
||||
heap_endscan(scan);
|
||||
heap_close(rel, NoLock); /* close rel but keep
|
||||
* lock! */
|
||||
|
||||
pfree(trig.tgargs);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
|
||||
}
|
||||
|
@ -1449,7 +1448,6 @@ AlterTableDropConstraint(const char *relationName,
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ALTER TABLE OWNER
|
||||
*/
|
||||
|
@ -1464,14 +1462,14 @@ AlterTableOwner(const char *relationName, const char *newOwnerName)
|
|||
/*
|
||||
* first check that we are a superuser
|
||||
*/
|
||||
if (! superuser() )
|
||||
if (! superuser())
|
||||
elog(ERROR, "ALTER TABLE: permission denied");
|
||||
|
||||
/*
|
||||
* look up the new owner in pg_shadow and get the sysid
|
||||
*/
|
||||
tuple = SearchSysCacheTuple(SHADOWNAME, PointerGetDatum(newOwnerName),
|
||||
0, 0, 0);
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "ALTER TABLE: user \"%s\" not found", newOwnerName);
|
||||
|
||||
|
@ -1510,10 +1508,9 @@ AlterTableOwner(const char *relationName, const char *newOwnerName)
|
|||
*/
|
||||
heap_freetuple(tuple);
|
||||
heap_close(class_rel, RowExclusiveLock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ALTER TABLE CREATE TOAST TABLE
|
||||
*/
|
||||
|
@ -1579,6 +1576,7 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||
* allow to create TOAST tables for views. But why not - someone
|
||||
* can insert into a view, so it shouldn't be impossible to hide
|
||||
* huge data there :-)
|
||||
*
|
||||
* Not any more.
|
||||
*/
|
||||
if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION)
|
||||
|
@ -1799,8 +1797,7 @@ LockTableCommand(LockStmt *lockstmt)
|
|||
}
|
||||
|
||||
|
||||
static
|
||||
bool
|
||||
static bool
|
||||
is_viewr(char *name)
|
||||
{
|
||||
Relation rel = heap_openr(name, NoLock);
|
||||
|
@ -1812,18 +1809,15 @@ is_viewr(char *name)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_view (Relation rel)
|
||||
static bool
|
||||
is_view(Relation rel)
|
||||
{
|
||||
Relation RewriteRelation;
|
||||
HeapScanDesc scanDesc;
|
||||
ScanKeyData scanKeyData;
|
||||
HeapTuple tuple;
|
||||
Form_pg_rewrite data;
|
||||
|
||||
|
||||
bool retval = 0;
|
||||
bool retval = false;
|
||||
|
||||
/*
|
||||
* Open the pg_rewrite relation.
|
||||
|
@ -1849,7 +1843,7 @@ is_view (Relation rel)
|
|||
data = (Form_pg_rewrite) GETSTRUCT(tuple);
|
||||
if (data->ev_type == '1')
|
||||
{
|
||||
retval = 1;
|
||||
retval = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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)
|
|||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
Assert(newattno != NULL);
|
||||
if (var->varlevelsup == 0 && var->varno == 1)
|
||||
{
|
||||
/*
|
||||
|
@ -270,18 +269,19 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno)
|
|||
*/
|
||||
Assert(newattno[var->varattno - 1] > 0);
|
||||
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
|
||||
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
|
||||
* Returns new schema given initial schema and supers.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* 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)
|
|||
|
||||
appendStringInfo(str, " on %s",
|
||||
stringStringInfo(rte->relname));
|
||||
if (rte->ref != NULL)
|
||||
if (rte->alias != NULL)
|
||||
{
|
||||
if ((strcmp(rte->ref->relname, rte->relname) != 0)
|
||||
|| (length(rte->ref->attrs) > 0))
|
||||
if ((strcmp(rte->alias->relname, rte->relname) != 0)
|
||||
|| (length(rte->alias->attrs) > 0))
|
||||
{
|
||||
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;
|
||||
int firstEntry = true;
|
||||
|
||||
appendStringInfo(str, " (");
|
||||
foreach(c, rte->ref->attrs)
|
||||
foreach(c, rte->alias->attrs)
|
||||
{
|
||||
if (!firstEntry)
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* 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 *
|
|||
MakeRetrieveViewRuleName(char *viewName)
|
||||
{
|
||||
char *buf;
|
||||
#ifdef MULTIBYTE
|
||||
int len;
|
||||
#endif
|
||||
|
||||
buf = palloc(strlen(viewName) + 5);
|
||||
snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName);
|
||||
|
||||
#ifdef MULTIBYTE
|
||||
int len;
|
||||
len = pg_mbcliplen(buf,strlen(buf),NAMEDATALEN-1);
|
||||
buf[len] = '\0';
|
||||
#else
|
||||
|
@ -203,6 +205,10 @@ DefineViewRules(char *viewName, Query *viewParse)
|
|||
* Of course we must also increase the 'varnos' of all the Var nodes
|
||||
* 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
|
||||
* make a complete copy of the parse tree and make the changes
|
||||
* in the copy.
|
||||
|
@ -211,43 +217,32 @@ DefineViewRules(char *viewName, Query *viewParse)
|
|||
static void
|
||||
UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
|
||||
{
|
||||
List *old_rt;
|
||||
List *new_rt;
|
||||
RangeTblEntry *rt_entry1,
|
||||
*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
|
||||
* table... OLD first, then NEW....
|
||||
*/
|
||||
rt_entry1 = addRangeTableEntry(NULL, (char *) viewName,
|
||||
rt_entry1 = addRangeTableEntry(NULL, viewName,
|
||||
makeAttr("*OLD*", NULL),
|
||||
FALSE, FALSE, FALSE);
|
||||
rt_entry2 = addRangeTableEntry(NULL, (char *) viewName,
|
||||
false, false);
|
||||
rt_entry2 = addRangeTableEntry(NULL, viewName,
|
||||
makeAttr("*NEW*", NULL),
|
||||
FALSE, FALSE, FALSE);
|
||||
new_rt = lcons(rt_entry2, old_rt);
|
||||
new_rt = lcons(rt_entry1, new_rt);
|
||||
false, false);
|
||||
new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
|
||||
|
||||
/*
|
||||
* Now the tricky part.... Update the range table in place... Be
|
||||
* careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
|
||||
*/
|
||||
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)
|
|||
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.
|
||||
*/
|
||||
DefineVirtualRelation(viewName, viewTlist);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
*
|
||||
*
|
||||
* 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)
|
|||
* If we have a result relation, determine whether the result rel is
|
||||
* scanned or merely written. If scanned, we will insist on read
|
||||
* 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)
|
||||
{
|
||||
List *qvars = pull_varnos(parseTree->qual);
|
||||
List *tvars = pull_varnos((Node *) parseTree->targetList);
|
||||
List *qvars = pull_varnos((Node *) parseTree);
|
||||
|
||||
resultIsScanned = (intMember(resultRelation, qvars) ||
|
||||
intMember(resultRelation, tvars));
|
||||
resultIsScanned = intMember(resultRelation, qvars);
|
||||
freeList(qvars);
|
||||
freeList(tvars);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -571,8 +572,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
|||
bool isResultRelation, bool resultIsScanned)
|
||||
{
|
||||
char *relName;
|
||||
Oid userid;
|
||||
int32 aclcheck_result;
|
||||
Oid userid;
|
||||
|
||||
if (rte->skipAcl)
|
||||
{
|
||||
|
@ -703,13 +704,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
|
|||
*/
|
||||
RelationInfo *resultRelationInfo;
|
||||
Index resultRelationIndex;
|
||||
RangeTblEntry *rtentry;
|
||||
Oid resultRelationOid;
|
||||
Relation resultRelationDesc;
|
||||
|
||||
resultRelationIndex = resultRelation;
|
||||
rtentry = rt_fetch(resultRelationIndex, rangeTable);
|
||||
resultRelationOid = rtentry->relid;
|
||||
resultRelationOid = getrelid(resultRelationIndex, rangeTable);
|
||||
resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
|
||||
|
||||
if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
|
||||
|
@ -770,7 +769,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
|
|||
|
||||
if (!(rm->info & ROW_MARK_FOR_UPDATE))
|
||||
continue;
|
||||
relid = rt_fetch(rm->rti, rangeTable)->relid;
|
||||
relid = getrelid(rm->rti, rangeTable);
|
||||
relation = heap_open(relid, RowShareLock);
|
||||
erm = (execRowMark *) palloc(sizeof(execRowMark));
|
||||
erm->relation = relation;
|
||||
|
@ -1623,10 +1622,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
|
|||
rte = makeNode(RangeTblEntry);
|
||||
|
||||
rte->relname = RelationGetRelationName(rel);
|
||||
rte->ref = makeNode(Attr);
|
||||
rte->ref->relname = rte->relname;
|
||||
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 */
|
||||
econtext->ecxt_range_table = lcons(rte, NIL);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*
|
||||
*
|
||||
* 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 @@
|
|||
* type of tuple in a slot
|
||||
*
|
||||
* CONVENIENCE INITIALIZATION ROUTINES
|
||||
* ExecInitResultTupleSlot \ convience routines to initialize
|
||||
* ExecInitResultTupleSlot \ convenience routines to initialize
|
||||
* ExecInitScanTupleSlot \ the various tuple slots for nodes
|
||||
* ExecInitMarkedTupleSlot / which store copies of tuples.
|
||||
* ExecInitOuterTupleSlot /
|
||||
* ExecInitHashTupleSlot /
|
||||
* ExecInitExtraTupleSlot / which store copies of tuples.
|
||||
* ExecInitNullTupleSlot /
|
||||
*
|
||||
* old routines:
|
||||
* ExecGetTupType - get type of tuple returned by this node
|
||||
|
@ -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
|
||||
* in nodes inheriting the appropriate state.
|
||||
* These are convenience routines to initialize the specified slot
|
||||
* in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
|
||||
* is used for initializing special-purpose slots.
|
||||
* --------------------------------
|
||||
*/
|
||||
#define INIT_SLOT_DEFS \
|
||||
|
@ -583,7 +583,7 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
|
|||
{
|
||||
INIT_SLOT_DEFS;
|
||||
INIT_SLOT_ALLOC;
|
||||
commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
|
||||
commonstate->cs_ResultTupleSlot = slot;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
@ -595,50 +595,51 @@ ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
|
|||
{
|
||||
INIT_SLOT_DEFS;
|
||||
INIT_SLOT_ALLOC;
|
||||
commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* ----------------
|
||||
* ExecInitMarkedTupleSlot
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
|
||||
{
|
||||
INIT_SLOT_DEFS;
|
||||
INIT_SLOT_ALLOC;
|
||||
mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* ExecInitOuterTupleSlot
|
||||
* ----------------
|
||||
*/
|
||||
void
|
||||
ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
|
||||
{
|
||||
INIT_SLOT_DEFS;
|
||||
INIT_SLOT_ALLOC;
|
||||
hashstate->hj_OuterTupleSlot = slot;
|
||||
commonscanstate->css_ScanTupleSlot = slot;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* ExecInitHashTupleSlot
|
||||
* ExecInitExtraTupleSlot
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
|
||||
TupleTableSlot *
|
||||
ExecInitExtraTupleSlot(EState *estate)
|
||||
{
|
||||
INIT_SLOT_DEFS;
|
||||
INIT_SLOT_ALLOC;
|
||||
hashstate->hj_HashTupleSlot = slot;
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* 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.
|
||||
* ----------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
|
||||
{
|
||||
TupleTableSlot* slot = ExecInitExtraTupleSlot(estate);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
|
||||
|
||||
return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static TupleTableSlot *
|
||||
NodeGetResultTupleSlot(Plan *node)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
|||
ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
|
||||
{
|
||||
List *targetList;
|
||||
int i;
|
||||
TupleDesc tupDesc;
|
||||
int len;
|
||||
List *tl;
|
||||
TargetEntry *tle;
|
||||
List *fjtl;
|
||||
TupleDesc origTupDesc;
|
||||
|
||||
targetList = node->targetlist;
|
||||
origTupDesc = ExecTypeFromTL(targetList);
|
||||
tupDesc = ExecTypeFromTL(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)
|
||||
{
|
||||
ExecAssignResultType(commonstate,
|
||||
origTupDesc);
|
||||
}
|
||||
ExecAssignResultType(commonstate, tupDesc);
|
||||
else
|
||||
ExecAssignResultType(commonstate,
|
||||
(TupleDesc) NULL);
|
||||
ExecAssignResultType(commonstate, (TupleDesc) NULL);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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)
|
|||
Hash *hashNode;
|
||||
List *hjclauses;
|
||||
Expr *clause;
|
||||
List *qual;
|
||||
List *joinqual;
|
||||
List *otherqual;
|
||||
ScanDirection dir;
|
||||
TupleTableSlot *inntuple;
|
||||
Node *outerVar;
|
||||
|
@ -70,11 +71,12 @@ ExecHashJoin(HashJoin *node)
|
|||
hjstate = node->hashjoinstate;
|
||||
hjclauses = node->hashclauses;
|
||||
clause = lfirst(hjclauses);
|
||||
estate = node->join.state;
|
||||
qual = node->join.qual;
|
||||
estate = node->join.plan.state;
|
||||
joinqual = node->join.joinqual;
|
||||
otherqual = node->join.plan.qual;
|
||||
hashNode = (Hash *) innerPlan(node);
|
||||
outerNode = outerPlan(node);
|
||||
hashPhaseDone = node->hashdone;
|
||||
hashPhaseDone = hjstate->hj_hashdone;
|
||||
dir = estate->es_direction;
|
||||
|
||||
/* -----------------
|
||||
|
@ -132,7 +134,7 @@ ExecHashJoin(HashJoin *node)
|
|||
hashNode->hashstate->hashtable = hashtable;
|
||||
innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
|
||||
}
|
||||
node->hashdone = true;
|
||||
hjstate->hj_hashdone = true;
|
||||
/* ----------------
|
||||
* Open temp files for outer batches, if needed.
|
||||
* Note that file buffers are palloc'd in regular executor context.
|
||||
|
@ -153,11 +155,10 @@ ExecHashJoin(HashJoin *node)
|
|||
|
||||
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,
|
||||
(Plan *) node,
|
||||
|
@ -173,11 +174,15 @@ ExecHashJoin(HashJoin *node)
|
|||
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
|
||||
* for this tuple from the hash table
|
||||
*/
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
|
||||
outerVar);
|
||||
hjstate->hj_CurTuple = NULL;
|
||||
|
@ -205,7 +210,7 @@ ExecHashJoin(HashJoin *node)
|
|||
hashtable->outerBatchSize[batchno]++;
|
||||
ExecHashJoinSaveTuple(outerTupleSlot->val,
|
||||
hashtable->outerBatchFile[batchno]);
|
||||
ExecClearTuple(outerTupleSlot);
|
||||
hjstate->hj_NeedNewOuter = true;
|
||||
continue; /* loop around for a new outer tuple */
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +228,7 @@ ExecHashJoin(HashJoin *node)
|
|||
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,
|
||||
hjstate->hj_HashTupleSlot,
|
||||
|
@ -231,35 +236,77 @@ ExecHashJoin(HashJoin *node)
|
|||
false); /* don't pfree this tuple */
|
||||
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);
|
||||
|
||||
/* ----------------
|
||||
* if we pass the qual, then save state for next call and
|
||||
* have ExecProject form the projection, store it
|
||||
* 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;
|
||||
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
|
||||
if (isDone != ExprEndResult)
|
||||
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
|
||||
{
|
||||
hjstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
|
||||
return result;
|
||||
TupleTableSlot *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,
|
||||
* 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)
|
|||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->join.state = estate;
|
||||
node->join.plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create state structure
|
||||
* ----------------
|
||||
*/
|
||||
hjstate = makeNode(HashJoinState);
|
||||
|
||||
node->hashjoinstate = hjstate;
|
||||
|
||||
/* ----------------
|
||||
|
@ -298,14 +344,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
|||
*/
|
||||
ExecAssignExprContext(estate, &hjstate->jstate);
|
||||
|
||||
#define HASHJOIN_NSLOTS 2
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &hjstate->jstate);
|
||||
ExecInitOuterTupleSlot(estate, hjstate);
|
||||
|
||||
/* ----------------
|
||||
* initializes child nodes
|
||||
* ----------------
|
||||
|
@ -316,6 +354,28 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
|||
ExecInitNode(outerNode, 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
|
||||
* is actually the result tuple slot of the Hash node
|
||||
|
@ -331,11 +391,6 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
|||
|
||||
hjstate->hj_HashTupleSlot = slot;
|
||||
}
|
||||
hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
|
||||
|
||||
/*
|
||||
hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type and projection info
|
||||
|
@ -344,20 +399,25 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
|||
ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
|
||||
ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
|
||||
|
||||
ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
|
||||
ExecGetTupType(outerNode));
|
||||
|
||||
/* ----------------
|
||||
* initialize hash-specific info
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
node->hashdone = false;
|
||||
hjstate->hj_hashdone = false;
|
||||
|
||||
hjstate->hj_HashTable = (HashJoinTable) NULL;
|
||||
hjstate->hj_CurBucketNo = 0;
|
||||
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
||||
hjstate->hj_InnerHashKey = (Node *) NULL;
|
||||
|
||||
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
|
||||
hjstate->jstate.cs_OuterTupleSlot = NULL;
|
||||
hjstate->jstate.cs_TupFromTlist = false;
|
||||
hjstate->hj_NeedNewOuter = true;
|
||||
hjstate->hj_MatchedOuter = false;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -646,10 +706,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
|
|||
{
|
||||
HashJoinState *hjstate = node->hashjoinstate;
|
||||
|
||||
if (!node->hashdone)
|
||||
if (!hjstate->hj_hashdone)
|
||||
return;
|
||||
|
||||
node->hashdone = false;
|
||||
hjstate->hj_hashdone = false;
|
||||
|
||||
/*
|
||||
* Unfortunately, currently we have to destroy hashtable in all
|
||||
|
@ -667,6 +727,8 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
|
|||
|
||||
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
|
||||
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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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)
|
|||
NestLoopState *nlstate;
|
||||
Plan *innerPlan;
|
||||
Plan *outerPlan;
|
||||
bool needNewOuterTuple;
|
||||
TupleTableSlot *outerTupleSlot;
|
||||
TupleTableSlot *innerTupleSlot;
|
||||
List *qual;
|
||||
List *joinqual;
|
||||
List *otherqual;
|
||||
ExprContext *econtext;
|
||||
|
||||
/* ----------------
|
||||
|
@ -75,9 +75,10 @@ ExecNestLoop(NestLoop *node)
|
|||
ENL1_printf("getting info from node");
|
||||
|
||||
nlstate = node->nlstate;
|
||||
qual = node->join.qual;
|
||||
outerPlan = outerPlan(&node->join);
|
||||
innerPlan = innerPlan(&node->join);
|
||||
joinqual = node->join.joinqual;
|
||||
otherqual = node->join.plan.qual;
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
innerPlan = innerPlan((Plan *) node);
|
||||
econtext = nlstate->jstate.cs_ExprContext;
|
||||
|
||||
/* ----------------
|
||||
|
@ -115,7 +116,7 @@ ExecNestLoop(NestLoop *node)
|
|||
|
||||
/* ----------------
|
||||
* 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");
|
||||
|
@ -123,44 +124,14 @@ ExecNestLoop(NestLoop *node)
|
|||
for (;;)
|
||||
{
|
||||
/* ----------------
|
||||
* The essential idea now is to get the next inner tuple
|
||||
* and join it with the current outer tuple.
|
||||
* If we don't have an outer tuple, get the next one and
|
||||
* reset the inner scan.
|
||||
* ----------------
|
||||
*/
|
||||
needNewOuterTuple = TupIsNull(outerTupleSlot);
|
||||
|
||||
/* ----------------
|
||||
* if we have an outerTuple, try to get the next inner tuple.
|
||||
* ----------------
|
||||
*/
|
||||
if (!needNewOuterTuple)
|
||||
if (nlstate->nl_NeedNewOuter)
|
||||
{
|
||||
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");
|
||||
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* if there are no more outer tuples, then the join
|
||||
|
@ -175,12 +146,14 @@ ExecNestLoop(NestLoop *node)
|
|||
|
||||
ENL1_printf("saving new outer tuple information");
|
||||
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");
|
||||
|
||||
/*
|
||||
|
@ -189,48 +162,101 @@ ExecNestLoop(NestLoop *node)
|
|||
* expr context.
|
||||
*/
|
||||
ExecReScan(innerPlan, econtext, (Plan *) node);
|
||||
}
|
||||
|
||||
ENL1_printf("getting new inner tuple");
|
||||
/* ----------------
|
||||
* 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;
|
||||
innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
|
||||
econtext->ecxt_innertuple = innerTupleSlot;
|
||||
|
||||
if (TupIsNull(innerTupleSlot))
|
||||
ENL1_printf("couldn't get inner tuple - need new outer tuple");
|
||||
else
|
||||
if (TupIsNull(innerTupleSlot))
|
||||
{
|
||||
ENL1_printf("no inner tuple, need new outer tuple");
|
||||
|
||||
nlstate->nl_NeedNewOuter = true;
|
||||
|
||||
if (! nlstate->nl_MatchedOuter &&
|
||||
node->join.jointype == JOIN_LEFT)
|
||||
{
|
||||
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
|
||||
* 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");
|
||||
|
||||
if (ExecQual((List *) qual, econtext, false))
|
||||
if (ExecQual(joinqual, econtext, false))
|
||||
{
|
||||
/* ----------------
|
||||
* qualification was satisified so we project and
|
||||
* return the slot containing the result tuple
|
||||
* using ExecProject().
|
||||
* ----------------
|
||||
*/
|
||||
TupleTableSlot *result;
|
||||
ExprDoneCond isDone;
|
||||
nlstate->nl_MatchedOuter = true;
|
||||
|
||||
ENL1_printf("qualification succeeded, projecting tuple");
|
||||
|
||||
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
|
||||
|
||||
if (isDone != ExprEndResult)
|
||||
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
|
||||
{
|
||||
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)
|
|||
* assign execution state to node
|
||||
* ----------------
|
||||
*/
|
||||
node->join.state = estate;
|
||||
node->join.plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new nest loop state
|
||||
|
@ -281,13 +307,6 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
|
|||
*/
|
||||
ExecAssignExprContext(estate, &nlstate->jstate);
|
||||
|
||||
#define NESTLOOP_NSLOTS 1
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &nlstate->jstate);
|
||||
|
||||
/* ----------------
|
||||
* now initialize children
|
||||
* ----------------
|
||||
|
@ -295,6 +314,27 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
|
|||
ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
|
||||
ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
|
||||
|
||||
#define NESTLOOP_NSLOTS 2
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &nlstate->jstate);
|
||||
|
||||
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
|
||||
* ----------------
|
||||
|
@ -308,6 +348,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
|
|||
*/
|
||||
nlstate->jstate.cs_OuterTupleSlot = NULL;
|
||||
nlstate->jstate.cs_TupFromTlist = false;
|
||||
nlstate->nl_NeedNewOuter = true;
|
||||
nlstate->nl_MatchedOuter = false;
|
||||
|
||||
NL1_printf("ExecInitNestLoop: %s\n",
|
||||
"node initialized");
|
||||
|
@ -394,4 +436,6 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
|
|||
/* let outerPlan to free its result tuple ... */
|
||||
nlstate->jstate.cs_OuterTupleSlot = NULL;
|
||||
nlstate->jstate.cs_TupFromTlist = false;
|
||||
nlstate->nl_NeedNewOuter = true;
|
||||
nlstate->nl_MatchedOuter = false;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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)
|
|||
static void
|
||||
CopyJoinFields(Join *from, Join *newnode)
|
||||
{
|
||||
/* nothing extra */
|
||||
return;
|
||||
newnode->jointype = from->jointype;
|
||||
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)
|
|||
/*
|
||||
* We must add subplans in mergeclauses to the new plan's subPlan list
|
||||
*/
|
||||
if (from->join.subPlan != NIL)
|
||||
newnode->join.subPlan = nconc(newnode->join.subPlan,
|
||||
if (from->join.plan.subPlan != NIL)
|
||||
newnode->join.plan.subPlan = nconc(newnode->join.plan.subPlan,
|
||||
pull_subplans((Node *) newnode->mergeclauses));
|
||||
|
||||
return newnode;
|
||||
|
@ -414,8 +418,8 @@ _copyHashJoin(HashJoin *from)
|
|||
/*
|
||||
* We must add subplans in hashclauses to the new plan's subPlan list
|
||||
*/
|
||||
if (from->join.subPlan != NIL)
|
||||
newnode->join.subPlan = nconc(newnode->join.subPlan,
|
||||
if (from->join.plan.subPlan != NIL)
|
||||
newnode->join.plan.subPlan = nconc(newnode->join.plan.subPlan,
|
||||
pull_subplans((Node *) newnode->hashclauses));
|
||||
|
||||
return newnode;
|
||||
|
@ -510,21 +514,6 @@ _copyGroupClause(GroupClause *from)
|
|||
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
|
||||
* ----------------
|
||||
|
@ -914,6 +903,34 @@ _copyRelabelType(RelabelType *from)
|
|||
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
|
||||
* ----------------
|
||||
|
@ -1014,6 +1031,7 @@ _copyRelOptInfo(RelOptInfo *from)
|
|||
|
||||
Node_Copy(from, newnode, baserestrictinfo);
|
||||
newnode->baserestrictcost = from->baserestrictcost;
|
||||
newnode->outerjoinset = listCopy(from->outerjoinset);
|
||||
Node_Copy(from, newnode, joininfo);
|
||||
Node_Copy(from, newnode, innerjoin);
|
||||
|
||||
|
@ -1137,6 +1155,7 @@ _copyIndexPath(IndexPath *from)
|
|||
Node_Copy(from, newnode, indexqual);
|
||||
newnode->indexscandir = from->indexscandir;
|
||||
newnode->joinrelids = listCopy(from->joinrelids);
|
||||
newnode->alljoinquals = from->alljoinquals;
|
||||
newnode->rows = from->rows;
|
||||
|
||||
return newnode;
|
||||
|
@ -1177,6 +1196,7 @@ _copyTidPath(TidPath *from)
|
|||
static void
|
||||
CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
|
||||
{
|
||||
newnode->jointype = from->jointype;
|
||||
Node_Copy(from, newnode, outerjoinpath);
|
||||
Node_Copy(from, newnode, innerjoinpath);
|
||||
Node_Copy(from, newnode, joinrestrictinfo);
|
||||
|
@ -1286,6 +1306,7 @@ _copyRestrictInfo(RestrictInfo *from)
|
|||
* ----------------
|
||||
*/
|
||||
Node_Copy(from, newnode, clause);
|
||||
newnode->isjoinqual = from->isjoinqual;
|
||||
Node_Copy(from, newnode, subclauseindices);
|
||||
newnode->mergejoinoperator = from->mergejoinoperator;
|
||||
newnode->left_sortop = from->left_sortop;
|
||||
|
@ -1370,12 +1391,11 @@ _copyRangeTblEntry(RangeTblEntry *from)
|
|||
|
||||
if (from->relname)
|
||||
newnode->relname = pstrdup(from->relname);
|
||||
Node_Copy(from, newnode, ref);
|
||||
Node_Copy(from, newnode, eref);
|
||||
newnode->relid = from->relid;
|
||||
Node_Copy(from, newnode, alias);
|
||||
Node_Copy(from, newnode, eref);
|
||||
newnode->inh = from->inh;
|
||||
newnode->inFromCl = from->inFromCl;
|
||||
newnode->inJoinSet = from->inJoinSet;
|
||||
newnode->skipAcl = from->skipAcl;
|
||||
|
||||
return newnode;
|
||||
|
@ -1526,18 +1546,6 @@ _copyTypeName(TypeName *from)
|
|||
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 *
|
||||
_copySortGroupBy(SortGroupBy *from)
|
||||
{
|
||||
|
@ -1555,7 +1563,20 @@ _copyRangeVar(RangeVar *from)
|
|||
{
|
||||
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);
|
||||
|
||||
return newnode;
|
||||
|
@ -1650,6 +1671,8 @@ _copyQuery(Query *from)
|
|||
newnode->hasSubLinks = from->hasSubLinks;
|
||||
|
||||
Node_Copy(from, newnode, rtable);
|
||||
Node_Copy(from, newnode, jointree);
|
||||
|
||||
Node_Copy(from, newnode, targetList);
|
||||
Node_Copy(from, newnode, qual);
|
||||
Node_Copy(from, newnode, rowMark);
|
||||
|
@ -2548,6 +2571,12 @@ copyObject(void *from)
|
|||
case T_RelabelType:
|
||||
retval = _copyRelabelType(from);
|
||||
break;
|
||||
case T_RangeTblRef:
|
||||
retval = _copyRangeTblRef(from);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
retval = _copyJoinExpr(from);
|
||||
break;
|
||||
|
||||
/*
|
||||
* RELATION NODES
|
||||
|
@ -2809,15 +2838,15 @@ copyObject(void *from)
|
|||
case T_TypeCast:
|
||||
retval = _copyTypeCast(from);
|
||||
break;
|
||||
case T_RelExpr:
|
||||
retval = _copyRelExpr(from);
|
||||
break;
|
||||
case T_SortGroupBy:
|
||||
retval = _copySortGroupBy(from);
|
||||
break;
|
||||
case T_RangeVar:
|
||||
retval = _copyRangeVar(from);
|
||||
break;
|
||||
case T_RangeSubselect:
|
||||
retval = _copyRangeSubselect(from);
|
||||
break;
|
||||
case T_TypeName:
|
||||
retval = _copyTypeName(from);
|
||||
break;
|
||||
|
@ -2845,9 +2874,6 @@ copyObject(void *from)
|
|||
case T_GroupClause:
|
||||
retval = _copyGroupClause(from);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
retval = _copyJoinExpr(from);
|
||||
break;
|
||||
case T_CaseExpr:
|
||||
retval = _copyCaseExpr(from);
|
||||
break;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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)
|
|||
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
|
||||
_equalFieldSelect(FieldSelect *a, FieldSelect *b)
|
||||
{
|
||||
|
@ -283,23 +303,37 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
|
|||
}
|
||||
|
||||
static bool
|
||||
_equalArrayRef(ArrayRef *a, ArrayRef *b)
|
||||
_equalRangeTblRef(RangeTblRef *a, RangeTblRef *b)
|
||||
{
|
||||
if (a->refelemtype != b->refelemtype)
|
||||
if (a->rtindex != b->rtindex)
|
||||
return false;
|
||||
if (a->refattrlength != b->refattrlength)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJoinExpr(JoinExpr *a, JoinExpr *b)
|
||||
{
|
||||
if (a->jointype != b->jointype)
|
||||
return false;
|
||||
if (a->refelemlength != b->refelemlength)
|
||||
if (a->isNatural != b->isNatural)
|
||||
return false;
|
||||
if (a->refelembyval != b->refelembyval)
|
||||
if (!equal(a->larg, b->larg))
|
||||
return false;
|
||||
if (!equal(a->refupperindexpr, b->refupperindexpr))
|
||||
if (!equal(a->rarg, b->rarg))
|
||||
return false;
|
||||
if (!equal(a->reflowerindexpr, b->reflowerindexpr))
|
||||
if (!equal(a->using, b->using))
|
||||
return false;
|
||||
if (!equal(a->refexpr, b->refexpr))
|
||||
if (!equal(a->quals, b->quals))
|
||||
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)
|
|||
return false;
|
||||
if (!equali(a->joinrelids, b->joinrelids))
|
||||
return false;
|
||||
if (a->alljoinquals != b->alljoinquals)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Skip 'rows' because of possibility of floating-point roundoff
|
||||
|
@ -395,6 +431,8 @@ _equalJoinPath(JoinPath *a, JoinPath *b)
|
|||
{
|
||||
if (!_equalPath((Path *) a, (Path *) b))
|
||||
return false;
|
||||
if (a->jointype != b->jointype)
|
||||
return false;
|
||||
if (!equal(a->outerjoinpath, b->outerjoinpath))
|
||||
return false;
|
||||
if (!equal(a->innerjoinpath, b->innerjoinpath))
|
||||
|
@ -457,6 +495,8 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
|
|||
{
|
||||
if (!equal(a->clause, b->clause))
|
||||
return false;
|
||||
if (a->isjoinqual != b->isjoinqual)
|
||||
return false;
|
||||
if (!equal(a->subclauseindices, b->subclauseindices))
|
||||
return false;
|
||||
if (a->mergejoinoperator != b->mergejoinoperator)
|
||||
|
@ -557,6 +597,8 @@ _equalQuery(Query *a, Query *b)
|
|||
return false;
|
||||
if (!equal(a->rtable, b->rtable))
|
||||
return false;
|
||||
if (!equal(a->jointree, b->jointree))
|
||||
return false;
|
||||
if (!equal(a->targetList, b->targetList))
|
||||
return false;
|
||||
if (!equal(a->qual, b->qual))
|
||||
|
@ -1475,17 +1517,6 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalRelExpr(RelExpr *a, RelExpr *b)
|
||||
{
|
||||
if (!equalstr(a->relname, b->relname))
|
||||
return false;
|
||||
if (a->inh != b->inh)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
|
||||
{
|
||||
|
@ -1500,7 +1531,20 @@ _equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
|
|||
static bool
|
||||
_equalRangeVar(RangeVar *a, RangeVar *b)
|
||||
{
|
||||
if (!equal(a->relExpr, b->relExpr))
|
||||
if (!equalstr(a->relname, b->relname))
|
||||
return false;
|
||||
if (a->inh != b->inh)
|
||||
return false;
|
||||
if (!equal(a->name, b->name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalRangeSubselect(RangeSubselect *a, RangeSubselect *b)
|
||||
{
|
||||
if (!equal(a->subquery, b->subquery))
|
||||
return false;
|
||||
if (!equal(a->name, b->name))
|
||||
return false;
|
||||
|
@ -1605,17 +1649,16 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
|
|||
{
|
||||
if (!equalstr(a->relname, b->relname))
|
||||
return false;
|
||||
if (!equal(a->ref, b->ref))
|
||||
return false;
|
||||
/* XXX what about eref? */
|
||||
if (a->relid != b->relid)
|
||||
return false;
|
||||
if (!equal(a->alias, b->alias))
|
||||
return false;
|
||||
if (!equal(a->eref, b->eref))
|
||||
return false;
|
||||
if (a->inh != b->inh)
|
||||
return false;
|
||||
if (a->inFromCl != b->inFromCl)
|
||||
return false;
|
||||
if (a->inJoinSet != b->inJoinSet)
|
||||
return false;
|
||||
if (a->skipAcl != b->skipAcl)
|
||||
return false;
|
||||
|
||||
|
@ -1644,25 +1687,6 @@ _equalRowMark(RowMark *a, RowMark *b)
|
|||
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
|
||||
_equalFkConstraint(FkConstraint *a, FkConstraint *b)
|
||||
{
|
||||
|
@ -1808,6 +1832,12 @@ equal(void *a, void *b)
|
|||
case T_RelabelType:
|
||||
retval = _equalRelabelType(a, b);
|
||||
break;
|
||||
case T_RangeTblRef:
|
||||
retval = _equalRangeTblRef(a, b);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
retval = _equalJoinExpr(a, b);
|
||||
break;
|
||||
|
||||
case T_RelOptInfo:
|
||||
retval = _equalRelOptInfo(a, b);
|
||||
|
@ -2067,15 +2097,15 @@ equal(void *a, void *b)
|
|||
case T_TypeCast:
|
||||
retval = _equalTypeCast(a, b);
|
||||
break;
|
||||
case T_RelExpr:
|
||||
retval = _equalRelExpr(a, b);
|
||||
break;
|
||||
case T_SortGroupBy:
|
||||
retval = _equalSortGroupBy(a, b);
|
||||
break;
|
||||
case T_RangeVar:
|
||||
retval = _equalRangeVar(a, b);
|
||||
break;
|
||||
case T_RangeSubselect:
|
||||
retval = _equalRangeSubselect(a, b);
|
||||
break;
|
||||
case T_TypeName:
|
||||
retval = _equalTypeName(a, b);
|
||||
break;
|
||||
|
@ -2104,9 +2134,6 @@ equal(void *a, void *b)
|
|||
/* GroupClause is equivalent to SortClause */
|
||||
retval = _equalSortClause(a, b);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
retval = _equalJoinExpr(a, b);
|
||||
break;
|
||||
case T_CaseExpr:
|
||||
retval = _equalCaseExpr(a, b);
|
||||
break;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
||||
* XXX a few of the following functions are duplicated to handle
|
||||
|
@ -351,6 +351,25 @@ member(void *l1, List *l2)
|
|||
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
|
||||
intMember(int l1, List *l2)
|
||||
{
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* 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
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
|
@ -268,6 +268,9 @@ _outQuery(StringInfo str, Query *node)
|
|||
appendStringInfo(str, " :rtable ");
|
||||
_outNode(str, node->rtable);
|
||||
|
||||
appendStringInfo(str, " :jointree ");
|
||||
_outNode(str, node->jointree);
|
||||
|
||||
appendStringInfo(str, " :targetlist ");
|
||||
_outNode(str, node->targetList);
|
||||
|
||||
|
@ -389,7 +392,6 @@ _outAppend(StringInfo str, Append *node)
|
|||
" :inheritrelid %u :inheritrtable ",
|
||||
node->inheritrelid);
|
||||
_outNode(str, node->inheritrtable);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -400,7 +402,9 @@ _outJoin(StringInfo str, Join *node)
|
|||
{
|
||||
appendStringInfo(str, " JOIN ");
|
||||
_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)
|
|||
{
|
||||
appendStringInfo(str, " NESTLOOP ");
|
||||
_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)
|
|||
{
|
||||
appendStringInfo(str, " MERGEJOIN ");
|
||||
_outPlanInfo(str, (Plan *) node);
|
||||
appendStringInfo(str, " :jointype %d :joinqual ",
|
||||
(int) node->join.jointype);
|
||||
_outNode(str, node->join.joinqual);
|
||||
|
||||
appendStringInfo(str, " :mergeclauses ");
|
||||
_outNode(str, node->mergeclauses);
|
||||
|
@ -434,17 +444,14 @@ _outHashJoin(StringInfo str, HashJoin *node)
|
|||
{
|
||||
appendStringInfo(str, " HASHJOIN ");
|
||||
_outPlanInfo(str, (Plan *) node);
|
||||
appendStringInfo(str, " :jointype %d :joinqual ",
|
||||
(int) node->join.jointype);
|
||||
_outNode(str, node->join.joinqual);
|
||||
|
||||
appendStringInfo(str, " :hashclauses ");
|
||||
_outNode(str, node->hashclauses);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :hashjoinop %u ",
|
||||
appendStringInfo(str, " :hashjoinop %u ",
|
||||
node->hashjoinop);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :hashdone %d ",
|
||||
node->hashdone);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -757,32 +764,6 @@ _outSubLink(StringInfo str, SubLink *node)
|
|||
_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
|
||||
*/
|
||||
|
@ -846,6 +827,66 @@ _outParam(StringInfo str, Param *node)
|
|||
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
|
||||
*/
|
||||
|
@ -897,6 +938,11 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
|
|||
node->pruneable ? "true" : "false");
|
||||
_outNode(str, node->baserestrictinfo);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :baserestrictcost %.2f :outerjoinset ",
|
||||
node->baserestrictcost);
|
||||
_outIntList(str, node->outerjoinset);
|
||||
|
||||
appendStringInfo(str, " :joininfo ");
|
||||
_outNode(str, node->joininfo);
|
||||
|
||||
|
@ -931,14 +977,14 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
|
|||
{
|
||||
appendStringInfo(str, " RTE :relname ");
|
||||
_outToken(str, node->relname);
|
||||
appendStringInfo(str, " :ref ");
|
||||
_outNode(str, node->ref);
|
||||
appendStringInfo(str,
|
||||
" :relid %u :inh %s :inFromCl %s :inJoinSet %s :skipAcl %s",
|
||||
node->relid,
|
||||
appendStringInfo(str, " :relid %u :alias ",
|
||||
node->relid);
|
||||
_outNode(str, node->alias);
|
||||
appendStringInfo(str, " :eref ");
|
||||
_outNode(str, node->eref);
|
||||
appendStringInfo(str, " :inh %s :inFromCl %s :skipAcl %s",
|
||||
node->inh ? "true" : "false",
|
||||
node->inFromCl ? "true" : "false",
|
||||
node->inJoinSet ? "true" : "false",
|
||||
node->skipAcl ? "true" : "false");
|
||||
}
|
||||
|
||||
|
@ -985,7 +1031,8 @@ _outIndexPath(StringInfo str, IndexPath *node)
|
|||
(int) node->indexscandir);
|
||||
_outIntList(str, node->joinrelids);
|
||||
|
||||
appendStringInfo(str, " :rows %.2f ",
|
||||
appendStringInfo(str, " :alljoinquals %s :rows %.2f ",
|
||||
node->alljoinquals ? "true" : "false",
|
||||
node->rows);
|
||||
}
|
||||
|
||||
|
@ -1021,7 +1068,8 @@ _outNestPath(StringInfo str, NestPath *node)
|
|||
node->path.startup_cost,
|
||||
node->path.total_cost);
|
||||
_outNode(str, node->path.pathkeys);
|
||||
appendStringInfo(str, " :outerjoinpath ");
|
||||
appendStringInfo(str, " :jointype %d :outerjoinpath ",
|
||||
(int) node->jointype);
|
||||
_outNode(str, node->outerjoinpath);
|
||||
appendStringInfo(str, " :innerjoinpath ");
|
||||
_outNode(str, node->innerjoinpath);
|
||||
|
@ -1041,7 +1089,8 @@ _outMergePath(StringInfo str, MergePath *node)
|
|||
node->jpath.path.startup_cost,
|
||||
node->jpath.path.total_cost);
|
||||
_outNode(str, node->jpath.path.pathkeys);
|
||||
appendStringInfo(str, " :outerjoinpath ");
|
||||
appendStringInfo(str, " :jointype %d :outerjoinpath ",
|
||||
(int) node->jpath.jointype);
|
||||
_outNode(str, node->jpath.outerjoinpath);
|
||||
appendStringInfo(str, " :innerjoinpath ");
|
||||
_outNode(str, node->jpath.innerjoinpath);
|
||||
|
@ -1070,7 +1119,8 @@ _outHashPath(StringInfo str, HashPath *node)
|
|||
node->jpath.path.startup_cost,
|
||||
node->jpath.path.total_cost);
|
||||
_outNode(str, node->jpath.path.pathkeys);
|
||||
appendStringInfo(str, " :outerjoinpath ");
|
||||
appendStringInfo(str, " :jointype %d :outerjoinpath ",
|
||||
(int) node->jpath.jointype);
|
||||
_outNode(str, node->jpath.outerjoinpath);
|
||||
appendStringInfo(str, " :innerjoinpath ");
|
||||
_outNode(str, node->jpath.innerjoinpath);
|
||||
|
@ -1101,7 +1151,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
|
|||
appendStringInfo(str, " RESTRICTINFO :clause ");
|
||||
_outNode(str, node->clause);
|
||||
|
||||
appendStringInfo(str, " :subclauseindices ");
|
||||
appendStringInfo(str, " :isjoinqual %s :subclauseindices ",
|
||||
node->isjoinqual ? "true" : "false");
|
||||
_outNode(str, node->subclauseindices);
|
||||
|
||||
appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator);
|
||||
|
@ -1483,12 +1534,6 @@ _outNode(StringInfo str, void *obj)
|
|||
case T_SubLink:
|
||||
_outSubLink(str, obj);
|
||||
break;
|
||||
case T_FieldSelect:
|
||||
_outFieldSelect(str, obj);
|
||||
break;
|
||||
case T_RelabelType:
|
||||
_outRelabelType(str, obj);
|
||||
break;
|
||||
case T_ArrayRef:
|
||||
_outArrayRef(str, obj);
|
||||
break;
|
||||
|
@ -1501,6 +1546,18 @@ _outNode(StringInfo str, void *obj)
|
|||
case T_Param:
|
||||
_outParam(str, obj);
|
||||
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:
|
||||
_outEState(str, obj);
|
||||
break;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
|
@ -133,7 +133,7 @@ print_rt(List *rtable)
|
|||
RangeTblEntry *rte = lfirst(l);
|
||||
|
||||
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->inh ? "inh" : ""));
|
||||
i++;
|
||||
|
@ -157,7 +157,6 @@ print_expr(Node *expr, List *rtable)
|
|||
if (IsA(expr, Var))
|
||||
{
|
||||
Var *var = (Var *) expr;
|
||||
RangeTblEntry *rt;
|
||||
char *relname,
|
||||
*attname;
|
||||
|
||||
|
@ -173,10 +172,10 @@ print_expr(Node *expr, List *rtable)
|
|||
break;
|
||||
default:
|
||||
{
|
||||
RangeTblEntry *rt;
|
||||
|
||||
rt = rt_fetch(var->varno, rtable);
|
||||
relname = rt->relname;
|
||||
if (rt->ref && rt->ref->relname)
|
||||
relname = rt->ref->relname; /* table renamed */
|
||||
relname = rt->eref->relname;
|
||||
attname = get_attname(rt->relid, var->varattno);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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
|
||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||
|
@ -119,6 +119,9 @@ _readQuery()
|
|||
token = lsptok(NULL, &length); /* skip :rtable */
|
||||
local_node->rtable = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :jointree */
|
||||
local_node->jointree = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :targetlist */
|
||||
local_node->targetList = nodeRead(true);
|
||||
|
||||
|
@ -335,14 +338,22 @@ _readAppend()
|
|||
|
||||
/* ----------------
|
||||
* _getJoin
|
||||
*
|
||||
* In case Join is not the same structure as Plan someday.
|
||||
* ----------------
|
||||
*/
|
||||
static void
|
||||
_getJoin(Join *node)
|
||||
{
|
||||
char *token;
|
||||
int length;
|
||||
|
||||
_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()
|
|||
local_node = makeNode(MergeJoin);
|
||||
|
||||
_getJoin((Join *) local_node);
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :mergeclauses */
|
||||
local_node->mergeclauses = nodeRead(true); /* now read it */
|
||||
|
||||
|
@ -429,19 +441,13 @@ _readHashJoin()
|
|||
token = lsptok(NULL, &length); /* get hashjoinop */
|
||||
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;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _getScan
|
||||
*
|
||||
* Scan is a subclass of Node
|
||||
* (Actually, according to the plannodes.h include file, it is a
|
||||
* subclass of Plan. This is why _getPlan is used here.)
|
||||
* Scan is a subclass of Plan.
|
||||
*
|
||||
* Scan gets its own get function since stuff inherits it.
|
||||
* ----------------
|
||||
|
@ -462,7 +468,7 @@ _getScan(Scan *node)
|
|||
/* ----------------
|
||||
* _readScan
|
||||
*
|
||||
* Scan is a subclass of Plan (Not Node, see above).
|
||||
* Scan is a subclass of Plan.
|
||||
* ----------------
|
||||
*/
|
||||
static Scan *
|
||||
|
@ -1154,6 +1160,74 @@ _readRelabelType()
|
|||
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
|
||||
*/
|
||||
|
@ -1252,7 +1326,14 @@ _readRelOptInfo()
|
|||
local_node->pruneable = (token[0] == 't') ? true : false;
|
||||
|
||||
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 */
|
||||
local_node->joininfo = nodeRead(true); /* now read it */
|
||||
|
@ -1324,13 +1405,16 @@ _readRangeTblEntry()
|
|||
else
|
||||
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); /* get :relid */
|
||||
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); /* get :inh */
|
||||
local_node->inh = (token[0] == 't') ? true : false;
|
||||
|
@ -1339,10 +1423,6 @@ _readRangeTblEntry()
|
|||
token = lsptok(NULL, &length); /* get :inFromCl */
|
||||
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); /* get :skipAcl */
|
||||
local_node->skipAcl = (token[0] == 't') ? true : false;
|
||||
|
@ -1444,6 +1524,10 @@ _readIndexPath()
|
|||
token = lsptok(NULL, &length); /* get :joinrelids */
|
||||
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); /* now read it */
|
||||
local_node->rows = atof(token);
|
||||
|
@ -1520,6 +1604,10 @@ _readNestPath()
|
|||
token = lsptok(NULL, &length); /* get :pathkeys */
|
||||
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 */
|
||||
local_node->outerjoinpath = nodeRead(true); /* now read it */
|
||||
|
||||
|
@ -1527,7 +1615,7 @@ _readNestPath()
|
|||
local_node->innerjoinpath = nodeRead(true); /* now read it */
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1562,6 +1650,10 @@ _readMergePath()
|
|||
token = lsptok(NULL, &length); /* get :pathkeys */
|
||||
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 */
|
||||
local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */
|
||||
|
||||
|
@ -1569,7 +1661,7 @@ _readMergePath()
|
|||
local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */
|
||||
|
||||
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 */
|
||||
local_node->path_mergeclauses = nodeRead(true); /* now read it */
|
||||
|
@ -1613,6 +1705,10 @@ _readHashPath()
|
|||
token = lsptok(NULL, &length); /* get :pathkeys */
|
||||
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 */
|
||||
local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */
|
||||
|
||||
|
@ -1620,7 +1716,7 @@ _readHashPath()
|
|||
local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */
|
||||
|
||||
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 */
|
||||
local_node->path_hashclauses = nodeRead(true); /* now read it */
|
||||
|
@ -1672,6 +1768,10 @@ _readRestrictInfo()
|
|||
token = lsptok(NULL, &length); /* get :clause */
|
||||
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 */
|
||||
local_node->subclauseindices = nodeRead(true); /* now read it */
|
||||
|
||||
|
@ -1789,6 +1889,10 @@ parsePlanString(void)
|
|||
return_value = _readFieldSelect();
|
||||
else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0)
|
||||
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)
|
||||
return_value = _readAgg();
|
||||
else if (length == 4 && strncmp(token, "HASH", length) == 0)
|
||||
|
|
|
@ -35,10 +35,10 @@ RelOptInfo.pathlist. (Actually, we discard Paths that are obviously
|
|||
inferior alternatives before they ever get into the pathlist --- what
|
||||
ends up in the pathlist is the cheapest way of generating each potentially
|
||||
useful sort ordering of the relation.) Also create RelOptInfo.joininfo
|
||||
nodes that list all the joins that involve this relation. For example,
|
||||
the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo for tab1
|
||||
listing tab2 as an unjoined relation, and also one for tab2 showing tab1
|
||||
as an unjoined relation.
|
||||
nodes that list all the join clauses that involve this relation. For
|
||||
example, the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo
|
||||
for tab1 listing tab2 as an unjoined relation, and also one for tab2
|
||||
showing tab1 as an unjoined relation.
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
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
|
||||
-------------------
|
||||
|
@ -158,13 +171,12 @@ planner()
|
|||
get a target list that only contains column names, no expressions
|
||||
if none, then return
|
||||
---subplanner()
|
||||
make list of relations in target
|
||||
make list of relations in where clause
|
||||
make list of base relations used in query
|
||||
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()
|
||||
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
|
||||
-----make_one_rel_by_joins()
|
||||
jump to geqo if needed
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* 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)
|
|||
* Returns a new join relation incorporating all joins in a left-sided tree.
|
||||
*/
|
||||
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 */
|
||||
int base_rel_index;
|
||||
RelOptInfo *new_rel;
|
||||
|
||||
if (rel_count < num_gene)
|
||||
{ /* tree not yet finished */
|
||||
|
@ -116,16 +116,22 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
|
|||
else
|
||||
{ /* tree main part */
|
||||
List *acceptable_rels = lcons(inner_rel, NIL);
|
||||
List *new_rels;
|
||||
RelOptInfo *new_rel;
|
||||
|
||||
new_rel = make_rels_by_clause_joins(root, old_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,
|
||||
acceptable_rels);
|
||||
if (!new_rel)
|
||||
new_rels = make_rels_by_clauseless_joins(root, old_rel,
|
||||
acceptable_rels);
|
||||
Assert(length(new_rels) <= 1);
|
||||
if (new_rels == NIL)
|
||||
elog(ERROR, "gimme_tree: failed to construct join rel");
|
||||
}
|
||||
new_rel = (RelOptInfo *) lfirst(new_rels);
|
||||
|
||||
rel_count++;
|
||||
Assert(length(new_rel->relids) == rel_count);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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;
|
|||
|
||||
|
||||
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
|
||||
static void debug_print_rel(Query *root, RelOptInfo *rel);
|
||||
|
@ -43,27 +45,38 @@ RelOptInfo *
|
|||
make_one_rel(Query *root)
|
||||
{
|
||||
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)
|
||||
return NULL;
|
||||
return NULL; /* nothing to do? */
|
||||
|
||||
/*
|
||||
* Generate access paths for the base rels.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
|
@ -71,7 +84,7 @@ make_one_rel(Query *root)
|
|||
/*
|
||||
* 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)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* Find all possible joinpaths for a query by successively finding ways
|
||||
* to join component relations into join relations.
|
||||
*
|
||||
* '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
|
||||
* the result of joining all the original relations together.
|
||||
*/
|
||||
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;
|
||||
RelOptInfo *rel;
|
||||
|
||||
|
@ -152,34 +192,35 @@ make_one_rel_by_joins(Query *root, int levels_needed)
|
|||
|
||||
/*
|
||||
* We employ a simple "dynamic programming" algorithm: we first find
|
||||
* all ways to build joins of two base relations, then all ways to
|
||||
* build joins of three base relations (from two-base-rel joins and
|
||||
* other base rels), then four-base-rel joins, and so on until we have
|
||||
* considered all ways to join all N relations into one rel.
|
||||
* all ways to build joins of two jointree items, then all ways to
|
||||
* build joins of three items (from two-item joins and single items),
|
||||
* then four-item joins, and so on until we have considered all ways
|
||||
* 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++)
|
||||
{
|
||||
List *first_old_rel = root->join_rel_list;
|
||||
List *x;
|
||||
|
||||
/*
|
||||
* Determine all possible pairs of relations to be joined at this
|
||||
* level, and build paths for making each one from every available
|
||||
* pair of lower-level relations. Results are prepended to
|
||||
* root->join_rel_list.
|
||||
* pair of lower-level relations.
|
||||
*/
|
||||
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
|
||||
* front of root->join_rel_list.
|
||||
* Do cleanup work on each just-processed rel.
|
||||
*/
|
||||
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);
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
@ -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.
|
||||
*/
|
||||
Assert(length(root->join_rel_list) > 0);
|
||||
rel = (RelOptInfo *) lfirst(root->join_rel_list);
|
||||
Assert(length(rel->relids) == levels_needed);
|
||||
Assert(length(root->join_rel_list) == 1 ||
|
||||
length(((RelOptInfo *) lsecond(root->join_rel_list))->relids) < levels_needed);
|
||||
Assert(length(joinitems[levels_needed]) == 1);
|
||||
rel = (RelOptInfo *) lfirst(joinitems[levels_needed]);
|
||||
Assert(length(rel->relids) == length(root->base_rel_list));
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
*
|
||||
* 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,
|
|||
{
|
||||
List *clausegroup = lfirst(i);
|
||||
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? */
|
||||
|
||||
|
@ -1496,7 +1498,16 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
|||
*/
|
||||
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 */
|
||||
indexquals = expand_indexqual_conditions(indexquals);
|
||||
|
||||
|
@ -1514,6 +1525,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
|||
/* joinrelids saves the rels needed on the outer side of the join */
|
||||
pathnode->joinrelids = lfirst(outerrelids_list);
|
||||
|
||||
pathnode->alljoinquals = alljoinquals;
|
||||
|
||||
/*
|
||||
* We must compute the estimated number of output rows for the
|
||||
* indexscan. This is less than rel->rows because of the
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.55 2000/05/30 00:49:47 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.56 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -25,27 +25,32 @@
|
|||
#include "utils/lsyscache.h"
|
||||
|
||||
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list,
|
||||
JoinType jointype);
|
||||
static void match_unsorted_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list,
|
||||
JoinType jointype);
|
||||
|
||||
#ifdef NOT_USED
|
||||
static void match_unsorted_inner(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, List *mergeclause_list,
|
||||
JoinType jointype);
|
||||
|
||||
#endif
|
||||
static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
static Path *best_innerjoin(List *join_paths, List *outer_relid);
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, JoinType jointype);
|
||||
static Path *best_innerjoin(List *join_paths, List *outer_relid,
|
||||
JoinType jointype);
|
||||
static Selectivity estimate_disbursion(Query *root, Var *var);
|
||||
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist,
|
||||
JoinType jointype);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -64,6 +69,7 @@ add_paths_to_joinrel(Query *root,
|
|||
RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
JoinType jointype,
|
||||
List *restrictlist)
|
||||
{
|
||||
List *mergeclause_list = NIL;
|
||||
|
@ -75,14 +81,15 @@ add_paths_to_joinrel(Query *root,
|
|||
mergeclause_list = select_mergejoin_clauses(joinrel,
|
||||
outerrel,
|
||||
innerrel,
|
||||
restrictlist);
|
||||
restrictlist,
|
||||
jointype);
|
||||
|
||||
/*
|
||||
* 1. Consider mergejoin paths where both relations must be explicitly
|
||||
* sorted.
|
||||
*/
|
||||
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, mergeclause_list);
|
||||
restrictlist, mergeclause_list, jointype);
|
||||
|
||||
/*
|
||||
* 2. Consider paths where the outer relation need not be explicitly
|
||||
|
@ -90,7 +97,7 @@ add_paths_to_joinrel(Query *root,
|
|||
* path is already ordered.
|
||||
*/
|
||||
match_unsorted_outer(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, mergeclause_list);
|
||||
restrictlist, mergeclause_list, jointype);
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
|
@ -107,7 +114,7 @@ add_paths_to_joinrel(Query *root,
|
|||
* other order.
|
||||
*/
|
||||
match_unsorted_inner(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, mergeclause_list);
|
||||
restrictlist, mergeclause_list, jointype);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -116,7 +123,7 @@ add_paths_to_joinrel(Query *root,
|
|||
*/
|
||||
if (enable_hashjoin)
|
||||
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
|
||||
restrictlist);
|
||||
restrictlist, jointype);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -131,6 +138,7 @@ add_paths_to_joinrel(Query *root,
|
|||
* clauses that apply to this join
|
||||
* 'mergeclause_list' is a list of RestrictInfo nodes for available
|
||||
* mergejoin clauses in this join
|
||||
* 'jointype' is the type of join to do
|
||||
*/
|
||||
static void
|
||||
sort_inner_and_outer(Query *root,
|
||||
|
@ -138,7 +146,8 @@ sort_inner_and_outer(Query *root,
|
|||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist,
|
||||
List *mergeclause_list)
|
||||
List *mergeclause_list,
|
||||
JoinType jointype)
|
||||
{
|
||||
List *i;
|
||||
|
||||
|
@ -187,10 +196,10 @@ sort_inner_and_outer(Query *root,
|
|||
*/
|
||||
outerkeys = make_pathkeys_for_mergeclauses(root,
|
||||
curclause_list,
|
||||
outerrel->targetlist);
|
||||
outerrel);
|
||||
innerkeys = make_pathkeys_for_mergeclauses(root,
|
||||
curclause_list,
|
||||
innerrel->targetlist);
|
||||
innerrel);
|
||||
/* Build pathkeys representing output sort order. */
|
||||
merge_pathkeys = build_join_pathkeys(outerkeys,
|
||||
joinrel->targetlist,
|
||||
|
@ -204,6 +213,7 @@ sort_inner_and_outer(Query *root,
|
|||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
outerrel->cheapest_total_path,
|
||||
innerrel->cheapest_total_path,
|
||||
restrictlist,
|
||||
|
@ -243,6 +253,7 @@ sort_inner_and_outer(Query *root,
|
|||
* clauses that apply to this join
|
||||
* 'mergeclause_list' is a list of RestrictInfo nodes for available
|
||||
* mergejoin clauses in this join
|
||||
* 'jointype' is the type of join to do
|
||||
*/
|
||||
static void
|
||||
match_unsorted_outer(Query *root,
|
||||
|
@ -250,16 +261,33 @@ match_unsorted_outer(Query *root,
|
|||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist,
|
||||
List *mergeclause_list)
|
||||
List *mergeclause_list,
|
||||
JoinType jointype)
|
||||
{
|
||||
bool nestjoinOK;
|
||||
Path *bestinnerjoin;
|
||||
List *i;
|
||||
|
||||
/*
|
||||
* Nestloop only supports inner and left joins.
|
||||
*/
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
case JOIN_LEFT:
|
||||
nestjoinOK = true;
|
||||
break;
|
||||
default:
|
||||
nestjoinOK = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the best innerjoin indexpath (if any) for this outer rel. It's
|
||||
* the same for all outer paths.
|
||||
*/
|
||||
bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids);
|
||||
bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids,
|
||||
jointype);
|
||||
|
||||
foreach(i, outerrel->pathlist)
|
||||
{
|
||||
|
@ -282,31 +310,38 @@ match_unsorted_outer(Query *root,
|
|||
joinrel->targetlist,
|
||||
root->equi_key_list);
|
||||
|
||||
/*
|
||||
* Always consider a nestloop join with this outer and cheapest-
|
||||
* total-cost inner. Consider nestloops using the cheapest-
|
||||
* startup-cost inner as well, and the best innerjoin indexpath.
|
||||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_nestloop_path(joinrel,
|
||||
outerpath,
|
||||
innerrel->cheapest_total_path,
|
||||
restrictlist,
|
||||
merge_pathkeys));
|
||||
if (innerrel->cheapest_startup_path != innerrel->cheapest_total_path)
|
||||
if (nestjoinOK)
|
||||
{
|
||||
/*
|
||||
* Always consider a nestloop join with this outer and cheapest-
|
||||
* total-cost inner. Consider nestloops using the cheapest-
|
||||
* startup-cost inner as well, and the best innerjoin indexpath.
|
||||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_nestloop_path(joinrel,
|
||||
jointype,
|
||||
outerpath,
|
||||
innerrel->cheapest_startup_path,
|
||||
restrictlist,
|
||||
merge_pathkeys));
|
||||
if (bestinnerjoin != NULL)
|
||||
add_path(joinrel, (Path *)
|
||||
create_nestloop_path(joinrel,
|
||||
outerpath,
|
||||
bestinnerjoin,
|
||||
innerrel->cheapest_total_path,
|
||||
restrictlist,
|
||||
merge_pathkeys));
|
||||
if (innerrel->cheapest_startup_path !=
|
||||
innerrel->cheapest_total_path)
|
||||
add_path(joinrel, (Path *)
|
||||
create_nestloop_path(joinrel,
|
||||
jointype,
|
||||
outerpath,
|
||||
innerrel->cheapest_startup_path,
|
||||
restrictlist,
|
||||
merge_pathkeys));
|
||||
if (bestinnerjoin != NULL)
|
||||
add_path(joinrel, (Path *)
|
||||
create_nestloop_path(joinrel,
|
||||
jointype,
|
||||
outerpath,
|
||||
bestinnerjoin,
|
||||
restrictlist,
|
||||
merge_pathkeys));
|
||||
}
|
||||
|
||||
/* Look for useful mergeclauses (if any) */
|
||||
mergeclauses = find_mergeclauses_for_pathkeys(outerpath->pathkeys,
|
||||
|
@ -319,7 +354,7 @@ match_unsorted_outer(Query *root,
|
|||
/* Compute the required ordering of the inner path */
|
||||
innersortkeys = make_pathkeys_for_mergeclauses(root,
|
||||
mergeclauses,
|
||||
innerrel->targetlist);
|
||||
innerrel);
|
||||
|
||||
/*
|
||||
* Generate a mergejoin on the basis of sorting the cheapest
|
||||
|
@ -328,6 +363,7 @@ match_unsorted_outer(Query *root,
|
|||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
outerpath,
|
||||
innerrel->cheapest_total_path,
|
||||
restrictlist,
|
||||
|
@ -373,6 +409,7 @@ match_unsorted_outer(Query *root,
|
|||
newclauses = mergeclauses;
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
outerpath,
|
||||
innerpath,
|
||||
restrictlist,
|
||||
|
@ -409,6 +446,7 @@ match_unsorted_outer(Query *root,
|
|||
}
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
outerpath,
|
||||
innerpath,
|
||||
restrictlist,
|
||||
|
@ -437,6 +475,7 @@ match_unsorted_outer(Query *root,
|
|||
* clauses that apply to this join
|
||||
* 'mergeclause_list' is a list of RestrictInfo nodes for available
|
||||
* mergejoin clauses in this join
|
||||
* 'jointype' is the type of join to do
|
||||
*/
|
||||
static void
|
||||
match_unsorted_inner(Query *root,
|
||||
|
@ -444,7 +483,8 @@ match_unsorted_inner(Query *root,
|
|||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist,
|
||||
List *mergeclause_list)
|
||||
List *mergeclause_list,
|
||||
JoinType jointype)
|
||||
{
|
||||
List *i;
|
||||
|
||||
|
@ -466,7 +506,7 @@ match_unsorted_inner(Query *root,
|
|||
/* Compute the required ordering of the outer path */
|
||||
outersortkeys = make_pathkeys_for_mergeclauses(root,
|
||||
mergeclauses,
|
||||
outerrel->targetlist);
|
||||
outerrel);
|
||||
|
||||
/*
|
||||
* Generate a mergejoin on the basis of sorting the cheapest
|
||||
|
@ -478,6 +518,7 @@ match_unsorted_inner(Query *root,
|
|||
root->equi_key_list);
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
outerrel->cheapest_total_path,
|
||||
innerpath,
|
||||
restrictlist,
|
||||
|
@ -506,6 +547,7 @@ match_unsorted_inner(Query *root,
|
|||
root->equi_key_list);
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
totalouterpath,
|
||||
innerpath,
|
||||
restrictlist,
|
||||
|
@ -524,6 +566,7 @@ match_unsorted_inner(Query *root,
|
|||
root->equi_key_list);
|
||||
add_path(joinrel, (Path *)
|
||||
create_mergejoin_path(joinrel,
|
||||
jointype,
|
||||
startupouterpath,
|
||||
innerpath,
|
||||
restrictlist,
|
||||
|
@ -547,18 +590,36 @@ match_unsorted_inner(Query *root,
|
|||
* 'innerrel' is the inner join relation
|
||||
* 'restrictlist' contains all of the RestrictInfo nodes for restriction
|
||||
* clauses that apply to this join
|
||||
* 'jointype' is the type of join to do
|
||||
*/
|
||||
static void
|
||||
hash_inner_and_outer(Query *root,
|
||||
RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist)
|
||||
List *restrictlist,
|
||||
JoinType jointype)
|
||||
{
|
||||
Relids outerrelids = outerrel->relids;
|
||||
Relids innerrelids = innerrel->relids;
|
||||
bool isouterjoin;
|
||||
List *i;
|
||||
|
||||
/*
|
||||
* Hashjoin only supports inner and left joins.
|
||||
*/
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
isouterjoin = false;
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
isouterjoin = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the join's restrictinfo list to find hashjoinable clauses that
|
||||
* are usable with this pair of sub-relations. Since we currently
|
||||
|
@ -581,6 +642,13 @@ hash_inner_and_outer(Query *root,
|
|||
if (restrictinfo->hashjoinoperator == InvalidOid)
|
||||
continue; /* not hashjoinable */
|
||||
|
||||
/*
|
||||
* If processing an outer join, only use explicit join clauses for
|
||||
* hashing. For inner joins we need not be so picky.
|
||||
*/
|
||||
if (isouterjoin && !restrictinfo->isjoinqual)
|
||||
continue;
|
||||
|
||||
clause = restrictinfo->clause;
|
||||
/* these must be OK, since check_hashjoinable accepted the clause */
|
||||
left = get_leftop(clause);
|
||||
|
@ -609,6 +677,7 @@ hash_inner_and_outer(Query *root,
|
|||
*/
|
||||
add_path(joinrel, (Path *)
|
||||
create_hashjoin_path(joinrel,
|
||||
jointype,
|
||||
outerrel->cheapest_total_path,
|
||||
innerrel->cheapest_total_path,
|
||||
restrictlist,
|
||||
|
@ -617,6 +686,7 @@ hash_inner_and_outer(Query *root,
|
|||
if (outerrel->cheapest_startup_path != outerrel->cheapest_total_path)
|
||||
add_path(joinrel, (Path *)
|
||||
create_hashjoin_path(joinrel,
|
||||
jointype,
|
||||
outerrel->cheapest_startup_path,
|
||||
innerrel->cheapest_total_path,
|
||||
restrictlist,
|
||||
|
@ -641,26 +711,49 @@ hash_inner_and_outer(Query *root,
|
|||
* usable path.
|
||||
*/
|
||||
static Path *
|
||||
best_innerjoin(List *join_paths, Relids outer_relids)
|
||||
best_innerjoin(List *join_paths, Relids outer_relids, JoinType jointype)
|
||||
{
|
||||
Path *cheapest = (Path *) NULL;
|
||||
bool isouterjoin;
|
||||
List *join_path;
|
||||
|
||||
/*
|
||||
* Nestloop only supports inner and left joins.
|
||||
*/
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
isouterjoin = false;
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
isouterjoin = true;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
foreach(join_path, join_paths)
|
||||
{
|
||||
Path *path = (Path *) lfirst(join_path);
|
||||
IndexPath *path = (IndexPath *) lfirst(join_path);
|
||||
|
||||
Assert(IsA(path, IndexPath));
|
||||
|
||||
/*
|
||||
* If processing an outer join, only use explicit join clauses in the
|
||||
* inner indexscan. For inner joins we need not be so picky.
|
||||
*/
|
||||
if (isouterjoin && !path->alljoinquals)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* path->joinrelids is the set of base rels that must be part of
|
||||
* outer_relids in order to use this inner path, because those
|
||||
* rels are used in the index join quals of this inner path.
|
||||
*/
|
||||
if (is_subseti(((IndexPath *) path)->joinrelids, outer_relids) &&
|
||||
if (is_subseti(path->joinrelids, outer_relids) &&
|
||||
(cheapest == NULL ||
|
||||
compare_path_costs(path, cheapest, TOTAL_COST) < 0))
|
||||
cheapest = path;
|
||||
compare_path_costs((Path *) path, cheapest, TOTAL_COST) < 0))
|
||||
cheapest = (Path *) path;
|
||||
}
|
||||
return cheapest;
|
||||
}
|
||||
|
@ -684,6 +777,9 @@ estimate_disbursion(Query *root, Var *var)
|
|||
|
||||
relid = getrelid(var->varno, root->rtable);
|
||||
|
||||
if (relid == InvalidOid)
|
||||
return 0.1;
|
||||
|
||||
return (Selectivity) get_attdisbursion(relid, var->varattno, 0.1);
|
||||
}
|
||||
|
||||
|
@ -707,11 +803,13 @@ static List *
|
|||
select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist)
|
||||
List *restrictlist,
|
||||
JoinType jointype)
|
||||
{
|
||||
List *result_list = NIL;
|
||||
Relids outerrelids = outerrel->relids;
|
||||
Relids innerrelids = innerrel->relids;
|
||||
bool isouterjoin = IS_OUTER_JOIN(jointype);
|
||||
List *i;
|
||||
|
||||
foreach(i, restrictlist)
|
||||
|
@ -721,6 +819,37 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
|
|||
Var *left,
|
||||
*right;
|
||||
|
||||
/*
|
||||
* If processing an outer join, only use explicit join clauses in the
|
||||
* merge. For inner joins we need not be so picky.
|
||||
*
|
||||
* Furthermore, if it is a right/full join then *all* the explicit
|
||||
* join clauses must be mergejoinable, else the executor will fail.
|
||||
* If we are asked for a right join then just return NIL to indicate
|
||||
* no mergejoin is possible (we can handle it as a left join instead).
|
||||
* If we are asked for a full join then emit an error, because there
|
||||
* is no fallback.
|
||||
*/
|
||||
if (isouterjoin)
|
||||
{
|
||||
if (!restrictinfo->isjoinqual)
|
||||
continue;
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_RIGHT:
|
||||
if (restrictinfo->mergejoinoperator == InvalidOid)
|
||||
return NIL; /* not mergejoinable */
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
if (restrictinfo->mergejoinoperator == InvalidOid)
|
||||
elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
|
||||
break;
|
||||
default:
|
||||
/* otherwise, it's OK to have nonmergeable join quals */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (restrictinfo->mergejoinoperator == InvalidOid)
|
||||
continue; /* not mergejoinable */
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.46 2000/05/30 00:49:47 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.47 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -19,62 +19,52 @@
|
|||
|
||||
|
||||
static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1,
|
||||
RelOptInfo *rel2);
|
||||
RelOptInfo *rel2, JoinType jointype);
|
||||
|
||||
|
||||
/*
|
||||
* make_rels_by_joins
|
||||
* Consider ways to produce join relations containing exactly 'level'
|
||||
* base relations. (This is one step of the dynamic-programming method
|
||||
* jointree items. (This is one step of the dynamic-programming method
|
||||
* embodied in make_one_rel_by_joins.) Join rel nodes for each feasible
|
||||
* combination of base rels are created and added to the front of the
|
||||
* query's join_rel_list. Implementation paths are created for each
|
||||
* such joinrel, too.
|
||||
* combination of lower-level rels are created and returned in a list.
|
||||
* Implementation paths are created for each such joinrel, too.
|
||||
*
|
||||
* Returns nothing, but adds entries to root->join_rel_list.
|
||||
* level: level of rels we want to make this time.
|
||||
* joinrels[j], 1 <= j < level, is a list of rels containing j items.
|
||||
*/
|
||||
void
|
||||
make_rels_by_joins(Query *root, int level)
|
||||
List *
|
||||
make_rels_by_joins(Query *root, int level, List **joinrels)
|
||||
{
|
||||
List *first_old_rel = root->join_rel_list;
|
||||
List *result_rels = NIL;
|
||||
List *new_rels;
|
||||
List *nr;
|
||||
List *r;
|
||||
int k;
|
||||
|
||||
/*
|
||||
* First, consider left-sided and right-sided plans, in which rels of
|
||||
* exactly level-1 member relations are joined against base relations.
|
||||
* We prefer to join using join clauses, but if we find a rel of
|
||||
* level-1 members that has no join clauses, we will generate
|
||||
* Cartesian-product joins against all base rels not already contained
|
||||
* in it.
|
||||
* exactly level-1 member relations are joined against initial relations.
|
||||
* We prefer to join using join clauses, but if we find a rel of level-1
|
||||
* members that has no join clauses, we will generate Cartesian-product
|
||||
* joins against all initial rels not already contained in it.
|
||||
*
|
||||
* In the first pass (level == 2), we try to join each base rel to each
|
||||
* base rel that appears later in base_rel_list. (The mirror-image
|
||||
* In the first pass (level == 2), we try to join each initial rel to each
|
||||
* initial rel that appears later in joinrels[1]. (The mirror-image
|
||||
* joins are handled automatically by make_join_rel.) In later
|
||||
* passes, we try to join rels of size level-1 from join_rel_list to
|
||||
* each base rel in base_rel_list.
|
||||
*
|
||||
* We assume that the rels already present in join_rel_list appear in
|
||||
* decreasing order of level (number of members). This should be true
|
||||
* since we always add new higher-level rels to the front of the list.
|
||||
* passes, we try to join rels of size level-1 from joinrels[level-1]
|
||||
* to each initial rel in joinrels[1].
|
||||
*/
|
||||
if (level == 2)
|
||||
r = root->base_rel_list;/* level-1 is base rels */
|
||||
else
|
||||
r = root->join_rel_list;
|
||||
for (; r != NIL; r = lnext(r))
|
||||
foreach(r, joinrels[level-1])
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
int old_level = length(old_rel->relids);
|
||||
List *other_rels;
|
||||
|
||||
if (old_level != level - 1)
|
||||
break;
|
||||
|
||||
if (level == 2)
|
||||
other_rels = lnext(r); /* only consider remaining base
|
||||
other_rels = lnext(r); /* only consider remaining initial
|
||||
* rels */
|
||||
else
|
||||
other_rels = root->base_rel_list; /* consider all base rels */
|
||||
other_rels = joinrels[1]; /* consider all initial rels */
|
||||
|
||||
if (old_rel->joininfo != NIL)
|
||||
{
|
||||
|
@ -87,9 +77,9 @@ make_rels_by_joins(Query *root, int level)
|
|||
* have those other rels collected into a join rel. See also
|
||||
* the last-ditch case below.
|
||||
*/
|
||||
make_rels_by_clause_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
new_rels = make_rels_by_clause_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -98,64 +88,90 @@ make_rels_by_joins(Query *root, int level)
|
|||
* Oops, we have a relation that is not joined to any other
|
||||
* relation. Cartesian product time.
|
||||
*/
|
||||
make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
new_rels = make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
}
|
||||
|
||||
/*
|
||||
* At levels above 2 we will generate the same joined relation
|
||||
* in multiple ways --- for example (a join b) join c is the same
|
||||
* RelOptInfo as (b join c) join a, though the second case will
|
||||
* add a different set of Paths to it. To avoid making extra work
|
||||
* for subsequent passes, do not enter the same RelOptInfo into our
|
||||
* output list multiple times.
|
||||
*/
|
||||
foreach(nr, new_rels)
|
||||
{
|
||||
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
|
||||
|
||||
if (!ptrMember(jrel, result_rels))
|
||||
result_rels = lcons(jrel, result_rels);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, consider "bushy plans" in which relations of k base rels are
|
||||
* joined to relations of level-k base rels, for 2 <= k <= level-2.
|
||||
* The previous loop left r pointing to the first rel of level
|
||||
* level-2.
|
||||
* Now, consider "bushy plans" in which relations of k initial rels are
|
||||
* joined to relations of level-k initial rels, for 2 <= k <= level-2.
|
||||
*
|
||||
* We only consider bushy-plan joins for pairs of rels where there is a
|
||||
* suitable join clause, in order to avoid unreasonable growth of
|
||||
* planning time.
|
||||
*/
|
||||
for (; r != NIL; r = lnext(r))
|
||||
for (k = 2; ; k++)
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
int old_level = length(old_rel->relids);
|
||||
List *r2;
|
||||
int other_level = level - k;
|
||||
|
||||
/*
|
||||
* We can quit once past the halfway point (make_join_rel took
|
||||
* care of making the opposite-direction joins)
|
||||
* Since make_join_rel(x, y) handles both x,y and y,x cases,
|
||||
* we only need to go as far as the halfway point.
|
||||
*/
|
||||
if (old_level * 2 < level)
|
||||
if (k > other_level)
|
||||
break;
|
||||
|
||||
if (old_rel->joininfo == NIL)
|
||||
continue; /* we ignore clauseless joins here */
|
||||
|
||||
foreach(r2, lnext(r))
|
||||
foreach(r, joinrels[k])
|
||||
{
|
||||
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
|
||||
int new_level = length(new_rel->relids);
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
List *other_rels;
|
||||
List *r2;
|
||||
|
||||
if (old_level + new_level > level)
|
||||
continue; /* scan down to new_rels of right size */
|
||||
if (old_level + new_level < level)
|
||||
break; /* no more new_rels of right size */
|
||||
if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
|
||||
if (old_rel->joininfo == NIL)
|
||||
continue; /* we ignore clauseless joins here */
|
||||
|
||||
if (k == other_level)
|
||||
other_rels = lnext(r); /* only consider remaining rels */
|
||||
else
|
||||
other_rels = joinrels[other_level];
|
||||
|
||||
foreach(r2, other_rels)
|
||||
{
|
||||
List *i;
|
||||
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
|
||||
|
||||
/*
|
||||
* OK, we can build a rel of the right level from this
|
||||
* pair of rels. Do so if there is at least one usable
|
||||
* join clause.
|
||||
*/
|
||||
foreach(i, old_rel->joininfo)
|
||||
if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
List *i;
|
||||
|
||||
if (is_subseti(joininfo->unjoined_relids, new_rel->relids))
|
||||
/*
|
||||
* OK, we can build a rel of the right level from this
|
||||
* pair of rels. Do so if there is at least one usable
|
||||
* join clause.
|
||||
*/
|
||||
foreach(i, old_rel->joininfo)
|
||||
{
|
||||
make_join_rel(root, old_rel, new_rel);
|
||||
break;
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
|
||||
if (is_subseti(joininfo->unjoined_relids,
|
||||
new_rel->relids))
|
||||
{
|
||||
RelOptInfo *jrel;
|
||||
|
||||
jrel = make_join_rel(root, old_rel, new_rel,
|
||||
JOIN_INNER);
|
||||
/* Avoid making duplicate entries ... */
|
||||
if (!ptrMember(jrel, result_rels))
|
||||
result_rels = lcons(jrel, result_rels);
|
||||
break; /* need not consider more joininfos */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,39 +190,41 @@ make_rels_by_joins(Query *root, int level)
|
|||
* no choice but to make cartesian joins. We consider only left-sided
|
||||
* and right-sided cartesian joins in this case (no bushy).
|
||||
*/
|
||||
if (root->join_rel_list == first_old_rel)
|
||||
if (result_rels == NIL)
|
||||
{
|
||||
/* This loop is just like the first one, except we always call
|
||||
* make_rels_by_clauseless_joins().
|
||||
*/
|
||||
if (level == 2)
|
||||
r = root->base_rel_list; /* level-1 is base rels */
|
||||
else
|
||||
r = root->join_rel_list;
|
||||
for (; r != NIL; r = lnext(r))
|
||||
foreach(r, joinrels[level-1])
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
int old_level = length(old_rel->relids);
|
||||
List *other_rels;
|
||||
|
||||
if (old_level != level - 1)
|
||||
break;
|
||||
|
||||
if (level == 2)
|
||||
other_rels = lnext(r); /* only consider remaining base
|
||||
other_rels = lnext(r); /* only consider remaining initial
|
||||
* rels */
|
||||
else
|
||||
other_rels = root->base_rel_list; /* consider all base rels */
|
||||
other_rels = joinrels[1]; /* consider all initial rels */
|
||||
|
||||
make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
new_rels = make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
|
||||
foreach(nr, new_rels)
|
||||
{
|
||||
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
|
||||
|
||||
if (!ptrMember(jrel, result_rels))
|
||||
result_rels = lcons(jrel, result_rels);
|
||||
}
|
||||
}
|
||||
|
||||
if (root->join_rel_list == first_old_rel)
|
||||
if (result_rels == NIL)
|
||||
elog(ERROR, "make_rels_by_joins: failed to build any %d-way joins",
|
||||
level);
|
||||
}
|
||||
|
||||
return result_rels;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -214,28 +232,23 @@ make_rels_by_joins(Query *root, int level)
|
|||
* Build joins between the given relation 'old_rel' and other relations
|
||||
* that are mentioned within old_rel's joininfo nodes (i.e., relations
|
||||
* that participate in join clauses that 'old_rel' also participates in).
|
||||
* The join rel nodes are added to root->join_rel_list.
|
||||
* The join rel nodes are returned in a list.
|
||||
*
|
||||
* 'old_rel' is the relation entry for the relation to be joined
|
||||
* 'other_rels': other rels to be considered for joining
|
||||
*
|
||||
* Currently, this is only used with base rels in other_rels, but it would
|
||||
* work for joining to joinrels too, if the caller ensures there is no
|
||||
* Currently, this is only used with initial rels in other_rels, but it
|
||||
* will work for joining to joinrels too, if the caller ensures there is no
|
||||
* membership overlap between old_rel and the rels in other_rels. (We need
|
||||
* no extra test for overlap for base rels, since the is_subset test can
|
||||
* no extra test for overlap for initial rels, since the is_subset test can
|
||||
* only succeed when other_rel is not already part of old_rel.)
|
||||
*
|
||||
* Returns NULL if no suitable joins were found, else the last suitable
|
||||
* joinrel processed. (The only caller who checks the return value is
|
||||
* geqo_eval.c, and it sets things up so there can be no more than one
|
||||
* "suitable" joinrel; so we don't bother with returning a list.)
|
||||
*/
|
||||
RelOptInfo *
|
||||
List *
|
||||
make_rels_by_clause_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels)
|
||||
{
|
||||
RelOptInfo *result = NULL;
|
||||
List *result = NIL;
|
||||
List *i,
|
||||
*j;
|
||||
|
||||
|
@ -249,7 +262,9 @@ make_rels_by_clause_joins(Query *root,
|
|||
RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);
|
||||
|
||||
if (is_subseti(unjoined_relids, other_rel->relids))
|
||||
result = make_join_rel(root, old_rel, other_rel);
|
||||
result = lcons(make_join_rel(root, old_rel, other_rel,
|
||||
JOIN_INNER),
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,24 +276,20 @@ make_rels_by_clause_joins(Query *root,
|
|||
* Given a relation 'old_rel' and a list of other relations
|
||||
* 'other_rels', create a join relation between 'old_rel' and each
|
||||
* member of 'other_rels' that isn't already included in 'old_rel'.
|
||||
* The join rel nodes are returned in a list.
|
||||
*
|
||||
* 'old_rel' is the relation entry for the relation to be joined
|
||||
* 'other_rels': other rels to be considered for joining
|
||||
*
|
||||
* Currently, this is only used with base rels in other_rels, but it would
|
||||
* Currently, this is only used with initial rels in other_rels, but it would
|
||||
* work for joining to joinrels too.
|
||||
*
|
||||
* Returns NULL if no suitable joins were found, else the last suitable
|
||||
* joinrel processed. (The only caller who checks the return value is
|
||||
* geqo_eval.c, and it sets things up so there can be no more than one
|
||||
* "suitable" joinrel; so we don't bother with returning a list.)
|
||||
*/
|
||||
RelOptInfo *
|
||||
List *
|
||||
make_rels_by_clauseless_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels)
|
||||
{
|
||||
RelOptInfo *result = NULL;
|
||||
List *result = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, other_rels)
|
||||
|
@ -286,13 +297,61 @@ make_rels_by_clauseless_joins(Query *root,
|
|||
RelOptInfo *other_rel = (RelOptInfo *) lfirst(i);
|
||||
|
||||
if (nonoverlap_setsi(other_rel->relids, old_rel->relids))
|
||||
result = make_join_rel(root, old_rel, other_rel);
|
||||
result = lcons(make_join_rel(root, old_rel, other_rel,
|
||||
JOIN_INNER),
|
||||
result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_rel_from_jointree
|
||||
* Find or build a RelOptInfojoin rel representing a specific
|
||||
* jointree item. For JoinExprs, we only consider the construction
|
||||
* path that corresponds exactly to what the user wrote.
|
||||
*/
|
||||
RelOptInfo *
|
||||
make_rel_from_jointree(Query *root, Node *jtnode)
|
||||
{
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
|
||||
return get_base_rel(root, varno);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
RelOptInfo *rel,
|
||||
*lrel,
|
||||
*rrel;
|
||||
|
||||
/* Recurse */
|
||||
lrel = make_rel_from_jointree(root, j->larg);
|
||||
rrel = make_rel_from_jointree(root, j->rarg);
|
||||
|
||||
/* Make this join rel */
|
||||
rel = make_join_rel(root, lrel, rrel, j->jointype);
|
||||
|
||||
/*
|
||||
* Since we are only going to consider this one way to do it,
|
||||
* we're done generating Paths for this joinrel and can now select
|
||||
* the cheapest. In fact we *must* do so now, since next level up
|
||||
* will need it!
|
||||
*/
|
||||
set_cheapest(rel);
|
||||
|
||||
return rel;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "make_rel_from_jointree: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_join_rel
|
||||
* Find or create a join RelOptInfo that represents the join of
|
||||
|
@ -300,10 +359,10 @@ make_rels_by_clauseless_joins(Query *root,
|
|||
* created with the two rels as outer and inner rel.
|
||||
* (The join rel may already contain paths generated from other
|
||||
* pairs of rels that add up to the same set of base rels.)
|
||||
* The join rel is stored in the query's join_rel_list.
|
||||
*/
|
||||
static RelOptInfo *
|
||||
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
|
||||
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
|
||||
JoinType jointype)
|
||||
{
|
||||
RelOptInfo *joinrel;
|
||||
List *restrictlist;
|
||||
|
@ -315,10 +374,39 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
|
|||
joinrel = get_join_rel(root, rel1, rel2, &restrictlist);
|
||||
|
||||
/*
|
||||
* We consider paths using each rel as both outer and inner.
|
||||
* Consider paths using each rel as both outer and inner.
|
||||
*/
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, restrictlist);
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER,
|
||||
restrictlist);
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT,
|
||||
restrictlist);
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL,
|
||||
restrictlist);
|
||||
break;
|
||||
case JOIN_RIGHT:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT,
|
||||
restrictlist);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "make_join_rel: unsupported join type %d",
|
||||
(int) jointype);
|
||||
break;
|
||||
}
|
||||
|
||||
return joinrel;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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,
|
|||
|
||||
/* This isn't a nestloop innerjoin, so: */
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->alljoinquals = false;
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* 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)
|
|||
*
|
||||
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
|
||||
* that will be used in a merge join.
|
||||
* 'tlist' is a relation target list for either the inner or outer
|
||||
* side of the proposed join rel. (Not actually needed anymore)
|
||||
* 'rel' is the relation the pathkeys will apply to (ie, either the inner
|
||||
* or outer side of the proposed join rel).
|
||||
*
|
||||
* Returns a pathkeys list that can be applied to the indicated relation.
|
||||
*
|
||||
|
@ -706,7 +706,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
|||
List *
|
||||
make_pathkeys_for_mergeclauses(Query *root,
|
||||
List *mergeclauses,
|
||||
List *tlist)
|
||||
RelOptInfo *rel)
|
||||
{
|
||||
List *pathkeys = NIL;
|
||||
List *i;
|
||||
|
@ -722,30 +722,37 @@ make_pathkeys_for_mergeclauses(Query *root,
|
|||
Assert(restrictinfo->mergejoinoperator != InvalidOid);
|
||||
|
||||
/*
|
||||
* Find the key and sortop needed for this mergeclause.
|
||||
*
|
||||
* 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.
|
||||
* Which key and sortop is needed for this relation?
|
||||
*/
|
||||
key = (Node *) get_leftop(restrictinfo->clause);
|
||||
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
|
||||
* canonical set including the mergeclause's left and right sides;
|
||||
* if we get back just the one item, something is rotten.
|
||||
* Find or create canonical pathkey sublist for this sort item.
|
||||
*/
|
||||
item = makePathKeyItem(key, sortop);
|
||||
pathkey = make_canonical_pathkey(root, item);
|
||||
Assert(length(pathkey) > 1);
|
||||
|
||||
/*
|
||||
* Since the item we just made is not in the returned canonical
|
||||
* set, we can free it --- this saves a useful amount of storage
|
||||
* in a big join tree.
|
||||
* Most of the time we will get back a canonical pathkey set
|
||||
* including both the mergeclause's left and right sides (the only
|
||||
* 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);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.95 2000/08/13 02:50:06 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.96 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -42,36 +42,47 @@ static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path,
|
|||
static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
|
||||
List *scan_clauses);
|
||||
static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
|
||||
List *clauses, Plan *outer_node, List *outer_tlist,
|
||||
Plan *inner_node, List *inner_tlist);
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *outer_node, List *outer_tlist,
|
||||
Plan *inner_node, List *inner_tlist);
|
||||
static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
|
||||
List *clauses, Plan *outer_node, List *outer_tlist,
|
||||
Plan *inner_node, List *inner_tlist);
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *outer_node, List *outer_tlist,
|
||||
Plan *inner_node, List *inner_tlist);
|
||||
static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
|
||||
List *clauses, Plan *outer_node, List *outer_tlist,
|
||||
Plan *inner_node, List *inner_tlist);
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *outer_node, List *outer_tlist,
|
||||
Plan *inner_node, List *inner_tlist);
|
||||
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
|
||||
static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
|
||||
Form_pg_index index);
|
||||
static Node *fix_indxqual_operand(Node *node, int baserelid,
|
||||
Form_pg_index index,
|
||||
Oid *opclass);
|
||||
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
|
||||
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
List *indxid, List *indxqual,
|
||||
List *indxqualorig,
|
||||
ScanDirection indexscandir);
|
||||
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
List *tideval);
|
||||
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
|
||||
Plan *righttree);
|
||||
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
|
||||
List *hashclauses, Plan *lefttree, Plan *righttree);
|
||||
static NestLoop *make_nestloop(List *tlist,
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *lefttree, Plan *righttree,
|
||||
JoinType jointype);
|
||||
static HashJoin *make_hashjoin(List *tlist,
|
||||
List *joinclauses, List *otherclauses,
|
||||
List *hashclauses,
|
||||
Plan *lefttree, Plan *righttree,
|
||||
JoinType jointype);
|
||||
static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
|
||||
static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
|
||||
List *mergeclauses, Plan *righttree, Plan *lefttree);
|
||||
static MergeJoin *make_mergejoin(List *tlist,
|
||||
List *joinclauses, List *otherclauses,
|
||||
List *mergeclauses,
|
||||
Plan *lefttree, Plan *righttree,
|
||||
JoinType jointype);
|
||||
static void copy_path_costsize(Plan *dest, Path *src);
|
||||
static void copy_plan_costsize(Plan *dest, Plan *src);
|
||||
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
|
||||
|
||||
/*
|
||||
* create_plan
|
||||
|
@ -195,7 +206,8 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
|
|||
List *outer_tlist;
|
||||
Plan *inner_node;
|
||||
List *inner_tlist;
|
||||
List *clauses;
|
||||
List *joinclauses;
|
||||
List *otherclauses;
|
||||
Join *retval = NULL;
|
||||
|
||||
outer_node = create_plan(root, best_path->outerjoinpath);
|
||||
|
@ -204,14 +216,25 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
|
|||
inner_node = create_plan(root, best_path->innerjoinpath);
|
||||
inner_tlist = inner_node->targetlist;
|
||||
|
||||
clauses = get_actual_clauses(best_path->joinrestrictinfo);
|
||||
if (IS_OUTER_JOIN(best_path->jointype))
|
||||
{
|
||||
get_actual_join_clauses(best_path->joinrestrictinfo,
|
||||
&joinclauses, &otherclauses);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We can treat all clauses alike for an inner join */
|
||||
joinclauses = get_actual_clauses(best_path->joinrestrictinfo);
|
||||
otherclauses = NIL;
|
||||
}
|
||||
|
||||
switch (best_path->path.pathtype)
|
||||
{
|
||||
case T_MergeJoin:
|
||||
retval = (Join *) create_mergejoin_node((MergePath *) best_path,
|
||||
tlist,
|
||||
clauses,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
outer_tlist,
|
||||
inner_node,
|
||||
|
@ -220,7 +243,8 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
|
|||
case T_HashJoin:
|
||||
retval = (Join *) create_hashjoin_node((HashPath *) best_path,
|
||||
tlist,
|
||||
clauses,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
outer_tlist,
|
||||
inner_node,
|
||||
|
@ -229,7 +253,8 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
|
|||
case T_NestLoop:
|
||||
retval = (Join *) create_nestloop_node((NestPath *) best_path,
|
||||
tlist,
|
||||
clauses,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
outer_tlist,
|
||||
inner_node,
|
||||
|
@ -411,30 +436,6 @@ create_indexscan_node(Query *root,
|
|||
return scan_node;
|
||||
}
|
||||
|
||||
static TidScan *
|
||||
make_tidscan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
List *tideval)
|
||||
{
|
||||
TidScan *node = makeNode(TidScan);
|
||||
Plan *plan = &node->scan.plan;
|
||||
|
||||
/* cost should be inserted by caller */
|
||||
plan->state = (EState *) NULL;
|
||||
plan->targetlist = qptlist;
|
||||
plan->qual = qpqual;
|
||||
plan->lefttree = NULL;
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->tideval = copyObject(tideval); /* XXX do we really need a
|
||||
* copy? */
|
||||
node->needRescan = false;
|
||||
node->scan.scanstate = (CommonScanState *) NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_tidscan_node
|
||||
* Returns a tidscan node for the base relation scanned by 'best_path'
|
||||
|
@ -488,7 +489,8 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
|
|||
static NestLoop *
|
||||
create_nestloop_node(NestPath *best_path,
|
||||
List *tlist,
|
||||
List *clauses,
|
||||
List *joinclauses,
|
||||
List *otherclauses,
|
||||
Plan *outer_node,
|
||||
List *outer_tlist,
|
||||
Plan *inner_node,
|
||||
|
@ -535,7 +537,8 @@ create_nestloop_node(NestPath *best_path,
|
|||
* attnos, and may have been commuted as well).
|
||||
*/
|
||||
if (length(indxqualorig) == 1) /* single indexscan? */
|
||||
clauses = set_difference(clauses, lfirst(indxqualorig));
|
||||
joinclauses = set_difference(joinclauses,
|
||||
lfirst(indxqualorig));
|
||||
|
||||
/* only refs to outer vars get changed in the inner indexqual */
|
||||
innerscan->indxqualorig = join_references(indxqualorig,
|
||||
|
@ -577,15 +580,26 @@ create_nestloop_node(NestPath *best_path,
|
|||
inner_node);
|
||||
}
|
||||
|
||||
join_node = make_nestloop(tlist,
|
||||
join_references(clauses,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0),
|
||||
outer_node,
|
||||
inner_node);
|
||||
/*
|
||||
* Set quals to contain INNER/OUTER var references.
|
||||
*/
|
||||
joinclauses = join_references(joinclauses,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
otherclauses = join_references(otherclauses,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
|
||||
copy_path_costsize(&join_node->join, &best_path->path);
|
||||
join_node = make_nestloop(tlist,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
outer_node,
|
||||
inner_node,
|
||||
best_path->jointype);
|
||||
|
||||
copy_path_costsize(&join_node->join.plan, &best_path->path);
|
||||
|
||||
return join_node;
|
||||
}
|
||||
|
@ -593,14 +607,14 @@ create_nestloop_node(NestPath *best_path,
|
|||
static MergeJoin *
|
||||
create_mergejoin_node(MergePath *best_path,
|
||||
List *tlist,
|
||||
List *clauses,
|
||||
List *joinclauses,
|
||||
List *otherclauses,
|
||||
Plan *outer_node,
|
||||
List *outer_tlist,
|
||||
Plan *inner_node,
|
||||
List *inner_tlist)
|
||||
{
|
||||
List *qpqual,
|
||||
*mergeclauses;
|
||||
List *mergeclauses;
|
||||
MergeJoin *join_node;
|
||||
|
||||
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
|
||||
|
@ -610,10 +624,18 @@ create_mergejoin_node(MergePath *best_path,
|
|||
* the list of quals that must be checked as qpquals. Set those
|
||||
* clauses to contain INNER/OUTER var references.
|
||||
*/
|
||||
qpqual = join_references(set_difference(clauses, mergeclauses),
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
joinclauses = join_references(set_difference(joinclauses, mergeclauses),
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
|
||||
/*
|
||||
* Fix the additional qpquals too.
|
||||
*/
|
||||
otherclauses = join_references(otherclauses,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
|
||||
/*
|
||||
* Now set the references in the mergeclauses and rearrange them so
|
||||
|
@ -640,13 +662,54 @@ create_mergejoin_node(MergePath *best_path,
|
|||
inner_node,
|
||||
best_path->innersortkeys);
|
||||
|
||||
join_node = make_mergejoin(tlist,
|
||||
qpqual,
|
||||
mergeclauses,
|
||||
inner_node,
|
||||
outer_node);
|
||||
/*
|
||||
* The executor requires the inner side of a mergejoin to support "mark"
|
||||
* and "restore" operations. Not all plan types do, so we must be careful
|
||||
* not to generate an invalid plan. If necessary, an invalid inner plan
|
||||
* can be handled by inserting a Materialize node.
|
||||
*
|
||||
* Since the inner side must be ordered, and only Sorts and IndexScans can
|
||||
* create order to begin with, you might think there's no problem --- but
|
||||
* you'd be wrong. Nestloop and merge joins can *preserve* the order of
|
||||
* their inputs, so they can be selected as the input of a mergejoin,
|
||||
* and that won't work in the present executor.
|
||||
*
|
||||
* Doing this here is a bit of a kluge since the cost of the Materialize
|
||||
* wasn't taken into account in our earlier decisions. But Materialize
|
||||
* is hard to estimate a cost for, and the above consideration shows that
|
||||
* this is a rare case anyway, so this seems an acceptable way to proceed.
|
||||
*
|
||||
* This check must agree with ExecMarkPos/ExecRestrPos in
|
||||
* executor/execAmi.c!
|
||||
*/
|
||||
switch (nodeTag(inner_node))
|
||||
{
|
||||
case T_SeqScan:
|
||||
case T_IndexScan:
|
||||
case T_Material:
|
||||
case T_Sort:
|
||||
/* OK, these inner plans support mark/restore */
|
||||
break;
|
||||
|
||||
copy_path_costsize(&join_node->join, &best_path->jpath.path);
|
||||
default:
|
||||
/* Ooops, need to materialize the inner plan */
|
||||
inner_node = (Plan *) make_material(inner_tlist,
|
||||
inner_node);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we can build the mergejoin node.
|
||||
*/
|
||||
join_node = make_mergejoin(tlist,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
mergeclauses,
|
||||
outer_node,
|
||||
inner_node,
|
||||
best_path->jpath.jointype);
|
||||
|
||||
copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
|
||||
|
||||
return join_node;
|
||||
}
|
||||
|
@ -654,13 +717,13 @@ create_mergejoin_node(MergePath *best_path,
|
|||
static HashJoin *
|
||||
create_hashjoin_node(HashPath *best_path,
|
||||
List *tlist,
|
||||
List *clauses,
|
||||
List *joinclauses,
|
||||
List *otherclauses,
|
||||
Plan *outer_node,
|
||||
List *outer_tlist,
|
||||
Plan *inner_node,
|
||||
List *inner_tlist)
|
||||
{
|
||||
List *qpqual;
|
||||
List *hashclauses;
|
||||
HashJoin *join_node;
|
||||
Hash *hash_node;
|
||||
|
@ -679,10 +742,18 @@ create_hashjoin_node(HashPath *best_path,
|
|||
* the list of quals that must be checked as qpquals. Set those
|
||||
* clauses to contain INNER/OUTER var references.
|
||||
*/
|
||||
qpqual = join_references(set_difference(clauses, hashclauses),
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
joinclauses = join_references(set_difference(joinclauses, hashclauses),
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
|
||||
/*
|
||||
* Fix the additional qpquals too.
|
||||
*/
|
||||
otherclauses = join_references(otherclauses,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
|
||||
/*
|
||||
* Now set the references in the hashclauses and rearrange them so
|
||||
|
@ -701,12 +772,14 @@ create_hashjoin_node(HashPath *best_path,
|
|||
*/
|
||||
hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
|
||||
join_node = make_hashjoin(tlist,
|
||||
qpqual,
|
||||
joinclauses,
|
||||
otherclauses,
|
||||
hashclauses,
|
||||
outer_node,
|
||||
(Plan *) hash_node);
|
||||
(Plan *) hash_node,
|
||||
best_path->jpath.jointype);
|
||||
|
||||
copy_path_costsize(&join_node->join, &best_path->jpath.path);
|
||||
copy_path_costsize(&join_node->join.plan, &best_path->jpath.path);
|
||||
|
||||
return join_node;
|
||||
}
|
||||
|
@ -1065,45 +1138,75 @@ make_indexscan(List *qptlist,
|
|||
return node;
|
||||
}
|
||||
|
||||
|
||||
static NestLoop *
|
||||
make_nestloop(List *qptlist,
|
||||
List *qpqual,
|
||||
Plan *lefttree,
|
||||
Plan *righttree)
|
||||
static TidScan *
|
||||
make_tidscan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
List *tideval)
|
||||
{
|
||||
NestLoop *node = makeNode(NestLoop);
|
||||
Plan *plan = &node->join;
|
||||
TidScan *node = makeNode(TidScan);
|
||||
Plan *plan = &node->scan.plan;
|
||||
|
||||
/* cost should be inserted by caller */
|
||||
plan->state = (EState *) NULL;
|
||||
plan->targetlist = qptlist;
|
||||
plan->qual = qpqual;
|
||||
plan->lefttree = NULL;
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->tideval = copyObject(tideval); /* XXX do we really need a
|
||||
* copy? */
|
||||
node->needRescan = false;
|
||||
node->scan.scanstate = (CommonScanState *) NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
static NestLoop *
|
||||
make_nestloop(List *tlist,
|
||||
List *joinclauses,
|
||||
List *otherclauses,
|
||||
Plan *lefttree,
|
||||
Plan *righttree,
|
||||
JoinType jointype)
|
||||
{
|
||||
NestLoop *node = makeNode(NestLoop);
|
||||
Plan *plan = &node->join.plan;
|
||||
|
||||
/* cost should be inserted by caller */
|
||||
plan->state = (EState *) NULL;
|
||||
plan->targetlist = tlist;
|
||||
plan->qual = otherclauses;
|
||||
plan->lefttree = lefttree;
|
||||
plan->righttree = righttree;
|
||||
node->nlstate = (NestLoopState *) NULL;
|
||||
node->join.jointype = jointype;
|
||||
node->join.joinqual = joinclauses;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static HashJoin *
|
||||
make_hashjoin(List *tlist,
|
||||
List *qpqual,
|
||||
List *joinclauses,
|
||||
List *otherclauses,
|
||||
List *hashclauses,
|
||||
Plan *lefttree,
|
||||
Plan *righttree)
|
||||
Plan *righttree,
|
||||
JoinType jointype)
|
||||
{
|
||||
HashJoin *node = makeNode(HashJoin);
|
||||
Plan *plan = &node->join;
|
||||
Plan *plan = &node->join.plan;
|
||||
|
||||
/* cost should be inserted by caller */
|
||||
plan->state = (EState *) NULL;
|
||||
plan->targetlist = tlist;
|
||||
plan->qual = qpqual;
|
||||
plan->qual = otherclauses;
|
||||
plan->lefttree = lefttree;
|
||||
plan->righttree = righttree;
|
||||
node->hashclauses = hashclauses;
|
||||
node->hashdone = false;
|
||||
node->join.jointype = jointype;
|
||||
node->join.joinqual = joinclauses;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -1133,21 +1236,25 @@ make_hash(List *tlist, Node *hashkey, Plan *lefttree)
|
|||
|
||||
static MergeJoin *
|
||||
make_mergejoin(List *tlist,
|
||||
List *qpqual,
|
||||
List *joinclauses,
|
||||
List *otherclauses,
|
||||
List *mergeclauses,
|
||||
Plan *lefttree,
|
||||
Plan *righttree,
|
||||
Plan *lefttree)
|
||||
JoinType jointype)
|
||||
{
|
||||
MergeJoin *node = makeNode(MergeJoin);
|
||||
Plan *plan = &node->join;
|
||||
Plan *plan = &node->join.plan;
|
||||
|
||||
/* cost should be inserted by caller */
|
||||
plan->state = (EState *) NULL;
|
||||
plan->targetlist = tlist;
|
||||
plan->qual = qpqual;
|
||||
plan->qual = otherclauses;
|
||||
plan->lefttree = lefttree;
|
||||
plan->righttree = righttree;
|
||||
node->mergeclauses = mergeclauses;
|
||||
node->join.jointype = jointype;
|
||||
node->join.joinqual = joinclauses;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.49 2000/08/13 02:50:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.50 2000/09/12 21:06:54 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -26,13 +26,18 @@
|
|||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_oper.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static void add_restrict_and_join_to_rel(Query *root, Node *clause);
|
||||
static void mark_baserels_for_outer_join(Query *root, Relids rels,
|
||||
Relids outerrels);
|
||||
static void add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
bool isjoinqual,
|
||||
Relids outerjoinrelids);
|
||||
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
Relids join_relids);
|
||||
static void add_vars_to_targetlist(Query *root, List *vars);
|
||||
|
@ -47,14 +52,14 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
|
|||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* make_var_only_tlist
|
||||
* build_base_rel_tlists
|
||||
* Creates rel nodes for every relation mentioned in the target list
|
||||
* 'tlist' (if a node hasn't already been created) and adds them to
|
||||
* *query_relation_list*. Creates targetlist entries for each member of
|
||||
* 'tlist' and adds them to the tlist field of the appropriate rel node.
|
||||
* root->base_rel_list. Creates targetlist entries for each var seen
|
||||
* in 'tlist' and adds them to the tlist of the appropriate rel node.
|
||||
*/
|
||||
void
|
||||
make_var_only_tlist(Query *root, List *tlist)
|
||||
build_base_rel_tlists(Query *root, List *tlist)
|
||||
{
|
||||
List *tlist_vars = pull_var_clause((Node *) tlist, false);
|
||||
|
||||
|
@ -82,48 +87,75 @@ add_vars_to_targetlist(Query *root, List *vars)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*----------
|
||||
* add_missing_rels_to_query
|
||||
*
|
||||
* If we have a range variable in the FROM clause that does not appear
|
||||
* If we have a relation listed in the join tree that does not appear
|
||||
* in the target list nor qualifications, we must add it to the base
|
||||
* relation list so that it will be joined. For instance, "select f.x
|
||||
* from foo f, foo f2" is a join of f and f2. Note that if we have
|
||||
* "select foo.x from foo f", it also gets turned into a join (between
|
||||
* foo as foo and foo as f).
|
||||
* relation list so that it can be processed. For instance,
|
||||
* select f.x from foo f, foo f2
|
||||
* is a join of f and f2. Note that if we have
|
||||
* select foo.x from foo f
|
||||
* this also gets turned into a join (between foo as foo and foo as f).
|
||||
*
|
||||
* To avoid putting useless entries into the per-relation targetlists,
|
||||
* this should only be called after all the variables in the targetlist
|
||||
* and quals have been processed by the routines above.
|
||||
*
|
||||
* Returns a list of all the base relations (RelOptInfo nodes) that appear
|
||||
* in the join tree. This list can be used for cross-checking in the
|
||||
* reverse direction, ie, that we have a join tree entry for every
|
||||
* relation used in the query.
|
||||
*----------
|
||||
*/
|
||||
void
|
||||
add_missing_rels_to_query(Query *root)
|
||||
List *
|
||||
add_missing_rels_to_query(Query *root, Node *jtnode)
|
||||
{
|
||||
int varno = 1;
|
||||
List *l;
|
||||
List *result = NIL;
|
||||
|
||||
foreach(l, root->rtable)
|
||||
if (jtnode == NULL)
|
||||
return NIL;
|
||||
if (IsA(jtnode, List))
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
||||
List *l;
|
||||
|
||||
if (rte->inJoinSet)
|
||||
foreach(l, (List *) jtnode)
|
||||
{
|
||||
RelOptInfo *rel = get_base_rel(root, varno);
|
||||
|
||||
/*
|
||||
* If the rel isn't otherwise referenced, give it a dummy
|
||||
* targetlist consisting of its own OID.
|
||||
*/
|
||||
if (rel->targetlist == NIL)
|
||||
{
|
||||
Var *var = makeVar(varno, ObjectIdAttributeNumber,
|
||||
OIDOID, -1, 0);
|
||||
|
||||
add_var_to_tlist(rel, var);
|
||||
}
|
||||
result = nconc(result,
|
||||
add_missing_rels_to_query(root, lfirst(l)));
|
||||
}
|
||||
varno++;
|
||||
}
|
||||
else if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
RelOptInfo *rel = get_base_rel(root, varno);
|
||||
|
||||
/*
|
||||
* If the rel isn't otherwise referenced, give it a dummy
|
||||
* targetlist consisting of its own OID.
|
||||
*/
|
||||
if (rel->targetlist == NIL)
|
||||
{
|
||||
Var *var = makeVar(varno, ObjectIdAttributeNumber,
|
||||
OIDOID, -1, 0);
|
||||
|
||||
add_var_to_tlist(rel, var);
|
||||
}
|
||||
|
||||
result = lcons(rel, NIL);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
result = add_missing_rels_to_query(root, j->larg);
|
||||
result = nconc(result,
|
||||
add_missing_rels_to_query(root, j->rarg));
|
||||
}
|
||||
else
|
||||
elog(ERROR, "add_missing_rels_to_query: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -134,11 +166,145 @@ add_missing_rels_to_query(Query *root)
|
|||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* add_join_quals_to_rels
|
||||
* Recursively scan the join tree for JOIN/ON (and JOIN/USING) qual
|
||||
* clauses, and add these to the appropriate JoinInfo lists. Also,
|
||||
* mark base RelOptInfos with outerjoinset information, which will
|
||||
* be needed for proper placement of WHERE clauses during
|
||||
* add_restrict_and_join_to_rels().
|
||||
*
|
||||
* NOTE: when dealing with inner joins, it is appropriate to let a qual clause
|
||||
* be evaluated at the lowest level where all the variables it mentions are
|
||||
* available. However, we cannot do this within an outer join since the qual
|
||||
* might eliminate matching rows and cause a NULL row to be added improperly.
|
||||
* Therefore, rels appearing within (the nullable side of) an outer join
|
||||
* are marked with outerjoinset = list of Relids used at the outer join node.
|
||||
* This list will be added to the list of rels referenced by quals using
|
||||
* such a rel, thereby forcing them up the join tree to the right level.
|
||||
*
|
||||
* To ease the calculation of these values, add_join_quals_to_rels() returns
|
||||
* the list of Relids involved in its own level of join. This is just an
|
||||
* internal convenience; no outside callers pay attention to the result.
|
||||
*/
|
||||
Relids
|
||||
add_join_quals_to_rels(Query *root, Node *jtnode)
|
||||
{
|
||||
Relids result = NIL;
|
||||
|
||||
if (jtnode == NULL)
|
||||
return result;
|
||||
if (IsA(jtnode, List))
|
||||
{
|
||||
List *l;
|
||||
|
||||
/*
|
||||
* Note: we assume it's impossible to see same RT index from more
|
||||
* than one subtree, so nconc() is OK rather than LispUnioni().
|
||||
*/
|
||||
foreach(l, (List *) jtnode)
|
||||
result = nconc(result,
|
||||
add_join_quals_to_rels(root, lfirst(l)));
|
||||
}
|
||||
else if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
|
||||
/* No quals to deal with, just return correct result */
|
||||
result = lconsi(varno, NIL);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
Relids leftids,
|
||||
rightids,
|
||||
outerjoinids;
|
||||
List *qual;
|
||||
|
||||
/*
|
||||
* Order of operations here is subtle and critical. First we recurse
|
||||
* to handle sub-JOINs. Their join quals will be placed without
|
||||
* regard for whether this level is an outer join, which is correct.
|
||||
* Then, if we are an outer join, we mark baserels contained within
|
||||
* the nullable side(s) with our own rel list; this will restrict
|
||||
* placement of subsequent quals using those rels, including our own
|
||||
* quals, quals above us in the join tree, and WHERE quals.
|
||||
* Finally we place our own join quals.
|
||||
*/
|
||||
leftids = add_join_quals_to_rels(root, j->larg);
|
||||
rightids = add_join_quals_to_rels(root, j->rarg);
|
||||
|
||||
result = nconc(listCopy(leftids), rightids);
|
||||
|
||||
outerjoinids = NIL;
|
||||
switch (j->jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
/* Inner join adds no restrictions for quals */
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
mark_baserels_for_outer_join(root, rightids, result);
|
||||
outerjoinids = result;
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
mark_baserels_for_outer_join(root, result, result);
|
||||
outerjoinids = result;
|
||||
break;
|
||||
case JOIN_RIGHT:
|
||||
mark_baserels_for_outer_join(root, leftids, result);
|
||||
outerjoinids = result;
|
||||
break;
|
||||
case JOIN_UNION:
|
||||
/*
|
||||
* This is where we fail if upper levels of planner haven't
|
||||
* rewritten UNION JOIN as an Append ...
|
||||
*/
|
||||
elog(ERROR, "UNION JOIN is not implemented yet");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "add_join_quals_to_rels: unsupported join type %d",
|
||||
(int) j->jointype);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach(qual, (List *) j->quals)
|
||||
add_restrict_and_join_to_rel(root, (Node *) lfirst(qual),
|
||||
true, outerjoinids);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "add_join_quals_to_rels: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* mark_baserels_for_outer_join
|
||||
* Mark all base rels listed in 'rels' as having the given outerjoinset.
|
||||
*/
|
||||
static void
|
||||
mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
|
||||
{
|
||||
List *relid;
|
||||
|
||||
foreach(relid, rels)
|
||||
{
|
||||
RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
|
||||
|
||||
/*
|
||||
* Since we do this bottom-up, any outer-rels previously marked
|
||||
* should be within the new outer join set.
|
||||
*/
|
||||
Assert(is_subseti(rel->outerjoinset, outerrels));
|
||||
|
||||
rel->outerjoinset = outerrels;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add_restrict_and_join_to_rels
|
||||
* Fill RestrictInfo and JoinInfo lists of relation entries for all
|
||||
* relations appearing within clauses. Creates new relation entries if
|
||||
* necessary, adding them to *query_relation_list*.
|
||||
* necessary, adding them to root->base_rel_list.
|
||||
*
|
||||
* 'clauses': the list of clauses in the cnfify'd query qualification.
|
||||
*/
|
||||
|
@ -148,7 +314,8 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
|
|||
List *clause;
|
||||
|
||||
foreach(clause, clauses)
|
||||
add_restrict_and_join_to_rel(root, (Node *) lfirst(clause));
|
||||
add_restrict_and_join_to_rel(root, (Node *) lfirst(clause),
|
||||
false, NIL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -157,17 +324,31 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
|
|||
* (depending on whether the clause is a join) of each base relation
|
||||
* mentioned in the clause. A RestrictInfo node is created and added to
|
||||
* the appropriate list for each rel. Also, if the clause uses a
|
||||
* mergejoinable operator, enter the left- and right-side expressions
|
||||
* into the query's lists of equijoined vars.
|
||||
* mergejoinable operator and is not an outer-join qual, enter the left-
|
||||
* and right-side expressions into the query's lists of equijoined vars.
|
||||
*
|
||||
* isjoinqual is true if the clause came from JOIN/ON or JOIN/USING;
|
||||
* we have to mark the created RestrictInfo accordingly. If the JOIN
|
||||
* is an OUTER join, the caller must set outerjoinrelids = all relids of join,
|
||||
* which will override the joinrel identifiers extracted from the clause
|
||||
* itself. For inner join quals and WHERE clauses, set outerjoinrelids = NIL.
|
||||
* (Passing the whole list, and not just an "isouterjoin" boolean, is simply
|
||||
* a speed optimization: we could extract the same list from the base rels'
|
||||
* outerjoinsets, but since add_join_quals_to_rels() already knows what we
|
||||
* should use, might as well pass it in instead of recalculating it.)
|
||||
*/
|
||||
static void
|
||||
add_restrict_and_join_to_rel(Query *root, Node *clause)
|
||||
add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
bool isjoinqual,
|
||||
Relids outerjoinrelids)
|
||||
{
|
||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||
Relids relids;
|
||||
List *vars;
|
||||
bool can_be_equijoin;
|
||||
|
||||
restrictinfo->clause = (Expr *) clause;
|
||||
restrictinfo->isjoinqual = isjoinqual;
|
||||
restrictinfo->subclauseindices = NIL;
|
||||
restrictinfo->mergejoinoperator = InvalidOid;
|
||||
restrictinfo->left_sortop = InvalidOid;
|
||||
|
@ -179,6 +360,44 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
|
|||
*/
|
||||
clause_get_relids_vars(clause, &relids, &vars);
|
||||
|
||||
/*
|
||||
* If caller has given us a join relid list, use it; otherwise, we must
|
||||
* scan the referenced base rels and add in any outer-join rel lists.
|
||||
* This prevents the clause from being applied at a lower level of joining
|
||||
* than any OUTER JOIN that should be evaluated before it.
|
||||
*/
|
||||
if (outerjoinrelids)
|
||||
{
|
||||
/* Safety check: parser should have enforced this to start with */
|
||||
if (! is_subseti(relids, outerjoinrelids))
|
||||
elog(ERROR, "JOIN qualification may not refer to other relations");
|
||||
relids = outerjoinrelids;
|
||||
can_be_equijoin = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Relids newrelids = relids;
|
||||
List *relid;
|
||||
|
||||
/* We rely on LispUnioni to be nondestructive of its input lists... */
|
||||
can_be_equijoin = true;
|
||||
foreach(relid, relids)
|
||||
{
|
||||
RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
|
||||
|
||||
if (rel->outerjoinset)
|
||||
{
|
||||
newrelids = LispUnioni(newrelids, rel->outerjoinset);
|
||||
/*
|
||||
* Because application of the qual will be delayed by outer
|
||||
* join, we mustn't assume its vars are equal everywhere.
|
||||
*/
|
||||
can_be_equijoin = false;
|
||||
}
|
||||
}
|
||||
relids = newrelids;
|
||||
}
|
||||
|
||||
if (length(relids) == 1)
|
||||
{
|
||||
|
||||
|
@ -199,7 +418,8 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
|
|||
* that "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to
|
||||
* consider z and q equal after their rels are joined.
|
||||
*/
|
||||
check_mergejoinable(restrictinfo);
|
||||
if (can_be_equijoin)
|
||||
check_mergejoinable(restrictinfo);
|
||||
}
|
||||
else if (relids != NIL)
|
||||
{
|
||||
|
@ -209,11 +429,11 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
|
|||
* the relid list. Set additional RestrictInfo fields for
|
||||
* joining.
|
||||
*
|
||||
* We need the merge info whether or not mergejoin is enabled (for
|
||||
* constructing equijoined-var lists), but we don't bother setting
|
||||
* hash info if hashjoin is disabled.
|
||||
* We don't bother setting the merge/hashjoin info if we're not
|
||||
* going to need it.
|
||||
*/
|
||||
check_mergejoinable(restrictinfo);
|
||||
if (enable_mergejoin || can_be_equijoin)
|
||||
check_mergejoinable(restrictinfo);
|
||||
if (enable_hashjoin)
|
||||
check_hashjoinable(restrictinfo);
|
||||
|
||||
|
@ -223,7 +443,7 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
|
|||
add_join_info_to_rels(root, restrictinfo, relids);
|
||||
|
||||
/*
|
||||
* Add vars used in the join clause to targetlists of member
|
||||
* Add vars used in the join clause to targetlists of their
|
||||
* relations, so that they will be emitted by the plan nodes that
|
||||
* scan those relations (else they won't be available at the join
|
||||
* node!).
|
||||
|
@ -241,12 +461,14 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
|
|||
}
|
||||
|
||||
/*
|
||||
* If the clause has a mergejoinable operator, then the two sides
|
||||
* If the clause has a mergejoinable operator, and is not an outer-join
|
||||
* qualification nor bubbled up due to an outer join, then the two sides
|
||||
* represent equivalent PathKeyItems for path keys: any path that is
|
||||
* sorted by one side will also be sorted by the other (after joining,
|
||||
* that is). Record the key equivalence for future use.
|
||||
* sorted by one side will also be sorted by the other (as soon as the
|
||||
* two rels are joined, that is). Record the key equivalence for future
|
||||
* use.
|
||||
*/
|
||||
if (restrictinfo->mergejoinoperator != InvalidOid)
|
||||
if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid)
|
||||
add_equijoined_keys(root, restrictinfo);
|
||||
}
|
||||
|
||||
|
@ -392,7 +614,8 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
|||
BOOLOID); /* operator result type */
|
||||
clause->args = lcons(item1, lcons(item2, NIL));
|
||||
|
||||
add_restrict_and_join_to_rel(root, (Node *) clause);
|
||||
add_restrict_and_join_to_rel(root, (Node *) clause,
|
||||
false, NIL);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*
|
||||
*
|
||||
* 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 @@
|
|||
#include "optimizer/paths.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
|
@ -41,11 +42,8 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
|
|||
* not any fancier features.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* qual must already have been converted to implicit-AND form.
|
||||
*
|
||||
* 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
|
||||
* 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,
|
|||
Plan *
|
||||
query_planner(Query *root,
|
||||
List *tlist,
|
||||
List *qual,
|
||||
double tuple_fraction)
|
||||
{
|
||||
List *normal_qual;
|
||||
List *noncachable_qual;
|
||||
List *constant_qual;
|
||||
List *var_only_tlist;
|
||||
|
@ -96,7 +94,7 @@ query_planner(Query *root,
|
|||
root->query_pathkeys = NIL; /* signal unordered result */
|
||||
|
||||
/* 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,
|
|||
* noncachable functions but no vars, such as "WHERE random() < 0.5".
|
||||
* These cannot be treated as normal restriction or join quals, but
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
|
@ -132,7 +132,7 @@ query_planner(Query *root,
|
|||
/*
|
||||
* 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.
|
||||
|
@ -188,6 +188,8 @@ subplanner(Query *root,
|
|||
List *qual,
|
||||
double tuple_fraction)
|
||||
{
|
||||
List *joined_rels;
|
||||
List *brel;
|
||||
RelOptInfo *final_rel;
|
||||
Plan *resultplan;
|
||||
MemoryContext mycontext;
|
||||
|
@ -196,7 +198,7 @@ subplanner(Query *root,
|
|||
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
|
||||
* qualification, the targetlist, etc.). Restrict and join clauses
|
||||
* are added to appropriate lists belonging to the mentioned
|
||||
|
@ -207,13 +209,29 @@ subplanner(Query *root,
|
|||
root->join_rel_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);
|
||||
|
||||
/*
|
||||
* Make sure we have RelOptInfo nodes for all relations used.
|
||||
* Make sure we have RelOptInfo nodes for all relations to be joined.
|
||||
*/
|
||||
add_missing_rels_to_query(root);
|
||||
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.
|
||||
*/
|
||||
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
|
||||
|
@ -258,12 +276,11 @@ subplanner(Query *root,
|
|||
* We expect to end up here for a trivial INSERT ... VALUES query
|
||||
* (which will have a target relation, so it gets past
|
||||
* query_planner's check for empty range table; but the target rel
|
||||
* is unreferenced and not marked inJoinSet, so we find there is
|
||||
* nothing to join).
|
||||
* is not in the join tree, so we find there is nothing to join).
|
||||
*
|
||||
* It's also possible to get here if the query was rewritten by the
|
||||
* rule processor (creating rangetable entries not marked
|
||||
* inJoinSet) but the rules either did nothing or were simplified
|
||||
* rule processor (creating dummy rangetable entries that are not in
|
||||
* the join tree) but the rules either did nothing or were simplified
|
||||
* to nothing by constant-expression folding. So, don't complain.
|
||||
*/
|
||||
root->query_pathkeys = NIL; /* signal unordered result */
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* 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 @@
|
|||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static void preprocess_join_conditions(Query *parse, Node *jtnode);
|
||||
static List *make_subplanTargetList(Query *parse, List *tlist,
|
||||
AttrNumber **groupColIdx);
|
||||
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
|
||||
|
@ -163,6 +164,7 @@ subquery_planner(Query *parse, double tuple_fraction)
|
|||
* canonicalize_qual?
|
||||
*/
|
||||
parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true);
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
printf("After canonicalize_qual()\n");
|
||||
pprint(parse->qual);
|
||||
|
@ -211,6 +213,9 @@ subquery_planner(Query *parse, double tuple_fraction)
|
|||
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) */
|
||||
|
||||
return union_planner(parse, 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
|
||||
|
@ -542,7 +599,6 @@ union_planner(Query *parse,
|
|||
/* Generate the (sub) plan */
|
||||
result_plan = query_planner(parse,
|
||||
sub_tlist,
|
||||
(List *) parse->qual,
|
||||
tuple_fraction);
|
||||
|
||||
/*
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.64 2000/06/04 20:50:50 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.65 2000/09/12 21:06:54 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -106,11 +106,13 @@ set_plan_references(Plan *plan)
|
|||
set_join_references((Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
|
||||
break;
|
||||
case T_MergeJoin:
|
||||
set_join_references((Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
|
||||
fix_expr_references(plan,
|
||||
(Node *) ((MergeJoin *) plan)->mergeclauses);
|
||||
break;
|
||||
|
@ -118,6 +120,7 @@ set_plan_references(Plan *plan)
|
|||
set_join_references((Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
|
||||
fix_expr_references(plan,
|
||||
(Node *) ((HashJoin *) plan)->hashclauses);
|
||||
break;
|
||||
|
@ -236,15 +239,15 @@ fix_expr_references(Plan *plan, Node *node)
|
|||
static void
|
||||
set_join_references(Join *join)
|
||||
{
|
||||
Plan *outer = join->lefttree;
|
||||
Plan *inner = join->righttree;
|
||||
Plan *outer = join->plan.lefttree;
|
||||
Plan *inner = join->plan.righttree;
|
||||
List *outer_tlist = ((outer == NULL) ? NIL : outer->targetlist);
|
||||
List *inner_tlist = ((inner == NULL) ? NIL : inner->targetlist);
|
||||
|
||||
join->targetlist = join_references(join->targetlist,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
join->plan.targetlist = join_references(join->plan.targetlist,
|
||||
outer_tlist,
|
||||
inner_tlist,
|
||||
(Index) 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.40 2000/08/06 04:13:22 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.41 2000/09/12 21:06:54 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -649,12 +649,21 @@ SS_finalize_plan(Plan *plan)
|
|||
*/
|
||||
break;
|
||||
|
||||
case T_NestLoop:
|
||||
finalize_primnode((Node *) ((Join *) plan)->joinqual,
|
||||
&results);
|
||||
break;
|
||||
|
||||
case T_MergeJoin:
|
||||
finalize_primnode((Node *) ((Join *) plan)->joinqual,
|
||||
&results);
|
||||
finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
|
||||
&results);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
finalize_primnode((Node *) ((Join *) plan)->joinqual,
|
||||
&results);
|
||||
finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
|
||||
&results);
|
||||
break;
|
||||
|
@ -671,7 +680,6 @@ SS_finalize_plan(Plan *plan)
|
|||
|
||||
case T_Agg:
|
||||
case T_SeqScan:
|
||||
case T_NestLoop:
|
||||
case T_Material:
|
||||
case T_Sort:
|
||||
case T_Unique:
|
||||
|
|
|
@ -107,6 +107,7 @@ transformKeySetQuery(Query *origNode)
|
|||
Node_Copy(origNode, unionNode, distinctClause);
|
||||
Node_Copy(origNode, unionNode, sortClause);
|
||||
Node_Copy(origNode, unionNode, rtable);
|
||||
Node_Copy(origNode, unionNode, jointree);
|
||||
Node_Copy(origNode, unionNode, targetList);
|
||||
|
||||
origNode->unionClause = lappend(origNode->unionClause, unionNode);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.51 2000/06/20 04:22:16 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.52 2000/09/12 21:06:57 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -528,12 +528,9 @@ fix_parsetree_attnums(Index rt_index,
|
|||
context.new_relid = new_relid;
|
||||
context.sublevels_up = 0;
|
||||
|
||||
/*
|
||||
* We must scan both the targetlist and qual, but we know the
|
||||
* havingQual is empty, so we can ignore it.
|
||||
*/
|
||||
fix_parsetree_attnums_walker((Node *) parsetree->targetList, &context);
|
||||
fix_parsetree_attnums_walker((Node *) parsetree->qual, &context);
|
||||
query_tree_walker(parsetree,
|
||||
fix_parsetree_attnums_walker,
|
||||
(void *) &context);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -565,38 +562,17 @@ fix_parsetree_attnums_walker(Node *node,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (fix_parsetree_attnums_walker((Node *) (sub->lefthand), context))
|
||||
return true;
|
||||
context->sublevels_up++;
|
||||
if (fix_parsetree_attnums_walker((Node *) (sub->subselect), context))
|
||||
{
|
||||
context->sublevels_up--;
|
||||
return true;
|
||||
}
|
||||
context->sublevels_up--;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
if (fix_parsetree_attnums_walker((Node *) (qry->targetList), context))
|
||||
return true;
|
||||
if (fix_parsetree_attnums_walker((Node *) (qry->qual), context))
|
||||
return true;
|
||||
if (fix_parsetree_attnums_walker((Node *) (qry->havingQual), context))
|
||||
return true;
|
||||
return false;
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node,
|
||||
fix_parsetree_attnums_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, fix_parsetree_attnums_walker,
|
||||
(void *) context);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.73 2000/08/24 03:29:05 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.74 2000/09/12 21:06:58 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
|
@ -591,7 +591,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
|
|||
elog(ERROR, "cache lookup of attribute %d in relation %u failed",
|
||||
var->varattno, rte->relid);
|
||||
elog(ERROR, "Sub-SELECT uses un-GROUPed attribute %s.%s from outer query",
|
||||
rte->ref->relname, attname);
|
||||
rte->eref->relname, attname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1639,25 +1639,44 @@ simplify_op_or_func(Expr *expr, List *args)
|
|||
* will have List structure at the top level, and it handles TargetEntry nodes
|
||||
* so that a scan of a target list can be handled without additional code.
|
||||
* (But only the "expr" part of a TargetEntry is examined, unless the walker
|
||||
* chooses to process TargetEntry nodes specially.)
|
||||
* chooses to process TargetEntry nodes specially.) Also, RangeTblRef and
|
||||
* JoinExpr nodes are handled, so that qual expressions in a jointree can be
|
||||
* processed without additional code.
|
||||
*
|
||||
* expression_tree_walker will handle a SUBPLAN_EXPR node by recursing into
|
||||
* the args and slink->oper lists (which belong to the outer plan), but it
|
||||
* will *not* visit the inner plan, since that's typically what expression
|
||||
* tree walkers want. A walker that wants to visit the subplan can force
|
||||
* appropriate behavior by recognizing subplan expression nodes and doing
|
||||
* the right thing.
|
||||
* expression_tree_walker will handle SubLink and SubPlan nodes by recursing
|
||||
* normally into the "lefthand" arguments (which belong to the outer plan).
|
||||
* It will also call the walker on the sub-Query node; however, when
|
||||
* expression_tree_walker itself is called on a Query node, it does nothing
|
||||
* and returns "false". The net effect is that unless the walker does
|
||||
* something special at a Query node, sub-selects will not be visited
|
||||
* during an expression tree walk. This is exactly the behavior wanted
|
||||
* in many cases --- and for those walkers that do want to recurse into
|
||||
* sub-selects, special behavior is typically needed anyway at the entry
|
||||
* to a sub-select (such as incrementing a depth counter). A walker that
|
||||
* wants to examine sub-selects should include code along the lines of:
|
||||
*
|
||||
* Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into
|
||||
* the "lefthand" argument list only. (A bare SubLink should be seen only if
|
||||
* the tree has not yet been processed by subselect.c.) Again, this can be
|
||||
* overridden by the walker, but it seems to be the most useful default
|
||||
* behavior.
|
||||
* if (IsA(node, Query))
|
||||
* {
|
||||
* adjust context for subquery;
|
||||
* result = query_tree_walker((Query *) node, my_walker, context);
|
||||
* restore context if needed;
|
||||
* return result;
|
||||
* }
|
||||
*
|
||||
* query_tree_walker is a convenience routine (see below) that calls the
|
||||
* walker on all the expression subtrees of the given Query node.
|
||||
*
|
||||
* NOTE: currently, because make_subplan() clears the subselect link in
|
||||
* a SubLink node, it is not actually possible to recurse into subselects
|
||||
* of an already-planned expression tree. This is OK for current uses,
|
||||
* but ought to be cleaned up when we redesign querytree processing.
|
||||
*--------------------
|
||||
*/
|
||||
|
||||
bool
|
||||
expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
||||
expression_tree_walker(Node *node,
|
||||
bool (*walker) (),
|
||||
void *context)
|
||||
{
|
||||
List *temp;
|
||||
|
||||
|
@ -1677,6 +1696,7 @@ bool
|
|||
case T_Const:
|
||||
case T_Var:
|
||||
case T_Param:
|
||||
case T_RangeTblRef:
|
||||
/* primitive node types with no subnodes */
|
||||
break;
|
||||
case T_Expr:
|
||||
|
@ -1750,17 +1770,31 @@ bool
|
|||
|
||||
/*
|
||||
* If the SubLink has already been processed by
|
||||
* subselect.c, it will have lefthand=NIL, and we only
|
||||
* need to look at the oper list. Otherwise we only need
|
||||
* to look at lefthand (the Oper nodes in the oper list
|
||||
* are deemed uninteresting).
|
||||
* subselect.c, it will have lefthand=NIL, and we need to
|
||||
* scan the oper list. Otherwise we only need to look at
|
||||
* the lefthand list (the incomplete Oper nodes in the oper
|
||||
* list are deemed uninteresting, perhaps even confusing).
|
||||
*/
|
||||
if (sublink->lefthand)
|
||||
return walker((Node *) sublink->lefthand, context);
|
||||
{
|
||||
if (walker((Node *) sublink->lefthand, context))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return walker((Node *) sublink->oper, context);
|
||||
{
|
||||
if (walker((Node *) sublink->oper, context))
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* Also invoke the walker on the sublink's Query node,
|
||||
* so it can recurse into the sub-query if it wants to.
|
||||
*/
|
||||
return walker(sublink->subselect, context);
|
||||
}
|
||||
break;
|
||||
case T_Query:
|
||||
/* Do nothing with a sub-Query, per discussion above */
|
||||
break;
|
||||
case T_List:
|
||||
foreach(temp, (List *) node)
|
||||
{
|
||||
|
@ -1770,6 +1804,23 @@ bool
|
|||
break;
|
||||
case T_TargetEntry:
|
||||
return walker(((TargetEntry *) node)->expr, context);
|
||||
case T_JoinExpr:
|
||||
{
|
||||
JoinExpr *join = (JoinExpr *) node;
|
||||
|
||||
if (walker(join->larg, context))
|
||||
return true;
|
||||
if (walker(join->rarg, context))
|
||||
return true;
|
||||
if (walker(join->quals, context))
|
||||
return true;
|
||||
if (walker((Node *) join->colvars, context))
|
||||
return true;
|
||||
/* alias clause, using list, colnames list are deemed
|
||||
* uninteresting.
|
||||
*/
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "expression_tree_walker: Unexpected node type %d",
|
||||
nodeTag(node));
|
||||
|
@ -1778,6 +1829,37 @@ bool
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* query_tree_walker --- initiate a walk of a Query's expressions
|
||||
*
|
||||
* This routine exists just to reduce the number of places that need to know
|
||||
* where all the expression subtrees of a Query are. Note it can be used
|
||||
* for starting a walk at top level of a Query regardless of whether the
|
||||
* walker intends to descend into subqueries. It is also useful for
|
||||
* descending into subqueries within a walker.
|
||||
*/
|
||||
bool
|
||||
query_tree_walker(Query *query,
|
||||
bool (*walker) (),
|
||||
void *context)
|
||||
{
|
||||
Assert(query != NULL && IsA(query, Query));
|
||||
|
||||
if (walker((Node *) query->targetList, context))
|
||||
return true;
|
||||
if (walker(query->qual, context))
|
||||
return true;
|
||||
if (walker(query->havingQual, context))
|
||||
return true;
|
||||
if (walker((Node *) query->jointree, context))
|
||||
return true;
|
||||
/*
|
||||
* XXX for subselect-in-FROM, may need to examine rtable as well
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------
|
||||
* expression_tree_mutator() is designed to support routines that make a
|
||||
* modified copy of an expression tree, with some nodes being added,
|
||||
|
@ -1838,7 +1920,9 @@ bool
|
|||
*/
|
||||
|
||||
Node *
|
||||
expression_tree_mutator(Node *node, Node *(*mutator) (), void *context)
|
||||
expression_tree_mutator(Node *node,
|
||||
Node *(*mutator) (),
|
||||
void *context)
|
||||
{
|
||||
|
||||
/*
|
||||
|
@ -1866,6 +1950,7 @@ Node *
|
|||
case T_Const:
|
||||
case T_Var:
|
||||
case T_Param:
|
||||
case T_RangeTblRef:
|
||||
/* primitive node types with no subnodes */
|
||||
return (Node *) copyObject(node);
|
||||
case T_Expr:
|
||||
|
@ -2044,6 +2129,20 @@ Node *
|
|||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
{
|
||||
JoinExpr *join = (JoinExpr *) node;
|
||||
JoinExpr *newnode;
|
||||
|
||||
FLATCOPY(newnode, join, JoinExpr);
|
||||
MUTATE(newnode->larg, join->larg, Node *);
|
||||
MUTATE(newnode->rarg, join->rarg, Node *);
|
||||
MUTATE(newnode->quals, join->quals, Node *);
|
||||
MUTATE(newnode->colvars, join->colvars, List *);
|
||||
/* We do not mutate alias, using, or colnames by default */
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "expression_tree_mutator: Unexpected node type %d",
|
||||
nodeTag(node));
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.64 2000/05/30 00:49:49 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.65 2000/09/12 21:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -119,7 +119,9 @@ set_cheapest(RelOptInfo *parent_rel)
|
|||
Path *cheapest_total_path;
|
||||
|
||||
Assert(IsA(parent_rel, RelOptInfo));
|
||||
Assert(pathlist != NIL);
|
||||
|
||||
if (pathlist == NIL)
|
||||
elog(ERROR, "Unable to devise a query plan for the given query");
|
||||
|
||||
cheapest_startup_path = cheapest_total_path = (Path *) lfirst(pathlist);
|
||||
|
||||
|
@ -352,6 +354,7 @@ create_index_path(Query *root,
|
|||
* number of rows is the same as the parent rel's estimate.
|
||||
*/
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->alljoinquals = false;
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
cost_index(&pathnode->path, root, rel, index, indexquals, false);
|
||||
|
@ -393,6 +396,7 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
|||
* relations.
|
||||
*
|
||||
* 'joinrel' is the join relation.
|
||||
* 'jointype' is the type of join required
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
||||
|
@ -403,6 +407,7 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
|||
*/
|
||||
NestPath *
|
||||
create_nestloop_path(RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
|
@ -412,6 +417,7 @@ create_nestloop_path(RelOptInfo *joinrel,
|
|||
|
||||
pathnode->path.pathtype = T_NestLoop;
|
||||
pathnode->path.parent = joinrel;
|
||||
pathnode->jointype = jointype;
|
||||
pathnode->outerjoinpath = outer_path;
|
||||
pathnode->innerjoinpath = inner_path;
|
||||
pathnode->joinrestrictinfo = restrict_clauses;
|
||||
|
@ -428,6 +434,7 @@ create_nestloop_path(RelOptInfo *joinrel,
|
|||
* two relations
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'jointype' is the type of join required
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
||||
|
@ -440,6 +447,7 @@ create_nestloop_path(RelOptInfo *joinrel,
|
|||
*/
|
||||
MergePath *
|
||||
create_mergejoin_path(RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
|
@ -463,6 +471,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
|||
|
||||
pathnode->jpath.path.pathtype = T_MergeJoin;
|
||||
pathnode->jpath.path.parent = joinrel;
|
||||
pathnode->jpath.jointype = jointype;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.joinrestrictinfo = restrict_clauses;
|
||||
|
@ -486,6 +495,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
|||
* Creates a pathnode corresponding to a hash join between two relations.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'jointype' is the type of join required
|
||||
* 'outer_path' is the cheapest outer path
|
||||
* 'inner_path' is the cheapest inner path
|
||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
||||
|
@ -496,6 +506,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
|
|||
*/
|
||||
HashPath *
|
||||
create_hashjoin_path(RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
|
@ -506,6 +517,7 @@ create_hashjoin_path(RelOptInfo *joinrel,
|
|||
|
||||
pathnode->jpath.path.pathtype = T_HashJoin;
|
||||
pathnode->jpath.path.parent = joinrel;
|
||||
pathnode->jpath.jointype = jointype;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.joinrestrictinfo = restrict_clauses;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.27 2000/06/18 22:44:12 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.28 2000/09/12 21:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -72,6 +72,7 @@ get_base_rel(Query *root, int relid)
|
|||
rel->tuples = 0;
|
||||
rel->baserestrictinfo = NIL;
|
||||
rel->baserestrictcost = 0;
|
||||
rel->outerjoinset = NIL;
|
||||
rel->joininfo = NIL;
|
||||
rel->innerjoin = NIL;
|
||||
|
||||
|
@ -178,6 +179,7 @@ get_join_rel(Query *root,
|
|||
joinrel->tuples = 0;
|
||||
joinrel->baserestrictinfo = NIL;
|
||||
joinrel->baserestrictcost = 0;
|
||||
joinrel->outerjoinset = NIL;
|
||||
joinrel->joininfo = NIL;
|
||||
joinrel->innerjoin = NIL;
|
||||
|
||||
|
@ -216,8 +218,7 @@ get_join_rel(Query *root,
|
|||
restrictlist);
|
||||
|
||||
/*
|
||||
* Add the joinrel to the front of the query's joinrel list.
|
||||
* (allpaths.c depends on this ordering!)
|
||||
* Add the joinrel to the query's joinrel list.
|
||||
*/
|
||||
root->join_rel_list = lcons(joinrel, root->join_rel_list);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.10 2000/05/30 00:49:49 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.11 2000/09/12 21:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -54,3 +54,29 @@ get_actual_clauses(List *restrictinfo_list)
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_actual_join_clauses
|
||||
*
|
||||
* Extract clauses from 'restrictinfo_list', separating those that
|
||||
* came from JOIN/ON conditions from those that didn't.
|
||||
*/
|
||||
void
|
||||
get_actual_join_clauses(List *restrictinfo_list,
|
||||
List **joinquals, List **otherquals)
|
||||
{
|
||||
List *temp;
|
||||
|
||||
*joinquals = NIL;
|
||||
*otherquals = NIL;
|
||||
|
||||
foreach(temp, restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
|
||||
|
||||
if (clause->isjoinqual)
|
||||
*joinquals = lappend(*joinquals, clause->clause);
|
||||
else
|
||||
*otherquals = lappend(*otherquals, clause->clause);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.26 2000/04/12 17:15:24 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.27 2000/09/12 21:06:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -16,17 +16,25 @@
|
|||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/plannodes.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/var.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
List *varlist;
|
||||
int sublevels_up;
|
||||
} pull_varnos_context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
List *varlist;
|
||||
bool includeUpperVars;
|
||||
} pull_var_clause_context;
|
||||
|
||||
static bool pull_varnos_walker(Node *node, List **listptr);
|
||||
static bool pull_varnos_walker(Node *node,
|
||||
pull_varnos_context *context);
|
||||
static bool contain_var_clause_walker(Node *node, void *context);
|
||||
static bool pull_var_clause_walker(Node *node,
|
||||
pull_var_clause_context *context);
|
||||
|
@ -35,21 +43,39 @@ static bool pull_var_clause_walker(Node *node,
|
|||
/*
|
||||
* pull_varnos
|
||||
*
|
||||
* Create a list of all the distinct varnos present in a parsetree
|
||||
* (tlist or qual). Note that only varnos attached to level-zero
|
||||
* Vars are considered --- upper Vars refer to some other rtable!
|
||||
* Create a list of all the distinct varnos present in a parsetree.
|
||||
* Only varnos that reference level-zero rtable entries are considered.
|
||||
*
|
||||
* NOTE: unlike other routines in this file, pull_varnos() is used on
|
||||
* not-yet-planned expressions. It may therefore find bare SubLinks,
|
||||
* and if so it needs to recurse into them to look for uplevel references
|
||||
* to the desired rtable level! But when we find a completed SubPlan,
|
||||
* we only need to look at the parameters passed to the subplan.
|
||||
*/
|
||||
List *
|
||||
pull_varnos(Node *node)
|
||||
{
|
||||
List *result = NIL;
|
||||
pull_varnos_context context;
|
||||
|
||||
pull_varnos_walker(node, &result);
|
||||
return result;
|
||||
context.varlist = NIL;
|
||||
context.sublevels_up = 0;
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree;
|
||||
* if it's a Query, go straight to query_tree_walker to make sure that
|
||||
* sublevels_up doesn't get incremented prematurely.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
query_tree_walker((Query *) node, pull_varnos_walker,
|
||||
(void *) &context);
|
||||
else
|
||||
pull_varnos_walker(node, &context);
|
||||
|
||||
return context.varlist;
|
||||
}
|
||||
|
||||
static bool
|
||||
pull_varnos_walker(Node *node, List **listptr)
|
||||
pull_varnos_walker(Node *node, pull_varnos_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
|
@ -57,11 +83,42 @@ pull_varnos_walker(Node *node, List **listptr)
|
|||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == 0 && !intMember(var->varno, *listptr))
|
||||
*listptr = lconsi(var->varno, *listptr);
|
||||
if (var->varlevelsup == context->sublevels_up &&
|
||||
!intMember(var->varno, context->varlist))
|
||||
context->varlist = lconsi(var->varno, context->varlist);
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, pull_varnos_walker, (void *) listptr);
|
||||
if (is_subplan(node))
|
||||
{
|
||||
/*
|
||||
* Already-planned subquery. Examine the args list (parameters
|
||||
* to be passed to subquery), as well as the "oper" list which
|
||||
* is executed by the outer query. But short-circuit recursion into
|
||||
* the subquery itself, which would be a waste of effort.
|
||||
*/
|
||||
Expr *expr = (Expr *) node;
|
||||
|
||||
if (pull_varnos_walker((Node*) ((SubPlan*) expr->oper)->sublink->oper,
|
||||
context))
|
||||
return true;
|
||||
if (pull_varnos_walker((Node *) expr->args,
|
||||
context))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Recurse into not-yet-planned subquery */
|
||||
bool result;
|
||||
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node, pull_varnos_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, pull_varnos_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# Makefile for parser
|
||||
#
|
||||
# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.29 2000/08/28 11:53:19 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.30 2000/09/12 21:07:00 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
|
@ -31,7 +31,7 @@ $(srcdir)/gram.c $(srcdir)/parse.h: gram.y
|
|||
|
||||
$(srcdir)/scan.c: scan.l
|
||||
ifdef FLEX
|
||||
$(FLEX) $(FLEXFLAGS) -o'$@' $<
|
||||
$(FLEX) $(FLEXFLAGS) -Pbase_yy -o'$@' $<
|
||||
else
|
||||
@$(missing) flex $< $@
|
||||
endif
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: analyze.c,v 1.156 2000/08/29 04:20:44 momjian Exp $
|
||||
* $Id: analyze.c,v 1.157 2000/09/12 21:07:00 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include "parser/parse_relation.h"
|
||||
#include "parser/parse_target.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/relcache.h"
|
||||
|
@ -54,6 +55,8 @@ static void transformConstraintAttrs(List *constraintList);
|
|||
static void transformColumnType(ParseState *pstate, ColumnDef *column);
|
||||
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
|
||||
|
||||
static void release_pstate_resources(ParseState *pstate);
|
||||
|
||||
/* kluge to return extra info from transformCreateStmt() */
|
||||
static List *extras_before;
|
||||
static List *extras_after;
|
||||
|
@ -71,28 +74,22 @@ List *
|
|||
parse_analyze(List *pl, ParseState *parentParseState)
|
||||
{
|
||||
List *result = NIL;
|
||||
ParseState *pstate;
|
||||
Query *parsetree;
|
||||
|
||||
while (pl != NIL)
|
||||
{
|
||||
ParseState *pstate = make_parsestate(parentParseState);
|
||||
Query *parsetree;
|
||||
|
||||
extras_before = extras_after = NIL;
|
||||
pstate = make_parsestate(parentParseState);
|
||||
|
||||
parsetree = transformStmt(pstate, lfirst(pl));
|
||||
if (pstate->p_target_relation != NULL)
|
||||
heap_close(pstate->p_target_relation, AccessShareLock);
|
||||
pstate->p_target_relation = NULL;
|
||||
pstate->p_target_rangetblentry = NULL;
|
||||
release_pstate_resources(pstate);
|
||||
|
||||
while (extras_before != NIL)
|
||||
{
|
||||
result = lappend(result,
|
||||
transformStmt(pstate, lfirst(extras_before)));
|
||||
if (pstate->p_target_relation != NULL)
|
||||
heap_close(pstate->p_target_relation, AccessShareLock);
|
||||
pstate->p_target_relation = NULL;
|
||||
pstate->p_target_rangetblentry = NULL;
|
||||
transformStmt(pstate, lfirst(extras_before)));
|
||||
release_pstate_resources(pstate);
|
||||
extras_before = lnext(extras_before);
|
||||
}
|
||||
|
||||
|
@ -102,10 +99,7 @@ parse_analyze(List *pl, ParseState *parentParseState)
|
|||
{
|
||||
result = lappend(result,
|
||||
transformStmt(pstate, lfirst(extras_after)));
|
||||
if (pstate->p_target_relation != NULL)
|
||||
heap_close(pstate->p_target_relation, AccessShareLock);
|
||||
pstate->p_target_relation = NULL;
|
||||
pstate->p_target_rangetblentry = NULL;
|
||||
release_pstate_resources(pstate);
|
||||
extras_after = lnext(extras_after);
|
||||
}
|
||||
|
||||
|
@ -116,6 +110,15 @@ parse_analyze(List *pl, ParseState *parentParseState)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
release_pstate_resources(ParseState *pstate)
|
||||
{
|
||||
if (pstate->p_target_relation != NULL)
|
||||
heap_close(pstate->p_target_relation, AccessShareLock);
|
||||
pstate->p_target_relation = NULL;
|
||||
pstate->p_target_rangetblentry = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformStmt -
|
||||
* transform a Parse tree. If it is an optimizable statement, turn it
|
||||
|
@ -176,11 +179,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
|
|||
Resdom *rd;
|
||||
|
||||
id = nth(i, n->aliases);
|
||||
Assert(nodeTag(id) == T_Ident);
|
||||
Assert(IsA(id, Ident));
|
||||
te = nth(i, targetList);
|
||||
Assert(nodeTag(te) == T_TargetEntry);
|
||||
Assert(IsA(te, TargetEntry));
|
||||
rd = te->resdom;
|
||||
Assert(nodeTag(rd) == T_Resdom);
|
||||
Assert(IsA(rd, Resdom));
|
||||
rd->resname = pstrdup(id->name);
|
||||
}
|
||||
}
|
||||
|
@ -290,15 +293,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
|||
qry->commandType = CMD_DELETE;
|
||||
|
||||
/* set up a range table */
|
||||
makeRangeTable(pstate, NULL);
|
||||
setTargetTable(pstate, stmt->relname, stmt->inh);
|
||||
makeRangeTable(pstate, NIL);
|
||||
setTargetTable(pstate, stmt->relname, stmt->inh, true);
|
||||
|
||||
qry->distinctClause = NIL;
|
||||
|
||||
/* fix where clause */
|
||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
/* done building the rtable */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
@ -387,12 +392,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
|||
*
|
||||
* In particular, it's time to add the INSERT target to the rangetable.
|
||||
* (We didn't want it there until now since it shouldn't be visible in
|
||||
* the SELECT part.)
|
||||
* the SELECT part.) Note that the INSERT target is NOT added to the
|
||||
* join tree, since we don't want to join over it.
|
||||
*/
|
||||
setTargetTable(pstate, stmt->relname, FALSE);
|
||||
setTargetTable(pstate, stmt->relname, false, false);
|
||||
|
||||
/* now the range table will not change */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
||||
|
||||
/* Prepare to assign non-conflicting resnos to resjunk attributes */
|
||||
|
@ -908,7 +915,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
|||
while (dlist != NIL)
|
||||
{
|
||||
constraint = lfirst(dlist);
|
||||
Assert(nodeTag(constraint) == T_Constraint);
|
||||
Assert(IsA(constraint, Constraint));
|
||||
Assert((constraint->contype == CONSTR_PRIMARY)
|
||||
|| (constraint->contype == CONSTR_UNIQUE));
|
||||
|
||||
|
@ -1427,17 +1434,68 @@ static Query *
|
|||
transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
{
|
||||
Query *qry;
|
||||
Query *action;
|
||||
List *actions;
|
||||
RangeTblEntry *oldrte;
|
||||
RangeTblEntry *newrte;
|
||||
|
||||
qry = makeNode(Query);
|
||||
qry->commandType = CMD_UTILITY;
|
||||
qry->utilityStmt = (Node *) stmt;
|
||||
|
||||
/*
|
||||
* 'instead nothing' rules with a qualification need a query a
|
||||
* NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
|
||||
* equal to 2. Set up their RTEs in the main pstate for use
|
||||
* in parsing the rule qualification.
|
||||
*/
|
||||
Assert(pstate->p_rtable == NIL);
|
||||
oldrte = addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*OLD*", NULL),
|
||||
false, true);
|
||||
newrte = addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
false, true);
|
||||
/*
|
||||
* They must be in the jointree too for lookup purposes, but only add
|
||||
* the one(s) that are relevant for the current kind of rule. In an
|
||||
* UPDATE rule, quals must refer to OLD.field or NEW.field to be
|
||||
* unambiguous, but there's no need to be so picky for INSERT & DELETE.
|
||||
* (Note we marked the RTEs "inFromCl = true" above to allow unqualified
|
||||
* references to their fields.)
|
||||
*/
|
||||
switch (stmt->event)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
addRTEtoJoinTree(pstate, oldrte);
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
addRTEtoJoinTree(pstate, oldrte);
|
||||
addRTEtoJoinTree(pstate, newrte);
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
addRTEtoJoinTree(pstate, newrte);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
addRTEtoJoinTree(pstate, oldrte);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "transformRuleStmt: unexpected event type %d",
|
||||
(int) stmt->event);
|
||||
break;
|
||||
}
|
||||
|
||||
/* take care of the where clause */
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
if (length(pstate->p_rtable) != 2) /* naughty, naughty... */
|
||||
elog(ERROR, "Rule WHERE condition may not contain references to other relations");
|
||||
|
||||
/* save info about sublinks in where clause */
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
||||
/*
|
||||
* 'instead nothing' rules with a qualification need a query
|
||||
* rangetable so the rewrite handler can add the negated rule
|
||||
* qualification to the original query. We create a query with the new
|
||||
* command type CMD_NOTHING here that is treated special by the
|
||||
* command type CMD_NOTHING here that is treated specially by the
|
||||
* rewrite system.
|
||||
*/
|
||||
if (stmt->actions == NIL)
|
||||
|
@ -1445,54 +1503,95 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
|||
Query *nothing_qry = makeNode(Query);
|
||||
|
||||
nothing_qry->commandType = CMD_NOTHING;
|
||||
|
||||
addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*OLD*", NULL),
|
||||
FALSE, FALSE, FALSE);
|
||||
addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
FALSE, FALSE, FALSE);
|
||||
|
||||
nothing_qry->rtable = pstate->p_rtable;
|
||||
nothing_qry->jointree = NIL; /* no join actually wanted */
|
||||
|
||||
stmt->actions = lappend(NIL, nothing_qry);
|
||||
}
|
||||
|
||||
actions = stmt->actions;
|
||||
|
||||
/*
|
||||
* transform each statment, like parse_analyze()
|
||||
*/
|
||||
while (actions != NIL)
|
||||
else
|
||||
{
|
||||
List *actions;
|
||||
|
||||
/*
|
||||
* NOTE: 'OLD' must always have a varno equal to 1 and 'NEW'
|
||||
* equal to 2.
|
||||
* transform each statement, like parse_analyze()
|
||||
*/
|
||||
addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*OLD*", NULL),
|
||||
FALSE, FALSE, FALSE);
|
||||
addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
FALSE, FALSE, FALSE);
|
||||
foreach(actions, stmt->actions)
|
||||
{
|
||||
ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
|
||||
Query *sub_qry;
|
||||
bool has_old,
|
||||
has_new;
|
||||
|
||||
pstate->p_last_resno = 1;
|
||||
pstate->p_is_rule = true; /* for expand all */
|
||||
pstate->p_hasAggs = false;
|
||||
/*
|
||||
* Set up OLD/NEW in the rtable for this statement. The entries
|
||||
* are marked not inFromCl because we don't want them to be
|
||||
* referred to by unqualified field names nor "*" in the rule
|
||||
* actions. We don't need to add them to the jointree for
|
||||
* qualified-name lookup, either (see qualifiedNameToVar()).
|
||||
*/
|
||||
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
|
||||
makeAttr("*OLD*", NULL),
|
||||
false, false);
|
||||
newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
false, false);
|
||||
|
||||
action = (Query *) lfirst(actions);
|
||||
if (action->commandType != CMD_NOTHING)
|
||||
lfirst(actions) = transformStmt(pstate, lfirst(actions));
|
||||
actions = lnext(actions);
|
||||
/* Transform the rule action statement */
|
||||
sub_qry = transformStmt(sub_pstate, lfirst(actions));
|
||||
|
||||
/*
|
||||
* Validate action's use of OLD/NEW, qual too
|
||||
*/
|
||||
has_old =
|
||||
rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) ||
|
||||
rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0);
|
||||
has_new =
|
||||
rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) ||
|
||||
rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0);
|
||||
|
||||
switch (stmt->event)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
if (has_old)
|
||||
elog(ERROR, "ON SELECT rule may not use OLD");
|
||||
if (has_new)
|
||||
elog(ERROR, "ON SELECT rule may not use NEW");
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
/* both are OK */
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
if (has_old)
|
||||
elog(ERROR, "ON INSERT rule may not use OLD");
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
if (has_new)
|
||||
elog(ERROR, "ON DELETE rule may not use NEW");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "transformRuleStmt: unexpected event type %d",
|
||||
(int) stmt->event);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* For efficiency's sake, add OLD to the rule action's jointree
|
||||
* only if it was actually referenced in the statement or qual.
|
||||
* NEW is not really a relation and should never be added.
|
||||
*/
|
||||
if (has_old)
|
||||
{
|
||||
addRTEtoJoinTree(sub_pstate, oldrte);
|
||||
sub_qry->jointree = sub_pstate->p_jointree;
|
||||
}
|
||||
|
||||
lfirst(actions) = sub_qry;
|
||||
|
||||
release_pstate_resources(sub_pstate);
|
||||
pfree(sub_pstate);
|
||||
}
|
||||
}
|
||||
|
||||
/* take care of the where clause */
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
||||
qry->utilityStmt = (Node *) stmt;
|
||||
return qry;
|
||||
}
|
||||
|
||||
|
@ -1558,6 +1657,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||
qry->intersectClause = stmt->intersectClause;
|
||||
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
|
||||
if (stmt->forUpdate != NULL)
|
||||
transformForUpdate(qry, stmt->forUpdate);
|
||||
|
@ -1585,17 +1685,17 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
|||
* do this with REPLACE in POSTQUEL so we keep the feature.
|
||||
*/
|
||||
makeRangeTable(pstate, stmt->fromClause);
|
||||
setTargetTable(pstate, stmt->relname, stmt->inh);
|
||||
setTargetTable(pstate, stmt->relname, stmt->inh, true);
|
||||
|
||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||
|
||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
|
@ -1689,7 +1789,7 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
|
|||
transformColumnType(pstate, (ColumnDef *) stmt->def);
|
||||
break;
|
||||
case 'C':
|
||||
if (stmt->def && nodeTag(stmt->def) == T_FkConstraint)
|
||||
if (stmt->def && IsA(stmt->def, FkConstraint))
|
||||
{
|
||||
CreateTrigStmt *fk_trigger;
|
||||
List *fk_attr;
|
||||
|
@ -2085,7 +2185,7 @@ transformForUpdate(Query *qry, List *forUpdate)
|
|||
i++;
|
||||
}
|
||||
if (l2 == NULL)
|
||||
elog(ERROR, "FOR UPDATE: relation '%s' not found in FROM clause",
|
||||
elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
|
||||
relname);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.188 2000/09/12 05:09:44 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.189 2000/09/12 21:07:01 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
|
@ -36,6 +36,7 @@
|
|||
#include <ctype.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/htup.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/catname.h"
|
||||
|
@ -77,15 +78,10 @@ static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr);
|
|||
static Node *makeTypeCast(Node *arg, TypeName *typename);
|
||||
static Node *makeRowExpr(char *opr, List *largs, List *rargs);
|
||||
static void mapTargetColumns(List *source, List *target);
|
||||
static void param_type_init(Oid *typev, int nargs);
|
||||
static bool exprIsNullConstant(Node *arg);
|
||||
static Node *doNegate(Node *n);
|
||||
static void doNegateFloat(Value *v);
|
||||
|
||||
/* old versions of flex define this as a macro */
|
||||
#if defined(yywrap)
|
||||
#undef yywrap
|
||||
#endif /* yywrap */
|
||||
%}
|
||||
|
||||
|
||||
|
@ -95,6 +91,7 @@ static void doNegateFloat(Value *v);
|
|||
char chr;
|
||||
char *str;
|
||||
bool boolean;
|
||||
JoinType jtype;
|
||||
List *list;
|
||||
Node *node;
|
||||
Value *value;
|
||||
|
@ -108,7 +105,6 @@ static void doNegateFloat(Value *v);
|
|||
JoinExpr *jexpr;
|
||||
IndexElem *ielem;
|
||||
RangeVar *range;
|
||||
RelExpr *relexp;
|
||||
A_Indices *aind;
|
||||
ResTarget *target;
|
||||
ParamNo *paramno;
|
||||
|
@ -194,19 +190,8 @@ static void doNegateFloat(Value *v);
|
|||
%type <boolean> opt_table
|
||||
%type <boolean> opt_chain, opt_trans
|
||||
|
||||
%type <jexpr> from_expr, join_clause, join_expr
|
||||
%type <jexpr> join_clause_with_union, join_expr_with_union
|
||||
%type <node> join_outer, join_qual
|
||||
%type <ival> join_type
|
||||
%type <list> using_list
|
||||
%type <ident> using_expr
|
||||
/***
|
||||
#ifdef ENABLE_ORACLE_JOIN_SYNTAX
|
||||
%type <list> oracle_list
|
||||
%type <jexpr> oracle_expr
|
||||
%type <boolean> oracle_outer
|
||||
#endif
|
||||
***/
|
||||
%type <jtype> join_type
|
||||
|
||||
%type <list> extract_list, position_list
|
||||
%type <list> substr_list, substr_from, substr_for, trim_list
|
||||
|
@ -246,8 +231,9 @@ static void doNegateFloat(Value *v);
|
|||
%type <attr> event_object, attr, alias_clause
|
||||
%type <sortgroupby> sortby
|
||||
%type <ielem> index_elem, func_index
|
||||
%type <range> table_expr
|
||||
%type <relexp> relation_expr
|
||||
%type <node> table_ref
|
||||
%type <jexpr> joined_table
|
||||
%type <range> relation_expr
|
||||
%type <target> target_el, update_target_el
|
||||
%type <paramno> ParamNo
|
||||
|
||||
|
@ -356,6 +342,12 @@ static void doNegateFloat(Value *v);
|
|||
TEMP, TOAST, TRUNCATE, TRUSTED,
|
||||
UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
|
||||
|
||||
/* The grammar thinks these are keywords, but they are not in the keywords.c
|
||||
* list and so can never be entered directly. The filter in parser.c
|
||||
* creates these tokens when required.
|
||||
*/
|
||||
%token UNIONJOIN
|
||||
|
||||
/* Special keywords, not in the query language - see the "lex" file */
|
||||
%token <str> IDENT, FCONST, SCONST, Op
|
||||
%token <ival> ICONST, PARAM
|
||||
|
@ -364,7 +356,9 @@ static void doNegateFloat(Value *v);
|
|||
%token OP
|
||||
|
||||
/* precedence: lowest to highest */
|
||||
%left UNION INTERSECT EXCEPT
|
||||
%left UNION EXCEPT
|
||||
%left INTERSECT
|
||||
%left JOIN UNIONJOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
|
||||
%left OR
|
||||
%left AND
|
||||
%right NOT
|
||||
|
@ -800,7 +794,7 @@ VariableSetStmt: SET ColId TO var_value
|
|||
n->value = $3;
|
||||
$$ = (Node *) n;
|
||||
#else
|
||||
elog(ERROR, "SET NAMES is not supported.");
|
||||
elog(ERROR, "SET NAMES is not supported");
|
||||
#endif
|
||||
}
|
||||
;
|
||||
|
@ -1031,7 +1025,6 @@ AlterTableStmt:
|
|||
n->relname = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
|
||||
/* ALTER TABLE <name> OWNER TO UserId */
|
||||
| ALTER TABLE relation_name OWNER TO UserId
|
||||
{
|
||||
|
@ -2956,7 +2949,7 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_location createdb
|
|||
CreatedbStmt *n;
|
||||
|
||||
if ($5 == NULL && $6 == -1)
|
||||
elog(ERROR, "CREATE DATABASE WITH requires at least one option.");
|
||||
elog(ERROR, "CREATE DATABASE WITH requires at least one option");
|
||||
|
||||
n = makeNode(CreatedbStmt);
|
||||
n->dbname = $3;
|
||||
|
@ -3465,7 +3458,7 @@ SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
|
|||
/* This rule parses Select statements that can appear within set operations,
|
||||
* including UNION, INTERSECT and EXCEPT. '(' and ')' can be used to specify
|
||||
* the ordering of the set operations. Without '(' and ')' we want the
|
||||
* operations to be left associative.
|
||||
* operations to be ordered per the precedence specs at the head of this file.
|
||||
*
|
||||
* Note that sort clauses cannot be included at this level --- a sort clause
|
||||
* can only appear at the end of the complete Select, and it will be handled
|
||||
|
@ -3486,10 +3479,12 @@ select_clause: '(' select_clause ')'
|
|||
{
|
||||
$$ = $1;
|
||||
}
|
||||
| select_clause EXCEPT select_clause
|
||||
| select_clause EXCEPT opt_all select_clause
|
||||
{
|
||||
$$ = (Node *)makeA_Expr(AND,NULL,$1,
|
||||
makeA_Expr(NOT,NULL,NULL,$3));
|
||||
makeA_Expr(NOT,NULL,NULL,$4));
|
||||
if ($3)
|
||||
elog(ERROR, "EXCEPT ALL is not implemented yet");
|
||||
}
|
||||
| select_clause UNION opt_all select_clause
|
||||
{
|
||||
|
@ -3506,9 +3501,11 @@ select_clause: '(' select_clause ')'
|
|||
}
|
||||
$$ = (Node *)makeA_Expr(OR,NULL,$1,$4);
|
||||
}
|
||||
| select_clause INTERSECT select_clause
|
||||
| select_clause INTERSECT opt_all select_clause
|
||||
{
|
||||
$$ = (Node *)makeA_Expr(AND,NULL,$1,$3);
|
||||
$$ = (Node *)makeA_Expr(AND,NULL,$1,$4);
|
||||
if ($3)
|
||||
elog(ERROR, "INTERSECT ALL is not implemented yet");
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -3741,64 +3738,150 @@ update_list: OF va_list { $$ = $2; }
|
|||
*****************************************************************************/
|
||||
|
||||
from_clause: FROM from_list { $$ = $2; }
|
||||
/***
|
||||
#ifdef ENABLE_ORACLE_JOIN_SYNTAX
|
||||
| FROM oracle_list { $$ = $2; }
|
||||
#endif
|
||||
***/
|
||||
| FROM from_expr { $$ = lcons($2, NIL); }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
from_list: from_list ',' table_expr { $$ = lappend($1, $3); }
|
||||
| table_expr { $$ = lcons($1, NIL); }
|
||||
from_list: from_list ',' table_ref { $$ = lappend($1, $3); }
|
||||
| table_ref { $$ = lcons($1, NIL); }
|
||||
;
|
||||
|
||||
/***********
|
||||
* This results in one shift/reduce conflict, presumably due to the trailing "(+)"
|
||||
* - Thomas 1999-09-20
|
||||
/*
|
||||
* table_ref is where an alias clause can be attached. Note we cannot make
|
||||
* alias_clause have an empty production because that causes parse conflicts
|
||||
* between table_ref := '(' joined_table ')' alias_clause
|
||||
* and joined_table := '(' joined_table ')'. So, we must have the
|
||||
* redundant-looking productions here instead.
|
||||
*/
|
||||
table_ref: relation_expr
|
||||
{
|
||||
$$ = (Node *) $1;
|
||||
}
|
||||
| relation_expr alias_clause
|
||||
{
|
||||
$1->name = $2;
|
||||
$$ = (Node *) $1;
|
||||
}
|
||||
| '(' select_clause ')'
|
||||
{
|
||||
RangeSubselect *n = makeNode(RangeSubselect);
|
||||
n->subquery = $2;
|
||||
n->name = NULL;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| '(' select_clause ')' alias_clause
|
||||
{
|
||||
RangeSubselect *n = makeNode(RangeSubselect);
|
||||
n->subquery = $2;
|
||||
n->name = $4;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| joined_table
|
||||
{
|
||||
$$ = (Node *) $1;
|
||||
}
|
||||
| '(' joined_table ')' alias_clause
|
||||
{
|
||||
$2->alias = $4;
|
||||
$$ = (Node *) $2;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* It may seem silly to separate joined_table from table_ref, but there is
|
||||
* method in SQL92's madness: if you don't do it this way you get reduce-
|
||||
* reduce conflicts, because it's not clear to the parser generator whether
|
||||
* to expect alias_clause after ')' or not. For the same reason we must
|
||||
* treat 'JOIN' and 'join_type JOIN' separately, rather than allowing
|
||||
* join_type to expand to empty; if we try it, the parser generator can't
|
||||
* figure out when to reduce an empty join_type right after table_ref.
|
||||
*
|
||||
#ifdef ENABLE_ORACLE_JOIN_SYNTAX
|
||||
oracle_list: oracle_expr { $$ = lcons($1, NIL); }
|
||||
;
|
||||
* Note that a CROSS JOIN is the same as an unqualified
|
||||
* INNER JOIN, and an INNER JOIN/ON has the same shape
|
||||
* but a qualification expression to limit membership.
|
||||
* A NATURAL JOIN implicitly matches column names between
|
||||
* tables and the shape is determined by which columns are
|
||||
* in common. We'll collect columns during the later transformations.
|
||||
*/
|
||||
|
||||
oracle_expr: ColId ',' ColId oracle_outer
|
||||
joined_table: '(' joined_table ')'
|
||||
{
|
||||
elog(ERROR,"Oracle OUTER JOIN not yet supported");
|
||||
$$ = NULL;
|
||||
$$ = $2;
|
||||
}
|
||||
| oracle_outer ColId ',' ColId
|
||||
| table_ref CROSS JOIN table_ref
|
||||
{
|
||||
elog(ERROR,"Oracle OUTER JOIN not yet supported");
|
||||
$$ = NULL;
|
||||
/* CROSS JOIN is same as unqualified inner join */
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = JOIN_INNER;
|
||||
n->isNatural = FALSE;
|
||||
n->larg = $1;
|
||||
n->rarg = $4;
|
||||
n->using = NIL;
|
||||
n->quals = NULL;
|
||||
$$ = n;
|
||||
}
|
||||
;
|
||||
|
||||
oracle_outer: '(' '+' ')' { $$ = TRUE; }
|
||||
;
|
||||
#endif
|
||||
***********/
|
||||
|
||||
from_expr: '(' join_clause_with_union ')' alias_clause
|
||||
| table_ref UNIONJOIN table_ref
|
||||
{
|
||||
JoinExpr *j = $2;
|
||||
j->alias = $4;
|
||||
$$ = j;
|
||||
/* UNION JOIN is made into 1 token to avoid shift/reduce
|
||||
* conflict against regular UNION keyword.
|
||||
*/
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = JOIN_UNION;
|
||||
n->isNatural = FALSE;
|
||||
n->larg = $1;
|
||||
n->rarg = $3;
|
||||
n->using = NIL;
|
||||
n->quals = NULL;
|
||||
$$ = n;
|
||||
}
|
||||
| join_clause
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
table_expr: relation_expr alias_clause
|
||||
| table_ref join_type JOIN table_ref join_qual
|
||||
{
|
||||
$$ = makeNode(RangeVar);
|
||||
$$->relExpr = $1;
|
||||
$$->name = $2;
|
||||
|
||||
#ifdef DISABLE_JOIN_SYNTAX
|
||||
if (($2 != NULL) && ($2->attrs != NULL))
|
||||
elog(ERROR, "Column aliases in table expressions not yet supported");
|
||||
#endif
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = $2;
|
||||
n->isNatural = FALSE;
|
||||
n->larg = $1;
|
||||
n->rarg = $4;
|
||||
if ($5 != NULL && IsA($5, List))
|
||||
n->using = (List *) $5; /* USING clause */
|
||||
else
|
||||
n->quals = $5; /* ON clause */
|
||||
$$ = n;
|
||||
}
|
||||
| table_ref JOIN table_ref join_qual
|
||||
{
|
||||
/* letting join_type reduce to empty doesn't work */
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = JOIN_INNER;
|
||||
n->isNatural = FALSE;
|
||||
n->larg = $1;
|
||||
n->rarg = $3;
|
||||
if ($4 != NULL && IsA($4, List))
|
||||
n->using = (List *) $4; /* USING clause */
|
||||
else
|
||||
n->quals = $4; /* ON clause */
|
||||
$$ = n;
|
||||
}
|
||||
| table_ref NATURAL join_type JOIN table_ref
|
||||
{
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = $3;
|
||||
n->isNatural = TRUE;
|
||||
n->larg = $1;
|
||||
n->rarg = $5;
|
||||
n->using = NIL; /* figure out which columns later... */
|
||||
n->quals = NULL; /* fill later */
|
||||
$$ = n;
|
||||
}
|
||||
| table_ref NATURAL JOIN table_ref
|
||||
{
|
||||
/* letting join_type reduce to empty doesn't work */
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = JOIN_INNER;
|
||||
n->isNatural = TRUE;
|
||||
n->larg = $1;
|
||||
n->rarg = $4;
|
||||
n->using = NIL; /* figure out which columns later... */
|
||||
n->quals = NULL; /* fill later */
|
||||
$$ = n;
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -3824,102 +3907,17 @@ alias_clause: AS ColId '(' name_list ')'
|
|||
$$ = makeNode(Attr);
|
||||
$$->relname = $1;
|
||||
}
|
||||
| /*EMPTY*/
|
||||
{
|
||||
$$ = NULL; /* no qualifiers */
|
||||
}
|
||||
;
|
||||
|
||||
/* A UNION JOIN is the same as a FULL OUTER JOIN which *omits*
|
||||
* all result rows which would have matched on an INNER JOIN.
|
||||
* Syntactically, must enclose the UNION JOIN in parens to avoid
|
||||
* conflicts with SELECT/UNION.
|
||||
*/
|
||||
join_clause: join_clause join_expr
|
||||
{
|
||||
$2->larg = (Node *)$1;
|
||||
$$ = $2;
|
||||
}
|
||||
| table_expr join_expr
|
||||
{
|
||||
$2->larg = (Node *)$1;
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
/* This is everything but the left side of a join.
|
||||
* Note that a CROSS JOIN is the same as an unqualified
|
||||
* INNER JOIN, and an INNER JOIN/ON has the same shape
|
||||
* but a qualification expression to limit membership.
|
||||
* A NATURAL JOIN implicitly matches column names between
|
||||
* tables and the shape is determined by which columns are
|
||||
* in common. We'll collect columns during the later transformations.
|
||||
*/
|
||||
join_expr: join_type JOIN table_expr join_qual
|
||||
{
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = $1;
|
||||
n->rarg = (Node *)$3;
|
||||
n->quals = (List *)$4;
|
||||
$$ = n;
|
||||
}
|
||||
| NATURAL join_type JOIN table_expr
|
||||
{
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = $2;
|
||||
n->isNatural = TRUE;
|
||||
n->rarg = (Node *)$4;
|
||||
n->quals = NULL; /* figure out which columns later... */
|
||||
$$ = n;
|
||||
}
|
||||
| CROSS JOIN table_expr
|
||||
{
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = INNER_P;
|
||||
n->isNatural = FALSE;
|
||||
n->rarg = (Node *)$3;
|
||||
n->quals = NULL;
|
||||
$$ = n;
|
||||
}
|
||||
;
|
||||
|
||||
join_clause_with_union: join_clause_with_union join_expr_with_union
|
||||
{
|
||||
$2->larg = (Node *)$1;
|
||||
$$ = $2;
|
||||
}
|
||||
| table_expr join_expr_with_union
|
||||
{
|
||||
$2->larg = (Node *)$1;
|
||||
$$ = $2;
|
||||
}
|
||||
;
|
||||
|
||||
join_expr_with_union: join_expr
|
||||
{ $$ = $1; }
|
||||
| UNION JOIN table_expr
|
||||
{
|
||||
JoinExpr *n = makeNode(JoinExpr);
|
||||
n->jointype = UNION;
|
||||
n->rarg = (Node *)$3;
|
||||
n->quals = NULL;
|
||||
$$ = n;
|
||||
|
||||
elog(ERROR,"UNION JOIN not yet implemented");
|
||||
}
|
||||
join_type: FULL join_outer { $$ = JOIN_FULL; }
|
||||
| LEFT join_outer { $$ = JOIN_LEFT; }
|
||||
| RIGHT join_outer { $$ = JOIN_RIGHT; }
|
||||
| INNER_P { $$ = JOIN_INNER; }
|
||||
;
|
||||
|
||||
/* OUTER is just noise... */
|
||||
join_type: FULL join_outer { $$ = FULL; }
|
||||
| LEFT join_outer { $$ = LEFT; }
|
||||
| RIGHT join_outer { $$ = RIGHT; }
|
||||
| OUTER_P { $$ = LEFT; }
|
||||
| INNER_P { $$ = INNER_P; }
|
||||
| /*EMPTY*/ { $$ = INNER_P; }
|
||||
;
|
||||
|
||||
join_outer: OUTER_P { $$ = NULL; }
|
||||
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
|
||||
| /*EMPTY*/ { $$ = NULL; }
|
||||
;
|
||||
|
||||
/* JOIN qualification clauses
|
||||
|
@ -3927,60 +3925,43 @@ join_outer: OUTER_P { $$ = NULL; }
|
|||
* USING ( column list ) allows only unqualified column names,
|
||||
* which must match between tables.
|
||||
* ON expr allows more general qualifications.
|
||||
* - thomas 1999-01-07
|
||||
*
|
||||
* We return USING as a List node, while an ON-expr will not be a List.
|
||||
*/
|
||||
|
||||
join_qual: USING '(' using_list ')' { $$ = (Node *)$3; }
|
||||
| ON a_expr { $$ = (Node *)$2; }
|
||||
join_qual: USING '(' name_list ')' { $$ = (Node *) $3; }
|
||||
| ON a_expr { $$ = $2; }
|
||||
;
|
||||
|
||||
using_list: using_list ',' using_expr { $$ = lappend($1, $3); }
|
||||
| using_expr { $$ = lcons($1, NIL); }
|
||||
;
|
||||
|
||||
using_expr: ColId
|
||||
{
|
||||
/* could be a column name or a relation_name */
|
||||
Ident *n = makeNode(Ident);
|
||||
n->name = $1;
|
||||
n->indirection = NULL;
|
||||
$$ = n;
|
||||
}
|
||||
;
|
||||
|
||||
where_clause: WHERE a_expr { $$ = $2; }
|
||||
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
|
||||
;
|
||||
|
||||
relation_expr: relation_name
|
||||
{
|
||||
/* default inheritance */
|
||||
$$ = makeNode(RelExpr);
|
||||
$$ = makeNode(RangeVar);
|
||||
$$->relname = $1;
|
||||
$$->inh = SQL_inheritance;
|
||||
$$->name = NULL;
|
||||
}
|
||||
| relation_name '*' %prec '='
|
||||
{
|
||||
/* inheritance query */
|
||||
$$ = makeNode(RelExpr);
|
||||
$$ = makeNode(RangeVar);
|
||||
$$->relname = $1;
|
||||
$$->inh = TRUE;
|
||||
$$->name = NULL;
|
||||
}
|
||||
| ONLY relation_name %prec '='
|
||||
{
|
||||
/* no inheritance */
|
||||
$$ = makeNode(RelExpr);
|
||||
$$ = makeNode(RangeVar);
|
||||
$$->relname = $2;
|
||||
$$->inh = FALSE;
|
||||
$$->name = NULL;
|
||||
}
|
||||
;
|
||||
|
||||
opt_array_bounds: '[' ']' opt_array_bounds
|
||||
{ $$ = lcons(makeInteger(-1), $3); }
|
||||
| '[' Iconst ']' opt_array_bounds
|
||||
{ $$ = lcons(makeInteger($2), $4); }
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
where_clause: WHERE a_expr { $$ = $2; }
|
||||
| /*EMPTY*/ { $$ = NULL; /* no qualifiers */ }
|
||||
;
|
||||
|
||||
|
||||
|
@ -4023,6 +4004,14 @@ Typename: SimpleTypename opt_array_bounds
|
|||
}
|
||||
;
|
||||
|
||||
opt_array_bounds: '[' ']' opt_array_bounds
|
||||
{ $$ = lcons(makeInteger(-1), $3); }
|
||||
| '[' Iconst ']' opt_array_bounds
|
||||
{ $$ = lcons(makeInteger($2), $4); }
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
;
|
||||
|
||||
SimpleTypename: ConstTypename
|
||||
| ConstInterval
|
||||
;
|
||||
|
@ -6024,29 +6013,19 @@ xlateSqlType(char *name)
|
|||
|
||||
void parser_init(Oid *typev, int nargs)
|
||||
{
|
||||
saved_relname[0] = '\0';
|
||||
QueryIsRule = FALSE;
|
||||
saved_relname[0]= '\0';
|
||||
|
||||
param_type_init(typev, nargs);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* param_type_init()
|
||||
*
|
||||
* Keep enough information around to fill out the type of param nodes
|
||||
* used in postquel functions
|
||||
*/
|
||||
static void
|
||||
param_type_init(Oid *typev, int nargs)
|
||||
{
|
||||
pfunc_num_args = nargs;
|
||||
/*
|
||||
* Keep enough information around to fill out the type of param nodes
|
||||
* used in postquel functions
|
||||
*/
|
||||
param_type_info = typev;
|
||||
pfunc_num_args = nargs;
|
||||
}
|
||||
|
||||
Oid param_type(int t)
|
||||
{
|
||||
if ((t > pfunc_num_args) || (t == 0))
|
||||
if ((t > pfunc_num_args) || (t <= 0))
|
||||
return InvalidOid;
|
||||
return param_type_info[t - 1];
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.39 2000/07/17 03:05:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.40 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -152,6 +152,11 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
|||
*/
|
||||
if (contain_agg_clause(qry->qual))
|
||||
elog(ERROR, "Aggregates not allowed in WHERE clause");
|
||||
/*
|
||||
* ON-conditions in JOIN expressions are like WHERE clauses.
|
||||
*/
|
||||
if (contain_agg_clause((Node *) qry->jointree))
|
||||
elog(ERROR, "Aggregates not allowed in JOIN conditions");
|
||||
|
||||
/*
|
||||
* No aggregates allowed in GROUP BY clauses, either.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.82 2000/08/08 15:42:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.83 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -157,41 +157,51 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
|||
{
|
||||
case OP:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
|
||||
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
|
||||
result = (Node *) make_op(a->opname, lexpr, rexpr);
|
||||
}
|
||||
break;
|
||||
case ISNULL:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
"nullvalue",
|
||||
lcons(lexpr, NIL),
|
||||
false, false,
|
||||
&pstate->p_last_resno,
|
||||
precedence);
|
||||
}
|
||||
break;
|
||||
case NOTNULL:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
"nonnullvalue",
|
||||
lcons(lexpr, NIL),
|
||||
false, false,
|
||||
&pstate->p_last_resno,
|
||||
precedence);
|
||||
}
|
||||
break;
|
||||
case AND:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
Expr *expr = makeNode(Expr);
|
||||
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
|
||||
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
|
||||
|
||||
if (exprType(lexpr) != BOOLOID)
|
||||
elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
|
||||
|
@ -209,9 +219,13 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
|||
break;
|
||||
case OR:
|
||||
{
|
||||
Node *lexpr = transformExpr(pstate,
|
||||
a->lexpr,
|
||||
precedence);
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
Expr *expr = makeNode(Expr);
|
||||
Node *lexpr = transformExpr(pstate, a->lexpr, precedence);
|
||||
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
|
||||
|
||||
if (exprType(lexpr) != BOOLOID)
|
||||
elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
|
||||
|
@ -227,8 +241,10 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
|||
break;
|
||||
case NOT:
|
||||
{
|
||||
Node *rexpr = transformExpr(pstate,
|
||||
a->rexpr,
|
||||
precedence);
|
||||
Expr *expr = makeNode(Expr);
|
||||
Node *rexpr = transformExpr(pstate, a->rexpr, precedence);
|
||||
|
||||
if (exprType(rexpr) != BOOLOID)
|
||||
elog(ERROR, "argument to NOT is type '%s', not '%s'",
|
||||
|
@ -254,13 +270,14 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
|||
|
||||
/* transform the list of arguments */
|
||||
foreach(args, fn->args)
|
||||
lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence);
|
||||
lfirst(args) = transformExpr(pstate,
|
||||
(Node *) lfirst(args),
|
||||
precedence);
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
fn->funcname,
|
||||
fn->args,
|
||||
fn->agg_star,
|
||||
fn->agg_distinct,
|
||||
&pstate->p_last_resno,
|
||||
precedence);
|
||||
break;
|
||||
}
|
||||
|
@ -609,8 +626,7 @@ transformAttr(ParseState *pstate, Attr *att, int precedence)
|
|||
{
|
||||
Node *basenode;
|
||||
|
||||
basenode = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,
|
||||
precedence);
|
||||
basenode = ParseNestedFuncOrColumn(pstate, att, precedence);
|
||||
return transformIndirection(pstate, basenode, att->indirection);
|
||||
}
|
||||
|
||||
|
@ -618,7 +634,6 @@ static Node *
|
|||
transformIdent(ParseState *pstate, Ident *ident, int precedence)
|
||||
{
|
||||
Node *result = NULL;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
/*
|
||||
* try to find the ident as a relation ... but not if subscripts
|
||||
|
@ -634,14 +649,10 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
|
|||
if (result == NULL || precedence == EXPR_COLUMN_FIRST)
|
||||
{
|
||||
/* try to find the ident as a column */
|
||||
if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL)
|
||||
{
|
||||
/* Convert it to a fully qualified Attr, and transform that */
|
||||
Attr *att = makeAttr(rte->eref->relname, ident->name);
|
||||
Node *var = colnameToVar(pstate, ident->name);
|
||||
|
||||
att->indirection = ident->indirection;
|
||||
return transformAttr(pstate, att, precedence);
|
||||
}
|
||||
if (var != NULL)
|
||||
result = transformIndirection(pstate, var, ident->indirection);
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.89 2000/08/24 03:29:05 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.90 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -64,19 +64,20 @@ static Oid agg_select_candidate(Oid typeid, CandidateList candidates);
|
|||
** a tree with of Iter and Func nodes.
|
||||
*/
|
||||
Node *
|
||||
ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int precedence)
|
||||
ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
|
||||
{
|
||||
List *mutator_iter;
|
||||
Node *retval = NULL;
|
||||
|
||||
if (attr->paramNo != NULL)
|
||||
{
|
||||
Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST);
|
||||
Param *param = (Param *) transformExpr(pstate,
|
||||
(Node *) attr->paramNo,
|
||||
EXPR_RELATION_FIRST);
|
||||
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
|
||||
lcons(param, NIL),
|
||||
false, false,
|
||||
curr_resno,
|
||||
precedence);
|
||||
}
|
||||
else
|
||||
|
@ -88,7 +89,6 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
|
|||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
|
||||
lcons(ident, NIL),
|
||||
false, false,
|
||||
curr_resno,
|
||||
precedence);
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,6 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int *curr_resno, int pre
|
|||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
|
||||
lcons(retval, NIL),
|
||||
false, false,
|
||||
curr_resno,
|
||||
precedence);
|
||||
}
|
||||
|
||||
|
@ -241,17 +240,15 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
|
|||
Node *
|
||||
ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
bool agg_star, bool agg_distinct,
|
||||
int *curr_resno, int precedence)
|
||||
int precedence)
|
||||
{
|
||||
Oid rettype = InvalidOid;
|
||||
Oid argrelid = InvalidOid;
|
||||
Oid funcid = InvalidOid;
|
||||
List *i = NIL;
|
||||
Node *first_arg = NULL;
|
||||
char *relname = NULL;
|
||||
char *refname = NULL;
|
||||
char *refname;
|
||||
Relation rd;
|
||||
Oid relid;
|
||||
int nargs = length(fargs);
|
||||
Func *funcnode;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
|
@ -283,81 +280,17 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||
if (IsA(first_arg, Ident) && ((Ident *) first_arg)->isRel)
|
||||
{
|
||||
Ident *ident = (Ident *) first_arg;
|
||||
RangeTblEntry *rte;
|
||||
AttrNumber attnum;
|
||||
|
||||
/*
|
||||
* first arg is a relation. This could be a projection.
|
||||
*/
|
||||
refname = ident->name;
|
||||
|
||||
rte = refnameRangeTableEntry(pstate, refname);
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, refname,
|
||||
makeAttr(refname, NULL),
|
||||
FALSE, FALSE, TRUE);
|
||||
warnAutoRange(pstate, refname);
|
||||
}
|
||||
retval = qualifiedNameToVar(pstate, refname, funcname, true);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
relname = rte->relname;
|
||||
relid = rte->relid;
|
||||
attnum = InvalidAttrNumber;
|
||||
|
||||
/*
|
||||
* If the attr isn't a set, just make a var for it. If it is
|
||||
* a set, treat it like a function and drop through. Look
|
||||
* through the explicit column list first, since we now allow
|
||||
* column aliases. - thomas 2000-02-07
|
||||
*/
|
||||
if (rte->eref->attrs != NULL)
|
||||
{
|
||||
List *c;
|
||||
|
||||
/*
|
||||
* start counting attributes/columns from one. zero is
|
||||
* reserved for InvalidAttrNumber. - thomas 2000-01-27
|
||||
*/
|
||||
int i = 1;
|
||||
|
||||
foreach(c, rte->eref->attrs)
|
||||
{
|
||||
char *colname = strVal(lfirst(c));
|
||||
|
||||
/* found a match? */
|
||||
if (strcmp(colname, funcname) == 0)
|
||||
{
|
||||
char *basename = get_attname(relid, i);
|
||||
|
||||
if (basename != NULL)
|
||||
{
|
||||
funcname = basename;
|
||||
attnum = i;
|
||||
}
|
||||
|
||||
/*
|
||||
* attnum was initialized to InvalidAttrNumber
|
||||
* earlier, so no need to reset it if the above
|
||||
* test fails. - thomas 2000-02-07
|
||||
*/
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (attnum == InvalidAttrNumber)
|
||||
attnum = specialAttNum(funcname);
|
||||
}
|
||||
else
|
||||
attnum = get_attnum(relid, funcname);
|
||||
|
||||
if (attnum != InvalidAttrNumber)
|
||||
{
|
||||
return (Node *) make_var(pstate,
|
||||
relid,
|
||||
refname,
|
||||
funcname);
|
||||
}
|
||||
/* else drop through - attr is a set */
|
||||
/* else drop through - attr is a set or function */
|
||||
}
|
||||
else if (ISCOMPLEX(exprType(first_arg)))
|
||||
{
|
||||
|
@ -376,10 +309,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||
toid = exprType(first_arg);
|
||||
rd = heap_openr_nofail(typeidTypeName(toid));
|
||||
if (RelationIsValid(rd))
|
||||
{
|
||||
relname = RelationGetRelationName(rd);
|
||||
heap_close(rd, NoLock);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Type '%s' is not a relation type",
|
||||
typeidTypeName(toid));
|
||||
|
@ -506,17 +436,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||
|
||||
rte = refnameRangeTableEntry(pstate, refname);
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, refname,
|
||||
makeAttr(refname, NULL),
|
||||
FALSE, FALSE, TRUE);
|
||||
warnAutoRange(pstate, refname);
|
||||
}
|
||||
rte = addImplicitRTE(pstate, refname);
|
||||
|
||||
relname = rte->relname;
|
||||
|
||||
vnum = refnameRangeTablePosn(pstate, rte->eref->relname,
|
||||
&sublevels_up);
|
||||
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
/*
|
||||
* for func(relname), the param to the function is the tuple
|
||||
|
@ -525,7 +447,8 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||
* but has varattno == 0 to signal that the whole tuple is the
|
||||
* argument.
|
||||
*/
|
||||
toid = typeTypeId(typenameType(relname));
|
||||
toid = typeTypeId(typenameType(rte->relname));
|
||||
|
||||
/* replace it in the arg list */
|
||||
lfirst(i) = makeVar(vnum, 0, toid, -1, sublevels_up);
|
||||
}
|
||||
|
@ -666,16 +589,6 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
|||
/* perform the necessary typecasting of arguments */
|
||||
make_arguments(pstate, nargs, fargs, oid_array, true_oid_array);
|
||||
|
||||
/*
|
||||
* Special checks to disallow sequence functions with side-effects
|
||||
* in WHERE clauses. This is pretty much of a hack; why disallow these
|
||||
* when we have no way to check for side-effects of user-defined fns?
|
||||
*/
|
||||
if (funcid == F_NEXTVAL && pstate->p_in_where_clause)
|
||||
elog(ERROR, "Sequence function nextval is not allowed in WHERE clauses");
|
||||
if (funcid == F_SETVAL && pstate->p_in_where_clause)
|
||||
elog(ERROR, "Sequence function setval is not allowed in WHERE clauses");
|
||||
|
||||
expr = makeNode(Expr);
|
||||
expr->typeOid = rettype;
|
||||
expr->opType = FUNC_EXPR;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.45 2000/08/24 03:29:05 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -49,8 +49,8 @@ make_parsestate(ParseState *parentParseState)
|
|||
pstate = palloc(sizeof(ParseState));
|
||||
MemSet(pstate, 0, sizeof(ParseState));
|
||||
|
||||
pstate->p_last_resno = 1;
|
||||
pstate->parentParseState = parentParseState;
|
||||
pstate->p_last_resno = 1;
|
||||
|
||||
return pstate;
|
||||
}
|
||||
|
@ -164,35 +164,44 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
|||
|
||||
/*
|
||||
* make_var
|
||||
* Build a Var node for an attribute identified by name
|
||||
* Build a Var node for an attribute identified by RTE and attrno
|
||||
*/
|
||||
Var *
|
||||
make_var(ParseState *pstate, Oid relid, char *refname,
|
||||
char *attrname)
|
||||
make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_attribute att_tup;
|
||||
int vnum,
|
||||
attid;
|
||||
sublevels_up;
|
||||
Oid vartypeid;
|
||||
int32 type_mod;
|
||||
int sublevels_up;
|
||||
|
||||
vnum = refnameRangeTablePosn(pstate, refname, &sublevels_up);
|
||||
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
tp = SearchSysCacheTuple(ATTNAME,
|
||||
ObjectIdGetDatum(relid),
|
||||
PointerGetDatum(attrname),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "Relation %s does not have attribute %s",
|
||||
refname, attrname);
|
||||
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
|
||||
attid = att_tup->attnum;
|
||||
vartypeid = att_tup->atttypid;
|
||||
type_mod = att_tup->atttypmod;
|
||||
if (rte->relid != InvalidOid)
|
||||
{
|
||||
/* Plain relation RTE --- get the attribute's type info */
|
||||
HeapTuple tp;
|
||||
Form_pg_attribute att_tup;
|
||||
|
||||
return makeVar(vnum, attid, vartypeid, type_mod, sublevels_up);
|
||||
tp = SearchSysCacheTuple(ATTNUM,
|
||||
ObjectIdGetDatum(rte->relid),
|
||||
Int16GetDatum(attrno),
|
||||
0, 0);
|
||||
/* this shouldn't happen... */
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "Relation %s does not have attribute %d",
|
||||
rte->relname, attrno);
|
||||
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
|
||||
vartypeid = att_tup->atttypid;
|
||||
type_mod = att_tup->atttypmod;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Subselect RTE --- get type info from subselect's tlist */
|
||||
elog(ERROR, "make_var: subselect in FROM not implemented yet");
|
||||
vartypeid = type_mod = 0;
|
||||
}
|
||||
|
||||
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.46 2000/08/08 15:42:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.47 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -20,14 +20,25 @@
|
|||
#include "access/htup.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
|
||||
char *colname);
|
||||
static Node *scanJoinForColumn(JoinExpr *join, char *colname,
|
||||
int sublevels_up);
|
||||
static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
|
||||
static void warnAutoRange(ParseState *pstate, char *refname);
|
||||
|
||||
|
||||
/*
|
||||
* Information defining the "system" attributes of every relation.
|
||||
*/
|
||||
|
@ -65,40 +76,96 @@ static struct
|
|||
#define SPECIALS ((int) (sizeof(special_attr)/sizeof(special_attr[0])))
|
||||
|
||||
|
||||
#ifdef NOT_USED
|
||||
/* refnameRangeTableEntries()
|
||||
* Given refname, return a list of range table entries
|
||||
* This is possible with JOIN syntax, where tables in a join
|
||||
* acquire the same reference name.
|
||||
* - thomas 2000-01-20
|
||||
* But at the moment we aren't carrying along a full list of
|
||||
* table/column aliases, so we don't have the full mechanism
|
||||
* to support outer joins in place yet.
|
||||
* - thomas 2000-03-04
|
||||
/*
|
||||
* refnameRangeOrJoinEntry
|
||||
* Given a refname, look to see if it matches any RTE or join table.
|
||||
* If so, return a pointer to the RangeTblEntry or JoinExpr.
|
||||
* Optionally get its nesting depth (0 = current). If sublevels_up
|
||||
* is NULL, only consider items at the current nesting level.
|
||||
*/
|
||||
|
||||
static List *
|
||||
refnameRangeTableEntries(ParseState *pstate, char *refname)
|
||||
Node *
|
||||
refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
char *refname,
|
||||
int *sublevels_up)
|
||||
{
|
||||
List *rteList = NULL;
|
||||
List *temp;
|
||||
if (sublevels_up)
|
||||
*sublevels_up = 0;
|
||||
|
||||
while (pstate != NULL)
|
||||
{
|
||||
List *temp;
|
||||
JoinExpr *join;
|
||||
|
||||
/*
|
||||
* Check the rangetable for RTEs; if no match, recursively scan
|
||||
* the jointree for join tables. We assume that no duplicate
|
||||
* entries have been made in any one nesting level.
|
||||
*/
|
||||
foreach(temp, pstate->p_rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(temp);
|
||||
|
||||
if (strcmp(rte->eref->relname, refname) == 0)
|
||||
rteList = lappend(rteList, rte);
|
||||
return (Node *) rte;
|
||||
}
|
||||
pstate = pstate->parentParseState;
|
||||
}
|
||||
return rteList;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* given refname, return a pointer to the range table entry */
|
||||
join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
|
||||
if (join)
|
||||
return (Node *) join;
|
||||
|
||||
pstate = pstate->parentParseState;
|
||||
if (sublevels_up)
|
||||
(*sublevels_up)++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Recursively search a jointree for a joinexpr with given refname */
|
||||
JoinExpr *
|
||||
scanJoinTreeForRefname(Node *jtnode, char *refname)
|
||||
{
|
||||
JoinExpr *result = NULL;
|
||||
|
||||
if (jtnode == NULL)
|
||||
return NULL;
|
||||
if (IsA(jtnode, List))
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) jtnode)
|
||||
{
|
||||
result = scanJoinTreeForRefname(lfirst(l), refname);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
/* ignore ... */
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
if (j->alias && strcmp(j->alias->relname, refname) == 0)
|
||||
return j;
|
||||
result = scanJoinTreeForRefname(j->larg, refname);
|
||||
if (! result)
|
||||
result = scanJoinTreeForRefname(j->rarg, refname);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "scanJoinTreeForRefname: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* given refname, return a pointer to the range table entry.
|
||||
*
|
||||
* NOTE that this routine will ONLY find RTEs, not join tables.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
refnameRangeTableEntry(ParseState *pstate, char *refname)
|
||||
{
|
||||
|
@ -118,9 +185,13 @@ refnameRangeTableEntry(ParseState *pstate, char *refname)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* given refname, return RT index (starting with 1) of the relation,
|
||||
/*
|
||||
* given refname, return RT index (starting with 1) of the relation,
|
||||
* and optionally get its nesting depth (0 = current). If sublevels_up
|
||||
* is NULL, only consider rels at the current nesting level.
|
||||
* A zero result means name not found.
|
||||
*
|
||||
* NOTE that this routine will ONLY find RTEs, not join tables.
|
||||
*/
|
||||
int
|
||||
refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
|
||||
|
@ -152,114 +223,264 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
|
|||
}
|
||||
|
||||
/*
|
||||
* returns range entry if found, else NULL
|
||||
* given an RTE, return RT index (starting with 1) of the entry,
|
||||
* and optionally get its nesting depth (0 = current). If sublevels_up
|
||||
* is NULL, only consider rels at the current nesting level.
|
||||
* Raises error if RTE not found.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
colnameRangeTableEntry(ParseState *pstate, char *colname)
|
||||
int
|
||||
RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
|
||||
{
|
||||
List *et;
|
||||
List *rtable;
|
||||
RangeTblEntry *rte_result = NULL;
|
||||
int index;
|
||||
List *temp;
|
||||
|
||||
if (sublevels_up)
|
||||
*sublevels_up = 0;
|
||||
|
||||
while (pstate != NULL)
|
||||
{
|
||||
if (pstate->p_is_rule)
|
||||
rtable = lnext(lnext(pstate->p_rtable));
|
||||
else
|
||||
rtable = pstate->p_rtable;
|
||||
|
||||
foreach(et, rtable)
|
||||
index = 1;
|
||||
foreach(temp, pstate->p_rtable)
|
||||
{
|
||||
RangeTblEntry *rte_candidate = NULL;
|
||||
RangeTblEntry *rte = lfirst(et);
|
||||
|
||||
/* only consider RTEs mentioned in FROM or UPDATE/DELETE */
|
||||
if (!rte->inFromCl && rte != pstate->p_target_rangetblentry)
|
||||
continue;
|
||||
|
||||
if (rte->eref->attrs != NULL)
|
||||
{
|
||||
List *c;
|
||||
|
||||
foreach(c, rte->ref->attrs)
|
||||
{
|
||||
if (strcmp(strVal(lfirst(c)), colname) == 0)
|
||||
{
|
||||
if (rte_candidate != NULL)
|
||||
elog(ERROR, "Column '%s' is ambiguous"
|
||||
" (internal error)", colname);
|
||||
rte_candidate = rte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Even if we have an attribute list in the RTE, look for the
|
||||
* column here anyway. This is the only way we will find
|
||||
* implicit columns like "oid". - thomas 2000-02-07
|
||||
*/
|
||||
if ((rte_candidate == NULL)
|
||||
&& (get_attnum(rte->relid, colname) != InvalidAttrNumber))
|
||||
rte_candidate = rte;
|
||||
|
||||
if (rte_candidate == NULL)
|
||||
continue;
|
||||
|
||||
if (rte_result != NULL)
|
||||
{
|
||||
if (!pstate->p_is_insert ||
|
||||
rte != pstate->p_target_rangetblentry)
|
||||
elog(ERROR, "Column '%s' is ambiguous", colname);
|
||||
}
|
||||
else
|
||||
rte_result = rte;
|
||||
if (rte == (RangeTblEntry *) lfirst(temp))
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (rte_result != NULL)
|
||||
break; /* found */
|
||||
|
||||
pstate = pstate->parentParseState;
|
||||
if (sublevels_up)
|
||||
(*sublevels_up)++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return rte_result;
|
||||
elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
|
||||
return 0; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
/*
|
||||
* put new entry in pstate p_rtable structure, or return pointer
|
||||
* if pstate null
|
||||
* scanRTEForColumn
|
||||
* Search the column names of a single RTE for the given name.
|
||||
* If found, return an appropriate Var node, else return NULL.
|
||||
* If the name proves ambiguous within this RTE, raise error.
|
||||
*/
|
||||
static Node *
|
||||
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
{
|
||||
Node *result = NULL;
|
||||
int attnum = 0;
|
||||
List *c;
|
||||
|
||||
/*
|
||||
* Scan the user column names (or aliases) for a match.
|
||||
* Complain if multiple matches.
|
||||
*/
|
||||
foreach(c, rte->eref->attrs)
|
||||
{
|
||||
attnum++;
|
||||
if (strcmp(strVal(lfirst(c)), colname) == 0)
|
||||
{
|
||||
if (result)
|
||||
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
|
||||
result = (Node *) make_var(pstate, rte, attnum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a unique match, return it. Note that this allows a user
|
||||
* alias to override a system column name (such as OID) without error.
|
||||
*/
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/*
|
||||
* If the RTE represents a table (not a sub-select), consider system
|
||||
* column names.
|
||||
*/
|
||||
if (rte->relid != InvalidOid)
|
||||
{
|
||||
attnum = specialAttNum(colname);
|
||||
if (attnum != InvalidAttrNumber)
|
||||
result = (Node *) make_var(pstate, rte, attnum);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* scanJoinForColumn
|
||||
* Search the column names of a single join table for the given name.
|
||||
* If found, return an appropriate Var node or expression, else return NULL.
|
||||
* If the name proves ambiguous within this jointable, raise error.
|
||||
*/
|
||||
static Node *
|
||||
scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
|
||||
{
|
||||
Node *result = NULL;
|
||||
int attnum = 0;
|
||||
List *c;
|
||||
|
||||
foreach(c, join->colnames)
|
||||
{
|
||||
attnum++;
|
||||
if (strcmp(strVal(lfirst(c)), colname) == 0)
|
||||
{
|
||||
if (result)
|
||||
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
|
||||
result = copyObject(nth(attnum-1, join->colvars));
|
||||
/*
|
||||
* If referencing an uplevel join item, we must adjust
|
||||
* sublevels settings in the copied expression.
|
||||
*/
|
||||
if (sublevels_up > 0)
|
||||
IncrementVarSublevelsUp(result, sublevels_up, 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* colnameToVar
|
||||
* Search for an unqualified column name.
|
||||
* If found, return the appropriate Var node (or expression).
|
||||
* If not found, return NULL. If the name proves ambiguous, raise error.
|
||||
*/
|
||||
Node *
|
||||
colnameToVar(ParseState *pstate, char *colname)
|
||||
{
|
||||
Node *result = NULL;
|
||||
ParseState *orig_pstate = pstate;
|
||||
int levels_up = 0;
|
||||
|
||||
while (pstate != NULL)
|
||||
{
|
||||
List *jt;
|
||||
|
||||
/*
|
||||
* We want to look only at top-level jointree items, and even for
|
||||
* those, ignore RTEs that are marked as not inFromCl and not
|
||||
* the query's target relation.
|
||||
*/
|
||||
foreach(jt, pstate->p_jointree)
|
||||
{
|
||||
Node *jtnode = (Node *) lfirst(jt);
|
||||
Node *newresult = NULL;
|
||||
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
if (! rte->inFromCl &&
|
||||
rte != pstate->p_target_rangetblentry)
|
||||
continue;
|
||||
|
||||
/* use orig_pstate here to get the right sublevels_up */
|
||||
newresult = scanRTEForColumn(orig_pstate, rte, colname);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
newresult = scanJoinForColumn(j, colname, levels_up);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "colnameToVar: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
|
||||
if (newresult)
|
||||
{
|
||||
if (result)
|
||||
elog(ERROR, "Column reference \"%s\" is ambiguous",
|
||||
colname);
|
||||
result = newresult;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != NULL)
|
||||
break; /* found */
|
||||
|
||||
pstate = pstate->parentParseState;
|
||||
levels_up++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* qualifiedNameToVar
|
||||
* Search for a qualified column name (refname + column name).
|
||||
* If found, return the appropriate Var node (or expression).
|
||||
* If not found, return NULL. If the name proves ambiguous, raise error.
|
||||
*/
|
||||
Node *
|
||||
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
||||
bool implicitRTEOK)
|
||||
{
|
||||
Node *result;
|
||||
Node *rteorjoin;
|
||||
int sublevels_up;
|
||||
|
||||
rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
|
||||
|
||||
if (rteorjoin == NULL)
|
||||
{
|
||||
if (! implicitRTEOK)
|
||||
return NULL;
|
||||
rteorjoin = (Node *) addImplicitRTE(pstate, refname);
|
||||
sublevels_up = 0;
|
||||
}
|
||||
|
||||
if (IsA(rteorjoin, RangeTblEntry))
|
||||
result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
|
||||
colname);
|
||||
else if (IsA(rteorjoin, JoinExpr))
|
||||
result = scanJoinForColumn((JoinExpr *) rteorjoin,
|
||||
colname, sublevels_up);
|
||||
else
|
||||
{
|
||||
elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
|
||||
nodeTag(rteorjoin));
|
||||
result = NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry to the pstate's range table (p_rtable), unless the
|
||||
* specified refname is already present, in which case raise error.
|
||||
*
|
||||
* If pstate is NULL, we just build an RTE and return it without worrying
|
||||
* about membership in an rtable list.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
addRangeTableEntry(ParseState *pstate,
|
||||
char *relname,
|
||||
Attr *ref,
|
||||
Attr *alias,
|
||||
bool inh,
|
||||
bool inFromCl,
|
||||
bool inJoinSet)
|
||||
bool inFromCl)
|
||||
{
|
||||
char *refname = alias ? alias->relname : relname;
|
||||
Relation rel;
|
||||
RangeTblEntry *rte;
|
||||
Attr *eref;
|
||||
int maxattrs;
|
||||
int sublevels_up;
|
||||
int numaliases;
|
||||
int varattno;
|
||||
|
||||
/* Look for an existing rte, if available... */
|
||||
/* Check for conflicting RTE or jointable alias (at level 0 only) */
|
||||
if (pstate != NULL)
|
||||
{
|
||||
int rt_index = refnameRangeTablePosn(pstate, ref->relname,
|
||||
&sublevels_up);
|
||||
Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
|
||||
|
||||
if (rt_index != 0 && (!inFromCl || sublevels_up == 0))
|
||||
{
|
||||
if (!strcmp(ref->relname, "*OLD*") || !strcmp(ref->relname, "*NEW*"))
|
||||
return (RangeTblEntry *) nth(rt_index - 1, pstate->p_rtable);
|
||||
elog(ERROR, "Table name '%s' specified more than once", ref->relname);
|
||||
}
|
||||
if (rteorjoin)
|
||||
elog(ERROR, "Table name \"%s\" specified more than once",
|
||||
refname);
|
||||
}
|
||||
|
||||
rte = makeNode(RangeTblEntry);
|
||||
|
||||
rte->relname = relname;
|
||||
rte->ref = ref;
|
||||
rte->alias = alias;
|
||||
|
||||
/*
|
||||
* Get the rel's OID. This access also ensures that we have an
|
||||
|
@ -271,30 +492,34 @@ addRangeTableEntry(ParseState *pstate,
|
|||
rte->relid = RelationGetRelid(rel);
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
|
||||
eref = copyObject(ref);
|
||||
if (maxattrs < length(eref->attrs))
|
||||
elog(ERROR, "Table '%s' has %d columns available but %d columns specified",
|
||||
relname, maxattrs, length(eref->attrs));
|
||||
eref = alias ? copyObject(alias) : makeAttr(refname, NULL);
|
||||
numaliases = length(eref->attrs);
|
||||
|
||||
if (maxattrs < numaliases)
|
||||
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
|
||||
refname, maxattrs, numaliases);
|
||||
|
||||
/* fill in any unspecified alias columns */
|
||||
for (varattno = length(eref->attrs); varattno < maxattrs; varattno++)
|
||||
for (varattno = numaliases; varattno < maxattrs; varattno++)
|
||||
{
|
||||
char *attrname;
|
||||
|
||||
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
|
||||
eref->attrs = lappend(eref->attrs, makeString(attrname));
|
||||
}
|
||||
heap_close(rel, AccessShareLock);
|
||||
rte->eref = eref;
|
||||
|
||||
/*
|
||||
* Flags: - this RTE should be expanded to include descendant tables,
|
||||
* - this RTE is in the FROM clause, - this RTE should be included in
|
||||
* the planner's final join.
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
/*----------
|
||||
* Flags:
|
||||
* - this RTE should be expanded to include descendant tables,
|
||||
* - this RTE is in the FROM clause,
|
||||
* - this RTE should not be checked for access rights.
|
||||
*----------
|
||||
*/
|
||||
rte->inh = inh;
|
||||
rte->inFromCl = inFromCl;
|
||||
rte->inJoinSet = inJoinSet;
|
||||
rte->skipAcl = false; /* always starts out false */
|
||||
|
||||
/*
|
||||
|
@ -306,75 +531,78 @@ addRangeTableEntry(ParseState *pstate,
|
|||
return rte;
|
||||
}
|
||||
|
||||
/* expandTable()
|
||||
* Populates an Attr with table name and column names
|
||||
* This is similar to expandAll(), but does not create an RTE
|
||||
* if it does not already exist.
|
||||
* - thomas 2000-01-19
|
||||
/*
|
||||
* Add the given RTE as a top-level entry in the pstate's join tree,
|
||||
* unless there already is an entry for it.
|
||||
*/
|
||||
Attr *
|
||||
expandTable(ParseState *pstate, char *refname, bool getaliases)
|
||||
void
|
||||
addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
|
||||
{
|
||||
Attr *attr;
|
||||
RangeTblEntry *rte;
|
||||
Relation rel;
|
||||
int varattno,
|
||||
maxattrs;
|
||||
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
|
||||
List *jt;
|
||||
RangeTblRef *rtr;
|
||||
|
||||
rte = refnameRangeTableEntry(pstate, refname);
|
||||
|
||||
if (getaliases && (rte != NULL))
|
||||
return rte->eref;
|
||||
|
||||
if (rte != NULL)
|
||||
rel = heap_open(rte->relid, AccessShareLock);
|
||||
else
|
||||
rel = heap_openr(refname, AccessShareLock);
|
||||
|
||||
if (rel == NULL)
|
||||
elog(ERROR, "Relation '%s' not found", refname);
|
||||
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
|
||||
attr = makeAttr(refname, NULL);
|
||||
|
||||
for (varattno = 0; varattno < maxattrs; varattno++)
|
||||
foreach(jt, pstate->p_jointree)
|
||||
{
|
||||
char *attrname;
|
||||
Node *n = (Node *) lfirst(jt);
|
||||
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
|
||||
continue;
|
||||
#endif /* _DROP_COLUMN_HACK__ */
|
||||
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
|
||||
attr->attrs = lappend(attr->attrs, makeString(attrname));
|
||||
if (IsA(n, RangeTblRef))
|
||||
{
|
||||
if (rtindex == ((RangeTblRef *) n)->rtindex)
|
||||
return; /* it's already being joined to */
|
||||
}
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
||||
return attr;
|
||||
/* Not present, so add it */
|
||||
rtr = makeNode(RangeTblRef);
|
||||
rtr->rtindex = rtindex;
|
||||
pstate->p_jointree = lappend(pstate->p_jointree, rtr);
|
||||
}
|
||||
|
||||
/*
|
||||
* expandAll -
|
||||
* makes a list of attributes
|
||||
* Add a POSTQUEL-style implicit RTE.
|
||||
*
|
||||
* We assume caller has already checked that there is no such RTE now.
|
||||
*/
|
||||
List *
|
||||
expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
|
||||
RangeTblEntry *
|
||||
addImplicitRTE(ParseState *pstate, char *relname)
|
||||
{
|
||||
List *te_list = NIL;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
rte = addRangeTableEntry(pstate, relname, NULL, false, false);
|
||||
addRTEtoJoinTree(pstate, rte);
|
||||
warnAutoRange(pstate, relname);
|
||||
|
||||
return rte;
|
||||
}
|
||||
|
||||
/* expandRTE()
|
||||
*
|
||||
* Given a rangetable entry, create lists of its column names (aliases if
|
||||
* provided, else real names) and Vars for each column. Only user columns
|
||||
* are considered, since this is primarily used to expand '*' and determine
|
||||
* the contents of JOIN tables.
|
||||
*
|
||||
* If only one of the two kinds of output list is needed, pass NULL for the
|
||||
* output pointer for the unwanted one.
|
||||
*/
|
||||
void
|
||||
expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
List **colnames, List **colvars)
|
||||
{
|
||||
Relation rel;
|
||||
int varattno,
|
||||
maxattrs;
|
||||
maxattrs,
|
||||
rtindex,
|
||||
sublevels_up;
|
||||
|
||||
rte = refnameRangeTableEntry(pstate, ref->relname);
|
||||
if (rte == NULL)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, relname, ref,
|
||||
FALSE, FALSE, TRUE);
|
||||
warnAutoRange(pstate, ref->relname);
|
||||
}
|
||||
if (colnames)
|
||||
*colnames = NIL;
|
||||
if (colvars)
|
||||
*colvars = NIL;
|
||||
|
||||
/* Need the RT index of the entry for creating Vars */
|
||||
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
rel = heap_open(rte->relid, AccessShareLock);
|
||||
|
||||
|
@ -382,42 +610,105 @@ expandAll(ParseState *pstate, char *relname, Attr *ref, int *this_resno)
|
|||
|
||||
for (varattno = 0; varattno < maxattrs; varattno++)
|
||||
{
|
||||
char *attrname;
|
||||
char *label;
|
||||
Var *varnode;
|
||||
TargetEntry *te = makeNode(TargetEntry);
|
||||
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
|
||||
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
if (COLUMN_IS_DROPPED(rel->rd_att->attrs[varattno]))
|
||||
if (COLUMN_IS_DROPPED(attr))
|
||||
continue;
|
||||
#endif /* _DROP_COLUMN_HACK__ */
|
||||
attrname = pstrdup(NameStr(rel->rd_att->attrs[varattno]->attname));
|
||||
|
||||
/*
|
||||
* varattno is zero-based, so check that length() is always
|
||||
* greater
|
||||
*/
|
||||
if (length(rte->eref->attrs) > varattno)
|
||||
label = pstrdup(strVal(nth(varattno, rte->eref->attrs)));
|
||||
else
|
||||
label = attrname;
|
||||
varnode = make_var(pstate, rte->relid, relname, attrname);
|
||||
if (colnames)
|
||||
{
|
||||
char *label;
|
||||
|
||||
/*
|
||||
* Even if the elements making up a set are complex, the set
|
||||
* itself is not.
|
||||
*/
|
||||
if (varattno < length(rte->eref->attrs))
|
||||
label = strVal(nth(varattno, rte->eref->attrs));
|
||||
else
|
||||
label = NameStr(attr->attname);
|
||||
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
||||
}
|
||||
|
||||
te->resdom = makeResdom((AttrNumber) (*this_resno)++,
|
||||
varnode->vartype,
|
||||
varnode->vartypmod,
|
||||
label,
|
||||
false);
|
||||
te->expr = (Node *) varnode;
|
||||
te_list = lappend(te_list, te);
|
||||
if (colvars)
|
||||
{
|
||||
Var *varnode;
|
||||
|
||||
varnode = makeVar(rtindex, attr->attnum,
|
||||
attr->atttypid, attr->atttypmod,
|
||||
sublevels_up);
|
||||
|
||||
*colvars = lappend(*colvars, varnode);
|
||||
}
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* expandRelAttrs -
|
||||
* makes a list of TargetEntry nodes for the attributes of the rel
|
||||
*/
|
||||
List *
|
||||
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
|
||||
{
|
||||
List *name_list,
|
||||
*var_list;
|
||||
|
||||
expandRTE(pstate, rte, &name_list, &var_list);
|
||||
|
||||
return expandNamesVars(pstate, name_list, var_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* expandJoinAttrs -
|
||||
* makes a list of TargetEntry nodes for the attributes of the join
|
||||
*/
|
||||
List *
|
||||
expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
|
||||
{
|
||||
List *vars;
|
||||
|
||||
vars = copyObject(join->colvars);
|
||||
/*
|
||||
* If referencing an uplevel join item, we must adjust
|
||||
* sublevels settings in the copied expression.
|
||||
*/
|
||||
if (sublevels_up > 0)
|
||||
IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
|
||||
|
||||
return expandNamesVars(pstate,
|
||||
copyObject(join->colnames),
|
||||
vars);
|
||||
}
|
||||
|
||||
/*
|
||||
* expandNamesVars -
|
||||
* Workhorse for "*" expansion: produce a list of targetentries
|
||||
* given lists of column names (as String nodes) and var references.
|
||||
*/
|
||||
static List *
|
||||
expandNamesVars(ParseState *pstate, List *names, List *vars)
|
||||
{
|
||||
List *te_list = NIL;
|
||||
|
||||
while (names)
|
||||
{
|
||||
char *label = strVal(lfirst(names));
|
||||
Node *varnode = (Node *) lfirst(vars);
|
||||
TargetEntry *te = makeNode(TargetEntry);
|
||||
|
||||
te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++,
|
||||
exprType(varnode),
|
||||
exprTypmod(varnode),
|
||||
label,
|
||||
false);
|
||||
te->expr = varnode;
|
||||
te_list = lappend(te_list, te);
|
||||
|
||||
names = lnext(names);
|
||||
vars = lnext(vars);
|
||||
}
|
||||
|
||||
Assert(vars == NIL); /* lists not same length? */
|
||||
|
||||
return te_list;
|
||||
}
|
||||
|
@ -531,11 +822,17 @@ attnumTypeId(Relation rd, int attid)
|
|||
return rd->rd_att->attrs[attid - 1]->atttypid;
|
||||
}
|
||||
|
||||
void
|
||||
/*
|
||||
* Generate a warning about an implicit RTE, if appropriate.
|
||||
*
|
||||
* Our current theory on this is that we should allow "SELECT foo.*"
|
||||
* but warn about a mixture of explicit and implicit RTEs.
|
||||
*/
|
||||
static void
|
||||
warnAutoRange(ParseState *pstate, char *refname)
|
||||
{
|
||||
List *temp;
|
||||
bool foundInFromCl = false;
|
||||
List *temp;
|
||||
|
||||
foreach(temp, pstate->p_rtable)
|
||||
{
|
||||
|
@ -548,8 +845,8 @@ warnAutoRange(ParseState *pstate, char *refname)
|
|||
}
|
||||
}
|
||||
if (foundInFromCl)
|
||||
elog(NOTICE, "Adding missing FROM-clause entry%s for table %s",
|
||||
pstate->parentParseState != NULL ? " in subquery" : "",
|
||||
refname);
|
||||
elog(NOTICE, "Adding missing FROM-clause entry%s for table \"%s\"",
|
||||
pstate->parentParseState != NULL ? " in subquery" : "",
|
||||
refname);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.61 2000/08/08 15:42:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
|
@ -104,36 +105,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
|||
* Target item is a single '*', expand all tables (eg.
|
||||
* SELECT * FROM emp)
|
||||
*/
|
||||
if (pstate->p_shape != NULL)
|
||||
{
|
||||
List *s,
|
||||
*a;
|
||||
int i;
|
||||
|
||||
Assert(length(pstate->p_shape) == length(pstate->p_alias));
|
||||
|
||||
s = pstate->p_shape;
|
||||
a = pstate->p_alias;
|
||||
for (i = 0; i < length(pstate->p_shape); i++)
|
||||
{
|
||||
TargetEntry *te;
|
||||
char *colname;
|
||||
Attr *shape = lfirst(s);
|
||||
Attr *alias = lfirst(a);
|
||||
|
||||
Assert(IsA(shape, Attr) &&IsA(alias, Attr));
|
||||
|
||||
colname = strVal(lfirst(alias->attrs));
|
||||
te = transformTargetEntry(pstate, (Node *) shape,
|
||||
NULL, colname, false);
|
||||
p_target = lappend(p_target, te);
|
||||
s = lnext(s);
|
||||
a = lnext(a);
|
||||
}
|
||||
}
|
||||
else
|
||||
p_target = nconc(p_target,
|
||||
ExpandAllTables(pstate));
|
||||
p_target = nconc(p_target,
|
||||
ExpandAllTables(pstate));
|
||||
}
|
||||
else if (att->attrs != NIL &&
|
||||
strcmp(strVal(lfirst(att->attrs)), "*") == 0)
|
||||
|
@ -143,10 +116,30 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
|||
* Target item is relation.*, expand that table (eg.
|
||||
* SELECT emp.*, dname FROM emp, dept)
|
||||
*/
|
||||
p_target = nconc(p_target,
|
||||
expandAll(pstate, att->relname,
|
||||
makeAttr(att->relname, NULL),
|
||||
&pstate->p_last_resno));
|
||||
Node *rteorjoin;
|
||||
int sublevels_up;
|
||||
|
||||
rteorjoin = refnameRangeOrJoinEntry(pstate, att->relname,
|
||||
&sublevels_up);
|
||||
|
||||
if (rteorjoin == NULL)
|
||||
{
|
||||
rteorjoin = (Node *) addImplicitRTE(pstate, att->relname);
|
||||
sublevels_up = 0;
|
||||
}
|
||||
|
||||
if (IsA(rteorjoin, RangeTblEntry))
|
||||
p_target = nconc(p_target,
|
||||
expandRelAttrs(pstate,
|
||||
(RangeTblEntry *) rteorjoin));
|
||||
else if (IsA(rteorjoin, JoinExpr))
|
||||
p_target = nconc(p_target,
|
||||
expandJoinAttrs(pstate,
|
||||
(JoinExpr *) rteorjoin,
|
||||
sublevels_up));
|
||||
else
|
||||
elog(ERROR, "transformTargetList: unexpected node type %d",
|
||||
nodeTag(rteorjoin));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -219,23 +212,12 @@ updateTargetListEntry(ParseState *pstate,
|
|||
*/
|
||||
if (indirection)
|
||||
{
|
||||
#ifndef DISABLE_JOIN_SYNTAX
|
||||
Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)), colname);
|
||||
|
||||
#else
|
||||
Attr *att = makeNode(Attr);
|
||||
|
||||
#endif
|
||||
Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)),
|
||||
colname);
|
||||
Node *arrayBase;
|
||||
ArrayRef *aref;
|
||||
|
||||
#ifdef DISABLE_JOIN_SYNTAX
|
||||
att->relname = pstrdup(RelationGetRelationName(rd));
|
||||
att->attrs = lcons(makeString(colname), NIL);
|
||||
#endif
|
||||
arrayBase = ParseNestedFuncOrColumn(pstate, att,
|
||||
&pstate->p_last_resno,
|
||||
EXPR_COLUMN_FIRST);
|
||||
arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST);
|
||||
aref = transformArraySubscripts(pstate, arrayBase,
|
||||
indirection,
|
||||
pstate->p_is_insert,
|
||||
|
@ -401,46 +383,54 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
|||
}
|
||||
|
||||
/* ExpandAllTables()
|
||||
* Turns '*' (in the target list) into a list of attributes
|
||||
* (of all relations in the range table)
|
||||
* Turns '*' (in the target list) into a list of targetlist entries.
|
||||
*
|
||||
* tlist entries are generated for each relation appearing in the FROM list,
|
||||
* which by now has been expanded into a join tree.
|
||||
*/
|
||||
static List *
|
||||
ExpandAllTables(ParseState *pstate)
|
||||
{
|
||||
List *target = NIL;
|
||||
List *rt,
|
||||
*rtable;
|
||||
|
||||
rtable = pstate->p_rtable;
|
||||
if (pstate->p_is_rule)
|
||||
{
|
||||
|
||||
/*
|
||||
* skip first two entries, "*new*" and "*current*"
|
||||
*/
|
||||
rtable = lnext(lnext(rtable));
|
||||
}
|
||||
List *jt;
|
||||
|
||||
/* SELECT *; */
|
||||
if (rtable == NIL)
|
||||
if (pstate->p_jointree == NIL)
|
||||
elog(ERROR, "Wildcard with no tables specified not allowed");
|
||||
|
||||
foreach(rt, rtable)
|
||||
foreach(jt, pstate->p_jointree)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(rt);
|
||||
Node *n = (Node *) lfirst(jt);
|
||||
|
||||
/*
|
||||
* we only expand those listed in the from clause. (This will also
|
||||
* prevent us from using the wrong table in inserts: eg. tenk2 in
|
||||
* "insert into tenk2 select * from tenk1;")
|
||||
*/
|
||||
if (!rte->inFromCl)
|
||||
continue;
|
||||
if (IsA(n, RangeTblRef))
|
||||
{
|
||||
RangeTblEntry *rte;
|
||||
|
||||
target = nconc(target,
|
||||
expandAll(pstate, rte->eref->relname, rte->eref,
|
||||
&pstate->p_last_resno));
|
||||
rte = rt_fetch(((RangeTblRef *) n)->rtindex,
|
||||
pstate->p_rtable);
|
||||
|
||||
/*
|
||||
* Ignore added-on relations that were not listed in the FROM
|
||||
* clause.
|
||||
*/
|
||||
if (!rte->inFromCl)
|
||||
continue;
|
||||
|
||||
target = nconc(target, expandRelAttrs(pstate, rte));
|
||||
}
|
||||
else if (IsA(n, JoinExpr))
|
||||
{
|
||||
/* A newfangled join expression */
|
||||
JoinExpr *j = (JoinExpr *) n;
|
||||
|
||||
/* Currently, a join expr could only have come from FROM. */
|
||||
target = nconc(target, expandJoinAttrs(pstate, j, 0));
|
||||
}
|
||||
else
|
||||
elog(ERROR, "ExpandAllTables: unexpected node (internal error)"
|
||||
"\n\t%s", nodeToString(n));
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +1,40 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* parser.c
|
||||
* Main entry point/driver for PostgreSQL parser
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.45 2000/04/12 17:15:27 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parse.h"
|
||||
#include "parser/parser.h"
|
||||
#include "parser/parse_expr.h"
|
||||
|
||||
|
||||
#if defined(FLEX_SCANNER)
|
||||
extern void DeleteBuffer(void);
|
||||
|
||||
#endif /* FLEX_SCANNER */
|
||||
|
||||
char *parseString; /* the char* which holds the string to be
|
||||
* parsed */
|
||||
List *parsetree; /* result of parsing is left here */
|
||||
|
||||
static int lookahead_token; /* one-token lookahead */
|
||||
static bool have_lookahead; /* lookahead_token set? */
|
||||
|
||||
#ifdef SETS_FIXED
|
||||
static void fixupsets();
|
||||
static void define_sets();
|
||||
|
@ -42,11 +50,11 @@ parser(char *str, Oid *typev, int nargs)
|
|||
List *queryList;
|
||||
int yyresult;
|
||||
|
||||
init_io();
|
||||
|
||||
parseString = pstrdup(str);
|
||||
parseString = str;
|
||||
parsetree = NIL; /* in case parser forgets to set it */
|
||||
have_lookahead = false;
|
||||
|
||||
scanner_init();
|
||||
parser_init(typev, nargs);
|
||||
parse_expr_init();
|
||||
|
||||
|
@ -83,6 +91,52 @@ parser(char *str, Oid *typev, int nargs)
|
|||
return queryList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Intermediate filter between parser and base lexer (base_yylex in scan.l).
|
||||
*
|
||||
* The filter is needed because in some cases SQL92 requires more than one
|
||||
* token lookahead. We reduce these cases to one-token lookahead by combining
|
||||
* tokens here, in order to keep the grammar LR(1).
|
||||
*
|
||||
* Using a filter is simpler than trying to recognize multiword tokens
|
||||
* directly in scan.l, because we'd have to allow for comments between the
|
||||
* words ...
|
||||
*/
|
||||
int
|
||||
yylex(void)
|
||||
{
|
||||
int cur_token;
|
||||
|
||||
/* Get next token --- we might already have it */
|
||||
if (have_lookahead)
|
||||
{
|
||||
cur_token = lookahead_token;
|
||||
have_lookahead = false;
|
||||
}
|
||||
else
|
||||
cur_token = base_yylex();
|
||||
|
||||
/* Do we need to look ahead for a possible multiword token? */
|
||||
switch (cur_token)
|
||||
{
|
||||
case UNION:
|
||||
/* UNION JOIN must be reduced to a single UNIONJOIN token */
|
||||
lookahead_token = base_yylex();
|
||||
if (lookahead_token == JOIN)
|
||||
cur_token = UNIONJOIN;
|
||||
else
|
||||
have_lookahead = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return cur_token;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SETS_FIXED
|
||||
static void
|
||||
fixupsets(Query *parse)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.76 2000/08/22 13:01:20 ishii Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.77 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -76,7 +76,7 @@ static char *literalbuf; /* expandable buffer */
|
|||
static int literallen; /* actual current length */
|
||||
static int literalalloc; /* current allocated buffer size */
|
||||
|
||||
static int xcdepth = 0;
|
||||
static int xcdepth = 0; /* depth of nesting in slash-star comments */
|
||||
|
||||
#define startlit() (literalbuf[0] = '\0', literallen = 0)
|
||||
static void addlit(char *ytext, int yleng);
|
||||
|
@ -510,22 +510,24 @@ other .
|
|||
|
||||
%%
|
||||
|
||||
void yyerror(const char * message)
|
||||
void
|
||||
yyerror(const char *message)
|
||||
{
|
||||
elog(ERROR, "parser: %s at or near \"%s\"", message, yytext);
|
||||
}
|
||||
|
||||
int yywrap()
|
||||
int
|
||||
yywrap(void)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
/*
|
||||
init_io:
|
||||
scanner_init:
|
||||
called by postgres before any actual parsing is done
|
||||
*/
|
||||
void
|
||||
init_io()
|
||||
scanner_init(void)
|
||||
{
|
||||
/* it's important to set this to NULL
|
||||
because input()/myinput() checks the non-nullness of parseCh
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.31 2000/09/06 14:15:20 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.32 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -56,38 +56,16 @@ thisLockWasTriggered_walker(Node *node,
|
|||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (thisLockWasTriggered_walker((Node *) (sub->lefthand), context))
|
||||
return true;
|
||||
context->sublevels_up++;
|
||||
if (thisLockWasTriggered_walker((Node *) (sub->subselect), context))
|
||||
{
|
||||
context->sublevels_up--; /* not really necessary */
|
||||
return true;
|
||||
}
|
||||
context->sublevels_up--;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
if (thisLockWasTriggered_walker((Node *) (qry->targetList), context))
|
||||
return true;
|
||||
if (thisLockWasTriggered_walker((Node *) (qry->qual), context))
|
||||
return true;
|
||||
if (thisLockWasTriggered_walker((Node *) (qry->havingQual), context))
|
||||
return true;
|
||||
return false;
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node, thisLockWasTriggered_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, thisLockWasTriggered_walker,
|
||||
(void *) context);
|
||||
|
@ -175,7 +153,7 @@ matchLocks(CmdType event,
|
|||
|
||||
typedef struct
|
||||
{
|
||||
Oid evowner;
|
||||
Oid evowner;
|
||||
} checkLockPerms_context;
|
||||
|
||||
static bool
|
||||
|
@ -184,23 +162,8 @@ checkLockPerms_walker(Node *node,
|
|||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (checkLockPerms_walker((Node *) (sub->lefthand), context))
|
||||
return true;
|
||||
if (checkLockPerms_walker((Node *) (sub->subselect), context))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
int rtablength = length(qry->rtable);
|
||||
int i;
|
||||
|
@ -212,13 +175,10 @@ checkLockPerms_walker(Node *node,
|
|||
int32 reqperm;
|
||||
int32 aclcheck_res;
|
||||
|
||||
if (rte->ref != NULL)
|
||||
{
|
||||
if (strcmp(rte->ref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
}
|
||||
if (strcmp(rte->eref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->eref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
|
||||
if (i == qry->resultRelation)
|
||||
switch (qry->commandType)
|
||||
|
@ -250,14 +210,8 @@ checkLockPerms_walker(Node *node,
|
|||
|
||||
/* If there are sublinks, search for them and check their RTEs */
|
||||
if (qry->hasSubLinks)
|
||||
{
|
||||
if (checkLockPerms_walker((Node *) (qry->targetList), context))
|
||||
return true;
|
||||
if (checkLockPerms_walker((Node *) (qry->qual), context))
|
||||
return true;
|
||||
if (checkLockPerms_walker((Node *) (qry->havingQual), context))
|
||||
return true;
|
||||
}
|
||||
return query_tree_walker(qry, checkLockPerms_walker,
|
||||
(void *) context);
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, checkLockPerms_walker,
|
||||
|
@ -278,7 +232,7 @@ checkLockPerms(List *locks, Query *parsetree, int rt_index)
|
|||
return; /* nothing to check */
|
||||
|
||||
/*
|
||||
* Get the usename of the rule's event relation owner
|
||||
* Get the userid of the rule's event relation owner
|
||||
*/
|
||||
rte = rt_fetch(rt_index, parsetree->rtable);
|
||||
ev_rel = heap_openr(rte->relname, AccessShareLock);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.79 2000/09/06 14:15:20 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.80 2000/09/12 21:07:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -51,12 +51,11 @@ static RewriteInfo *gatherRewriteMeta(Query *parsetree,
|
|||
Node *rule_qual,
|
||||
int rt_index,
|
||||
CmdType event,
|
||||
bool *instead_flag);
|
||||
static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
|
||||
static bool attribute_used(Node *node, int rt_index, int attno,
|
||||
int sublevels_up);
|
||||
static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
|
||||
int sublevels_up, int new_sublevels_up);
|
||||
bool instead_flag);
|
||||
static List *adjustJoinTree(Query *parsetree, int rt_index, bool *found);
|
||||
static bool modifyAggrefChangeVarnodes(Query *query,
|
||||
int rt_index, int new_index,
|
||||
int sublevels_up, int new_sublevels_up);
|
||||
static Node *modifyAggrefDropQual(Node *node, Node *targetNode);
|
||||
static SubLink *modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree);
|
||||
static Node *modifyAggrefQual(Node *node, Query *parsetree);
|
||||
|
@ -80,16 +79,15 @@ gatherRewriteMeta(Query *parsetree,
|
|||
Node *rule_qual,
|
||||
int rt_index,
|
||||
CmdType event,
|
||||
bool *instead_flag)
|
||||
bool instead_flag)
|
||||
{
|
||||
RewriteInfo *info;
|
||||
int rt_length;
|
||||
int result_reln;
|
||||
|
||||
info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
|
||||
info->rt_index = rt_index;
|
||||
info->event = event;
|
||||
info->instead_flag = *instead_flag;
|
||||
info->instead_flag = instead_flag;
|
||||
info->rule_action = (Query *) copyObject(rule_action);
|
||||
info->rule_qual = (Node *) copyObject(rule_qual);
|
||||
if (info->rule_action == NULL)
|
||||
|
@ -99,21 +97,54 @@ gatherRewriteMeta(Query *parsetree,
|
|||
info->nothing = FALSE;
|
||||
info->action = info->rule_action->commandType;
|
||||
info->current_varno = rt_index;
|
||||
info->rt = parsetree->rtable;
|
||||
rt_length = length(info->rt);
|
||||
info->rt = nconc(info->rt, copyObject(info->rule_action->rtable));
|
||||
rt_length = length(parsetree->rtable);
|
||||
|
||||
/* Adjust rule action and qual to offset its varnos */
|
||||
info->new_varno = PRS2_NEW_VARNO + rt_length;
|
||||
OffsetVarNodes(info->rule_action->qual, rt_length, 0);
|
||||
OffsetVarNodes((Node *) info->rule_action->targetList, rt_length, 0);
|
||||
OffsetVarNodes((Node *) info->rule_action, rt_length, 0);
|
||||
OffsetVarNodes(info->rule_qual, rt_length, 0);
|
||||
ChangeVarNodes((Node *) info->rule_action->qual,
|
||||
PRS2_OLD_VARNO + rt_length, rt_index, 0);
|
||||
ChangeVarNodes((Node *) info->rule_action->targetList,
|
||||
/* but its references to *OLD* should point at original rt_index */
|
||||
ChangeVarNodes((Node *) info->rule_action,
|
||||
PRS2_OLD_VARNO + rt_length, rt_index, 0);
|
||||
ChangeVarNodes(info->rule_qual,
|
||||
PRS2_OLD_VARNO + rt_length, rt_index, 0);
|
||||
|
||||
/*
|
||||
* We want the main parsetree's rtable to end up as the concatenation
|
||||
* of its original contents plus those of all the relevant rule
|
||||
* actions. Also store same into all the rule_action rtables.
|
||||
* Some of the entries may be unused after we finish rewriting, but
|
||||
* if we tried to clean those out we'd have a much harder job to
|
||||
* adjust RT indexes in the query's Vars. It's OK to have unused
|
||||
* RT entries, since planner will ignore them.
|
||||
*
|
||||
* NOTE KLUGY HACK: we assume the parsetree rtable had at least one
|
||||
* entry to begin with (OK enough, else where'd the rule come from?).
|
||||
* Because of this, if multiple rules nconc() their rtable additions
|
||||
* onto parsetree->rtable, they'll all see the same rtable because
|
||||
* they all have the same list head pointer.
|
||||
*/
|
||||
parsetree->rtable = nconc(parsetree->rtable,
|
||||
info->rule_action->rtable);
|
||||
info->rule_action->rtable = parsetree->rtable;
|
||||
|
||||
/*
|
||||
* Each rule action's jointree should be the main parsetree's jointree
|
||||
* plus that rule's jointree, but *without* the original rtindex
|
||||
* that we're replacing (if present, which it won't be for INSERT).
|
||||
* Note that if the rule refers to OLD, its jointree will add back
|
||||
* a reference to rt_index.
|
||||
*
|
||||
* XXX This might be wrong for subselect-in-FROM?
|
||||
*/
|
||||
{
|
||||
bool found;
|
||||
List *newjointree = adjustJoinTree(parsetree, rt_index, &found);
|
||||
|
||||
info->rule_action->jointree = nconc(newjointree,
|
||||
info->rule_action->jointree);
|
||||
}
|
||||
|
||||
/*
|
||||
* bug here about replace CURRENT -- sort of replace current is
|
||||
* deprecated now so this code shouldn't really need to be so
|
||||
|
@ -121,7 +152,8 @@ gatherRewriteMeta(Query *parsetree,
|
|||
*/
|
||||
if (info->action != CMD_SELECT)
|
||||
{ /* i.e update XXXXX */
|
||||
int new_result_reln = 0;
|
||||
int result_reln;
|
||||
int new_result_reln;
|
||||
|
||||
result_reln = info->rule_action->resultRelation;
|
||||
switch (result_reln)
|
||||
|
@ -140,152 +172,31 @@ gatherRewriteMeta(Query *parsetree,
|
|||
return info;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rangeTableEntry_used -
|
||||
* we need to process a RTE for RIR rules only if it is
|
||||
* referenced somewhere in var nodes of the query.
|
||||
* Copy the query's jointree list, and attempt to remove any occurrence
|
||||
* of the given rt_index as a top-level join item (we do not look for it
|
||||
* within JoinExprs). Returns modified jointree list --- original list
|
||||
* is not changed. *found is set to indicate if we found the rt_index.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
static List *
|
||||
adjustJoinTree(Query *parsetree, int rt_index, bool *found)
|
||||
{
|
||||
int rt_index;
|
||||
int sublevels_up;
|
||||
} rangeTableEntry_used_context;
|
||||
List *newjointree = listCopy(parsetree->jointree);
|
||||
List *jjt;
|
||||
|
||||
static bool
|
||||
rangeTableEntry_used_walker(Node *node,
|
||||
rangeTableEntry_used_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
*found = false;
|
||||
foreach(jjt, newjointree)
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
RangeTblRef *rtr = lfirst(jjt);
|
||||
|
||||
if (var->varlevelsup == context->sublevels_up &&
|
||||
var->varno == context->rt_index)
|
||||
return true;
|
||||
return false;
|
||||
if (IsA(rtr, RangeTblRef) && rtr->rtindex == rt_index)
|
||||
{
|
||||
newjointree = lremove(rtr, newjointree);
|
||||
*found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (rangeTableEntry_used_walker((Node *) (sub->lefthand), context))
|
||||
return true;
|
||||
if (rangeTableEntry_used((Node *) (sub->subselect),
|
||||
context->rt_index,
|
||||
context->sublevels_up + 1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
|
||||
if (rangeTableEntry_used_walker((Node *) (qry->targetList), context))
|
||||
return true;
|
||||
if (rangeTableEntry_used_walker((Node *) (qry->qual), context))
|
||||
return true;
|
||||
if (rangeTableEntry_used_walker((Node *) (qry->havingQual), context))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, rangeTableEntry_used_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
static bool
|
||||
rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
|
||||
{
|
||||
rangeTableEntry_used_context context;
|
||||
|
||||
context.rt_index = rt_index;
|
||||
context.sublevels_up = sublevels_up;
|
||||
return rangeTableEntry_used_walker(node, &context);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* attribute_used -
|
||||
* Check if a specific attribute number of a RTE is used
|
||||
* somewhere in the query
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int rt_index;
|
||||
int attno;
|
||||
int sublevels_up;
|
||||
} attribute_used_context;
|
||||
|
||||
static bool
|
||||
attribute_used_walker(Node *node,
|
||||
attribute_used_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == context->sublevels_up &&
|
||||
var->varno == context->rt_index &&
|
||||
var->varattno == context->attno)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (attribute_used_walker((Node *) (sub->lefthand), context))
|
||||
return true;
|
||||
if (attribute_used((Node *) (sub->subselect),
|
||||
context->rt_index,
|
||||
context->attno,
|
||||
context->sublevels_up + 1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
|
||||
if (attribute_used_walker((Node *) (qry->targetList), context))
|
||||
return true;
|
||||
if (attribute_used_walker((Node *) (qry->qual), context))
|
||||
return true;
|
||||
if (attribute_used_walker((Node *) (qry->havingQual), context))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, attribute_used_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
static bool
|
||||
attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
|
||||
{
|
||||
attribute_used_context context;
|
||||
|
||||
context.rt_index = rt_index;
|
||||
context.attno = attno;
|
||||
context.sublevels_up = sublevels_up;
|
||||
return attribute_used_walker(node, &context);
|
||||
return newjointree;
|
||||
}
|
||||
|
||||
|
||||
|
@ -330,48 +241,26 @@ modifyAggrefChangeVarnodes_walker(Node *node,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (modifyAggrefChangeVarnodes_walker((Node *) (sub->lefthand),
|
||||
context))
|
||||
return true;
|
||||
if (modifyAggrefChangeVarnodes((Node *) (sub->subselect),
|
||||
context->rt_index,
|
||||
context->new_index,
|
||||
context->sublevels_up + 1,
|
||||
context->new_sublevels_up + 1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
if (modifyAggrefChangeVarnodes_walker((Node *) (qry->targetList),
|
||||
context))
|
||||
return true;
|
||||
if (modifyAggrefChangeVarnodes_walker((Node *) (qry->qual),
|
||||
context))
|
||||
return true;
|
||||
if (modifyAggrefChangeVarnodes_walker((Node *) (qry->havingQual),
|
||||
context))
|
||||
return true;
|
||||
return false;
|
||||
context->sublevels_up++;
|
||||
context->new_sublevels_up++;
|
||||
result = query_tree_walker((Query *) node,
|
||||
modifyAggrefChangeVarnodes_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
context->new_sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, modifyAggrefChangeVarnodes_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
static bool
|
||||
modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
|
||||
modifyAggrefChangeVarnodes(Query *query, int rt_index, int new_index,
|
||||
int sublevels_up, int new_sublevels_up)
|
||||
{
|
||||
modifyAggrefChangeVarnodes_context context;
|
||||
|
@ -380,7 +269,8 @@ modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
|
|||
context.new_index = new_index;
|
||||
context.sublevels_up = sublevels_up;
|
||||
context.new_sublevels_up = new_sublevels_up;
|
||||
return modifyAggrefChangeVarnodes_walker(node, &context);
|
||||
return query_tree_walker(query, modifyAggrefChangeVarnodes_walker,
|
||||
(void *) &context);
|
||||
}
|
||||
|
||||
|
||||
|
@ -453,6 +343,7 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
|
|||
SubLink *sublink;
|
||||
TargetEntry *tle;
|
||||
Resdom *resdom;
|
||||
RangeTblRef *rtr;
|
||||
|
||||
aggVarNos = pull_varnos(aggref->target);
|
||||
if (length(aggVarNos) != 1)
|
||||
|
@ -492,6 +383,9 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
|
|||
subquery->distinctClause = NIL;
|
||||
subquery->sortClause = NIL;
|
||||
subquery->rtable = lcons(copyObject(rte), NIL);
|
||||
rtr = makeNode(RangeTblRef);
|
||||
rtr->rtindex = 1;
|
||||
subquery->jointree = lcons(rtr, NIL);
|
||||
subquery->targetList = lcons(tle, NIL);
|
||||
subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
|
||||
(Node *) aggref);
|
||||
|
@ -517,7 +411,7 @@ modifyAggrefMakeSublink(Aggref *aggref, Query *parsetree)
|
|||
* Note that because of previous line, these references have
|
||||
* varlevelsup = 1, which must be changed to 0.
|
||||
*/
|
||||
modifyAggrefChangeVarnodes((Node *) subquery,
|
||||
modifyAggrefChangeVarnodes(subquery,
|
||||
lfirsti(aggVarNos), 1,
|
||||
1, 0);
|
||||
|
||||
|
@ -675,6 +569,8 @@ apply_RIR_view_mutator(Node *node,
|
|||
apply_RIR_view_mutator, context);
|
||||
MUTATE(newnode->havingQual, query->havingQual, Node *,
|
||||
apply_RIR_view_mutator, context);
|
||||
MUTATE(newnode->jointree, query->jointree, List *,
|
||||
apply_RIR_view_mutator, context);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
return expression_tree_mutator(node, apply_RIR_view_mutator,
|
||||
|
@ -703,7 +599,7 @@ ApplyRetrieveRule(Query *parsetree,
|
|||
int rt_index,
|
||||
int relation_level,
|
||||
Relation relation,
|
||||
bool relWasInJoinSet)
|
||||
bool relIsUsed)
|
||||
{
|
||||
Query *rule_action = NULL;
|
||||
Node *rule_qual;
|
||||
|
@ -735,17 +631,34 @@ ApplyRetrieveRule(Query *parsetree,
|
|||
addedrtable = copyObject(rule_action->rtable);
|
||||
|
||||
/*
|
||||
* If the original rel wasn't in the join set, none of its spawn is.
|
||||
* If it was, then leave the spawn's flags as they are.
|
||||
* If the original rel wasn't in the join set (which'd be the case
|
||||
* for the target of an INSERT, for example), none of its spawn is.
|
||||
* If it was, then the spawn has to be added to the join set.
|
||||
*/
|
||||
if (!relWasInJoinSet)
|
||||
if (relIsUsed)
|
||||
{
|
||||
foreach(l, addedrtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(l);
|
||||
/*
|
||||
* QUICK HACK: this all needs to be replaced, but for now, find
|
||||
* the original rel in the jointree, remove it, and add the rule
|
||||
* action's jointree. This will not work for views referenced
|
||||
* in JoinExprs!!
|
||||
*
|
||||
* Note: it is possible that the old rel is referenced in the query
|
||||
* but isn't present in the jointree; this should only happen for
|
||||
* *OLD* and *NEW*. We must not fail if so, but add the rule's
|
||||
* jointree anyway. (This is a major crock ... should fix rule
|
||||
* representation ...)
|
||||
*/
|
||||
bool found;
|
||||
List *newjointree = adjustJoinTree(parsetree, rt_index, &found);
|
||||
List *addedjointree = (List *) copyObject(rule_action->jointree);
|
||||
|
||||
rte->inJoinSet = false;
|
||||
}
|
||||
if (!found)
|
||||
elog(DEBUG, "ApplyRetrieveRule: can't find old rel %s (%d) in jointree",
|
||||
rt_fetch(rt_index, rtable)->eref->relname, rt_index);
|
||||
OffsetVarNodes((Node *) addedjointree, rt_length, 0);
|
||||
newjointree = nconc(newjointree, addedjointree);
|
||||
parsetree->jointree = newjointree;
|
||||
}
|
||||
|
||||
rtable = nconc(rtable, addedrtable);
|
||||
|
@ -845,6 +758,10 @@ ApplyRetrieveRule(Query *parsetree,
|
|||
* NOTE: although this has the form of a walker, we cheat and modify the
|
||||
* SubLink nodes in-place. It is caller's responsibility to ensure that
|
||||
* no unwanted side-effects occur!
|
||||
*
|
||||
* This is unlike most of the other routines that recurse into subselects,
|
||||
* because we must take control at the SubLink node in order to replace
|
||||
* the SubLink's subselect link with the possibly-rewritten subquery.
|
||||
*/
|
||||
static bool
|
||||
fireRIRonSubselect(Node *node, void *context)
|
||||
|
@ -854,30 +771,15 @@ fireRIRonSubselect(Node *node, void *context)
|
|||
if (IsA(node, SubLink))
|
||||
{
|
||||
SubLink *sub = (SubLink *) node;
|
||||
Query *qry;
|
||||
|
||||
/* Process lefthand args */
|
||||
if (fireRIRonSubselect((Node *) (sub->lefthand), context))
|
||||
return true;
|
||||
/* Do what we came for */
|
||||
qry = fireRIRrules((Query *) (sub->subselect));
|
||||
sub->subselect = (Node *) qry;
|
||||
/* Need not recurse into subselect, because fireRIRrules did it */
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here when called from fireRIRrules */
|
||||
Query *qry = (Query *) node;
|
||||
|
||||
if (fireRIRonSubselect((Node *) (qry->targetList), context))
|
||||
return true;
|
||||
if (fireRIRonSubselect((Node *) (qry->qual), context))
|
||||
return true;
|
||||
if (fireRIRonSubselect((Node *) (qry->havingQual), context))
|
||||
return true;
|
||||
return false;
|
||||
sub->subselect = (Node *) fireRIRrules((Query *) (sub->subselect));
|
||||
/* Fall through to process lefthand args of SubLink */
|
||||
}
|
||||
/*
|
||||
* Do NOT recurse into Query nodes, because fireRIRrules already
|
||||
* processed subselects for us.
|
||||
*/
|
||||
return expression_tree_walker(node, fireRIRonSubselect,
|
||||
(void *) context);
|
||||
}
|
||||
|
@ -897,7 +799,7 @@ fireRIRrules(Query *parsetree)
|
|||
RuleLock *rules;
|
||||
RewriteRule *rule;
|
||||
RewriteRule RIRonly;
|
||||
bool relWasInJoinSet;
|
||||
bool relIsUsed;
|
||||
int i;
|
||||
List *l;
|
||||
|
||||
|
@ -916,11 +818,12 @@ fireRIRrules(Query *parsetree)
|
|||
* If the table is not referenced in the query, then we ignore it.
|
||||
* This prevents infinite expansion loop due to new rtable entries
|
||||
* inserted by expansion of a rule. A table is referenced if it is
|
||||
* part of the join set (a source table), or is the result table,
|
||||
* or is referenced by any Var nodes.
|
||||
* part of the join set (a source table), or is referenced by any
|
||||
* Var nodes, or is the result table.
|
||||
*/
|
||||
if (!rte->inJoinSet && rt_index != parsetree->resultRelation &&
|
||||
!rangeTableEntry_used((Node *) parsetree, rt_index, 0))
|
||||
relIsUsed = rangeTableEntry_used((Node *) parsetree, rt_index, 0);
|
||||
|
||||
if (!relIsUsed && rt_index != parsetree->resultRelation)
|
||||
continue;
|
||||
|
||||
rel = heap_openr(rte->relname, AccessShareLock);
|
||||
|
@ -931,9 +834,6 @@ fireRIRrules(Query *parsetree)
|
|||
continue;
|
||||
}
|
||||
|
||||
relWasInJoinSet = rte->inJoinSet; /* save before possibly
|
||||
* clearing */
|
||||
|
||||
/*
|
||||
* Collect the RIR rules that we must apply
|
||||
*/
|
||||
|
@ -947,22 +847,10 @@ fireRIRrules(Query *parsetree)
|
|||
if (rule->attrno > 0)
|
||||
{
|
||||
/* per-attr rule; do we need it? */
|
||||
if (!attribute_used((Node *) parsetree,
|
||||
rt_index,
|
||||
if (!attribute_used((Node *) parsetree, rt_index,
|
||||
rule->attrno, 0))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Rel-wide ON SELECT DO INSTEAD means this is a view.
|
||||
* Remove the view from the planner's join target set, or
|
||||
* we'll get no rows out because view itself is empty!
|
||||
*/
|
||||
if (rule->isInstead)
|
||||
rte->inJoinSet = false;
|
||||
}
|
||||
|
||||
locks = lappend(locks, rule);
|
||||
}
|
||||
|
@ -989,7 +877,7 @@ fireRIRrules(Query *parsetree)
|
|||
rt_index,
|
||||
RIRonly.attrno == -1,
|
||||
rel,
|
||||
relWasInJoinSet);
|
||||
relIsUsed);
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
|
@ -999,7 +887,7 @@ fireRIRrules(Query *parsetree)
|
|||
parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
|
||||
|
||||
if (parsetree->hasSubLinks)
|
||||
fireRIRonSubselect((Node *) parsetree, NULL);
|
||||
query_tree_walker(parsetree, fireRIRonSubselect, NULL);
|
||||
|
||||
return parsetree;
|
||||
}
|
||||
|
@ -1056,13 +944,20 @@ CopyAndAddQual(Query *parsetree,
|
|||
{
|
||||
List *rtable;
|
||||
int rt_length;
|
||||
List *jointree;
|
||||
|
||||
rtable = new_tree->rtable;
|
||||
rt_length = length(rtable);
|
||||
rtable = nconc(rtable, copyObject(rule_action->rtable));
|
||||
/* XXX above possibly wrong for subselect-in-FROM */
|
||||
new_tree->rtable = rtable;
|
||||
OffsetVarNodes(new_qual, rt_length, 0);
|
||||
ChangeVarNodes(new_qual, PRS2_OLD_VARNO + rt_length, rt_index, 0);
|
||||
jointree = copyObject(rule_action->jointree);
|
||||
OffsetVarNodes((Node *) jointree, rt_length, 0);
|
||||
ChangeVarNodes((Node *) jointree, PRS2_OLD_VARNO + rt_length,
|
||||
rt_index, 0);
|
||||
new_tree->jointree = nconc(new_tree->jointree, jointree);
|
||||
}
|
||||
/* XXX -- where current doesn't work for instead nothing.... yet */
|
||||
AddNotQual(new_tree, new_qual);
|
||||
|
@ -1103,8 +998,7 @@ fireRules(Query *parsetree,
|
|||
foreach(i, locks)
|
||||
{
|
||||
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
|
||||
Node *qual,
|
||||
*event_qual;
|
||||
Node *event_qual;
|
||||
List *actions;
|
||||
List *r;
|
||||
|
||||
|
@ -1227,7 +1121,7 @@ fireRules(Query *parsetree,
|
|||
*--------------------------------------------------
|
||||
*/
|
||||
info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
|
||||
rt_index, event, instead_flag);
|
||||
rt_index, event, *instead_flag);
|
||||
|
||||
/* handle escapable cases, or those handled by other code */
|
||||
if (info->nothing)
|
||||
|
@ -1247,11 +1141,9 @@ fireRules(Query *parsetree,
|
|||
* splitting into two queries one w/rule_qual, one w/NOT
|
||||
* rule_qual. Also add user query qual onto rule action
|
||||
*/
|
||||
qual = parsetree->qual;
|
||||
AddQual(info->rule_action, qual);
|
||||
AddQual(info->rule_action, parsetree->qual);
|
||||
|
||||
if (info->rule_qual != NULL)
|
||||
AddQual(info->rule_action, info->rule_qual);
|
||||
AddQual(info->rule_action, info->rule_qual);
|
||||
|
||||
/*--------------------------------------------------
|
||||
* Step 2:
|
||||
|
@ -1264,18 +1156,6 @@ fireRules(Query *parsetree,
|
|||
|
||||
/*--------------------------------------------------
|
||||
* Step 3:
|
||||
* rewriting due to retrieve rules
|
||||
*--------------------------------------------------
|
||||
*/
|
||||
info->rule_action->rtable = info->rt;
|
||||
|
||||
/*
|
||||
* ProcessRetrieveQuery(info->rule_action, info->rt,
|
||||
* &orig_instead_flag, TRUE);
|
||||
*/
|
||||
|
||||
/*--------------------------------------------------
|
||||
* Step 4
|
||||
* Simplify? hey, no algorithm for simplification... let
|
||||
* the planner do it.
|
||||
*--------------------------------------------------
|
||||
|
@ -1403,7 +1283,7 @@ deepRewriteQuery(Query *parsetree)
|
|||
rewritten = nconc(rewritten, qual_products);
|
||||
|
||||
/* ----------
|
||||
* The original query is appended last if not instead
|
||||
* The original query is appended last (if no "instead" rule)
|
||||
* because update and delete rule actions might not do
|
||||
* anything if they are invoked after the update or
|
||||
* delete is performed. The command counter increment
|
||||
|
@ -1471,17 +1351,15 @@ BasicQueryRewrite(Query *parsetree)
|
|||
*/
|
||||
if (query->hasAggs)
|
||||
{
|
||||
query->hasAggs =
|
||||
checkExprHasAggs((Node *) (query->targetList)) ||
|
||||
checkExprHasAggs((Node *) (query->havingQual));
|
||||
if (checkExprHasAggs((Node *) (query->qual)))
|
||||
elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
|
||||
query->hasAggs = checkExprHasAggs((Node *) query);
|
||||
if (query->hasAggs)
|
||||
if (checkExprHasAggs(query->qual))
|
||||
elog(ERROR, "BasicQueryRewrite: failed to remove aggs from qual");
|
||||
}
|
||||
if (query->hasSubLinks)
|
||||
query->hasSubLinks =
|
||||
checkExprHasSubLink((Node *) (query->targetList)) ||
|
||||
checkExprHasSubLink((Node *) (query->qual)) ||
|
||||
checkExprHasSubLink((Node *) (query->havingQual));
|
||||
{
|
||||
query->hasSubLinks = checkExprHasSubLink((Node *) query);
|
||||
}
|
||||
results = lappend(results, query);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.47 2000/05/30 00:49:51 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.48 2000/09/12 21:07:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -42,7 +42,15 @@ static bool checkExprHasSubLink_walker(Node *node, void *context);
|
|||
bool
|
||||
checkExprHasAggs(Node *node)
|
||||
{
|
||||
return checkExprHasAggs_walker(node, NULL);
|
||||
/*
|
||||
* If a Query is passed, examine it --- but we will not recurse
|
||||
* into sub-Queries.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
return query_tree_walker((Query *) node, checkExprHasAggs_walker,
|
||||
NULL);
|
||||
else
|
||||
return checkExprHasAggs_walker(node, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -64,7 +72,15 @@ checkExprHasAggs_walker(Node *node, void *context)
|
|||
bool
|
||||
checkExprHasSubLink(Node *node)
|
||||
{
|
||||
return checkExprHasSubLink_walker(node, NULL);
|
||||
/*
|
||||
* If a Query is passed, examine it --- but we will not recurse
|
||||
* into sub-Queries.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
return query_tree_walker((Query *) node, checkExprHasSubLink_walker,
|
||||
NULL);
|
||||
else
|
||||
return checkExprHasSubLink_walker(node, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -84,10 +100,11 @@ checkExprHasSubLink_walker(Node *node, void *context)
|
|||
*
|
||||
* Find all Var nodes in the given tree with varlevelsup == sublevels_up,
|
||||
* and increment their varno fields (rangetable indexes) by 'offset'.
|
||||
* The varnoold fields are adjusted similarly.
|
||||
* The varnoold fields are adjusted similarly. Also, RangeTblRef nodes
|
||||
* in join trees are adjusted.
|
||||
*
|
||||
* NOTE: although this has the form of a walker, we cheat and modify the
|
||||
* Var nodes in-place. The given expression tree should have been copied
|
||||
* nodes in-place. The given expression tree should have been copied
|
||||
* earlier to ensure that no unwanted side-effects occur!
|
||||
*/
|
||||
|
||||
|
@ -113,38 +130,24 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
if (IsA(node, RangeTblRef))
|
||||
{
|
||||
RangeTblRef *rtr = (RangeTblRef *) node;
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (OffsetVarNodes_walker((Node *) (sub->lefthand),
|
||||
context))
|
||||
return true;
|
||||
OffsetVarNodes((Node *) (sub->subselect),
|
||||
context->offset,
|
||||
context->sublevels_up + 1);
|
||||
if (context->sublevels_up == 0)
|
||||
rtr->rtindex += context->offset;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
if (OffsetVarNodes_walker((Node *) (qry->targetList),
|
||||
context))
|
||||
return true;
|
||||
if (OffsetVarNodes_walker((Node *) (qry->qual),
|
||||
context))
|
||||
return true;
|
||||
if (OffsetVarNodes_walker((Node *) (qry->havingQual),
|
||||
context))
|
||||
return true;
|
||||
return false;
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, OffsetVarNodes_walker,
|
||||
(void *) context);
|
||||
|
@ -157,7 +160,17 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
|
|||
|
||||
context.offset = offset;
|
||||
context.sublevels_up = sublevels_up;
|
||||
OffsetVarNodes_walker(node, &context);
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree;
|
||||
* if it's a Query, go straight to query_tree_walker to make sure that
|
||||
* sublevels_up doesn't get incremented prematurely.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
query_tree_walker((Query *) node, OffsetVarNodes_walker,
|
||||
(void *) &context);
|
||||
else
|
||||
OffsetVarNodes_walker(node, &context);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -165,10 +178,11 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
|
|||
*
|
||||
* Find all Var nodes in the given tree belonging to a specific relation
|
||||
* (identified by sublevels_up and rt_index), and change their varno fields
|
||||
* to 'new_index'. The varnoold fields are changed too.
|
||||
* to 'new_index'. The varnoold fields are changed too. Also, RangeTblRef
|
||||
* nodes in join trees are adjusted.
|
||||
*
|
||||
* NOTE: although this has the form of a walker, we cheat and modify the
|
||||
* Var nodes in-place. The given expression tree should have been copied
|
||||
* nodes in-place. The given expression tree should have been copied
|
||||
* earlier to ensure that no unwanted side-effects occur!
|
||||
*/
|
||||
|
||||
|
@ -196,39 +210,25 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
if (IsA(node, RangeTblRef))
|
||||
{
|
||||
RangeTblRef *rtr = (RangeTblRef *) node;
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (ChangeVarNodes_walker((Node *) (sub->lefthand),
|
||||
context))
|
||||
return true;
|
||||
ChangeVarNodes((Node *) (sub->subselect),
|
||||
context->rt_index,
|
||||
context->new_index,
|
||||
context->sublevels_up + 1);
|
||||
if (context->sublevels_up == 0 &&
|
||||
rtr->rtindex == context->rt_index)
|
||||
rtr->rtindex = context->new_index;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
if (ChangeVarNodes_walker((Node *) (qry->targetList),
|
||||
context))
|
||||
return true;
|
||||
if (ChangeVarNodes_walker((Node *) (qry->qual),
|
||||
context))
|
||||
return true;
|
||||
if (ChangeVarNodes_walker((Node *) (qry->havingQual),
|
||||
context))
|
||||
return true;
|
||||
return false;
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, ChangeVarNodes_walker,
|
||||
(void *) context);
|
||||
|
@ -242,7 +242,17 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
|
|||
context.rt_index = rt_index;
|
||||
context.new_index = new_index;
|
||||
context.sublevels_up = sublevels_up;
|
||||
ChangeVarNodes_walker(node, &context);
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree;
|
||||
* if it's a Query, go straight to query_tree_walker to make sure that
|
||||
* sublevels_up doesn't get incremented prematurely.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
query_tree_walker((Query *) node, ChangeVarNodes_walker,
|
||||
(void *) &context);
|
||||
else
|
||||
ChangeVarNodes_walker(node, &context);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -282,38 +292,17 @@ IncrementVarSublevelsUp_walker(Node *node,
|
|||
var->varlevelsup += context->delta_sublevels_up;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
|
||||
/*
|
||||
* Standard expression_tree_walker will not recurse into
|
||||
* subselect, but here we must do so.
|
||||
*/
|
||||
SubLink *sub = (SubLink *) node;
|
||||
|
||||
if (IncrementVarSublevelsUp_walker((Node *) (sub->lefthand),
|
||||
context))
|
||||
return true;
|
||||
IncrementVarSublevelsUp((Node *) (sub->subselect),
|
||||
context->delta_sublevels_up,
|
||||
context->min_sublevels_up + 1);
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Reach here after recursing down into subselect above... */
|
||||
Query *qry = (Query *) node;
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
if (IncrementVarSublevelsUp_walker((Node *) (qry->targetList),
|
||||
context))
|
||||
return true;
|
||||
if (IncrementVarSublevelsUp_walker((Node *) (qry->qual),
|
||||
context))
|
||||
return true;
|
||||
if (IncrementVarSublevelsUp_walker((Node *) (qry->havingQual),
|
||||
context))
|
||||
return true;
|
||||
return false;
|
||||
context->min_sublevels_up++;
|
||||
result = query_tree_walker((Query *) node,
|
||||
IncrementVarSublevelsUp_walker,
|
||||
(void *) context);
|
||||
context->min_sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
|
||||
(void *) context);
|
||||
|
@ -327,9 +316,157 @@ IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
|
|||
|
||||
context.delta_sublevels_up = delta_sublevels_up;
|
||||
context.min_sublevels_up = min_sublevels_up;
|
||||
IncrementVarSublevelsUp_walker(node, &context);
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree;
|
||||
* if it's a Query, go straight to query_tree_walker to make sure that
|
||||
* sublevels_up doesn't get incremented prematurely.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
query_tree_walker((Query *) node, IncrementVarSublevelsUp_walker,
|
||||
(void *) &context);
|
||||
else
|
||||
IncrementVarSublevelsUp_walker(node, &context);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rangeTableEntry_used - detect whether an RTE is referenced somewhere
|
||||
* in var nodes or jointree nodes of a query or expression.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int rt_index;
|
||||
int sublevels_up;
|
||||
} rangeTableEntry_used_context;
|
||||
|
||||
static bool
|
||||
rangeTableEntry_used_walker(Node *node,
|
||||
rangeTableEntry_used_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == context->sublevels_up &&
|
||||
var->varno == context->rt_index)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, RangeTblRef))
|
||||
{
|
||||
RangeTblRef *rtr = (RangeTblRef *) node;
|
||||
|
||||
if (rtr->rtindex == context->rt_index &&
|
||||
context->sublevels_up == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, rangeTableEntry_used_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
bool
|
||||
rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
|
||||
{
|
||||
rangeTableEntry_used_context context;
|
||||
|
||||
context.rt_index = rt_index;
|
||||
context.sublevels_up = sublevels_up;
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree;
|
||||
* if it's a Query, go straight to query_tree_walker to make sure that
|
||||
* sublevels_up doesn't get incremented prematurely.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
return query_tree_walker((Query *) node, rangeTableEntry_used_walker,
|
||||
(void *) &context);
|
||||
else
|
||||
return rangeTableEntry_used_walker(node, &context);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* attribute_used -
|
||||
* Check if a specific attribute number of a RTE is used
|
||||
* somewhere in the query or expression.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int rt_index;
|
||||
int attno;
|
||||
int sublevels_up;
|
||||
} attribute_used_context;
|
||||
|
||||
static bool
|
||||
attribute_used_walker(Node *node,
|
||||
attribute_used_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == context->sublevels_up &&
|
||||
var->varno == context->rt_index &&
|
||||
var->varattno == context->attno)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
context->sublevels_up++;
|
||||
result = query_tree_walker((Query *) node, attribute_used_walker,
|
||||
(void *) context);
|
||||
context->sublevels_up--;
|
||||
return result;
|
||||
}
|
||||
return expression_tree_walker(node, attribute_used_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
bool
|
||||
attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
|
||||
{
|
||||
attribute_used_context context;
|
||||
|
||||
context.rt_index = rt_index;
|
||||
context.attno = attno;
|
||||
context.sublevels_up = sublevels_up;
|
||||
|
||||
/*
|
||||
* Must be prepared to start with a Query or a bare expression tree;
|
||||
* if it's a Query, go straight to query_tree_walker to make sure that
|
||||
* sublevels_up doesn't get incremented prematurely.
|
||||
*/
|
||||
if (node && IsA(node, Query))
|
||||
return query_tree_walker((Query *) node, attribute_used_walker,
|
||||
(void *) &context);
|
||||
else
|
||||
return attribute_used_walker(node, &context);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add the given qualifier condition to the query's WHERE clause
|
||||
*/
|
||||
|
@ -615,11 +752,6 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
|||
Query *query = (Query *) node;
|
||||
Query *newnode;
|
||||
|
||||
/*
|
||||
* XXX original code for ResolveNew only recursed into qual field
|
||||
* of subquery. I'm assuming that was an oversight ... tgl 9/99
|
||||
*/
|
||||
|
||||
FLATCOPY(newnode, query, Query);
|
||||
MUTATE(newnode->targetList, query->targetList, List *,
|
||||
ResolveNew_mutator, context);
|
||||
|
@ -627,6 +759,8 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
|||
ResolveNew_mutator, context);
|
||||
MUTATE(newnode->havingQual, query->havingQual, Node *,
|
||||
ResolveNew_mutator, context);
|
||||
MUTATE(newnode->jointree, query->jointree, List *,
|
||||
ResolveNew_mutator, context);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
return expression_tree_mutator(node, ResolveNew_mutator,
|
||||
|
@ -650,13 +784,15 @@ void
|
|||
FixNew(RewriteInfo *info, Query *parsetree)
|
||||
{
|
||||
info->rule_action->targetList = (List *)
|
||||
ResolveNew((Node *) info->rule_action->targetList,
|
||||
info, parsetree->targetList, 0);
|
||||
ResolveNew((Node *) info->rule_action->targetList,
|
||||
info, parsetree->targetList, 0);
|
||||
info->rule_action->qual = ResolveNew(info->rule_action->qual,
|
||||
info, parsetree->targetList, 0);
|
||||
/* XXX original code didn't fix havingQual; presumably an oversight? */
|
||||
info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual,
|
||||
info, parsetree->targetList, 0);
|
||||
info, parsetree->targetList, 0);
|
||||
info->rule_action->jointree = (List *)
|
||||
ResolveNew((Node *) info->rule_action->jointree,
|
||||
info, parsetree->targetList, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -758,11 +894,6 @@ HandleRIRAttributeRule_mutator(Node *node,
|
|||
Query *query = (Query *) node;
|
||||
Query *newnode;
|
||||
|
||||
/*
|
||||
* XXX original code for HandleRIRAttributeRule only recursed into
|
||||
* qual field of subquery. I'm assuming that was an oversight ...
|
||||
*/
|
||||
|
||||
FLATCOPY(newnode, query, Query);
|
||||
MUTATE(newnode->targetList, query->targetList, List *,
|
||||
HandleRIRAttributeRule_mutator, context);
|
||||
|
@ -770,6 +901,8 @@ HandleRIRAttributeRule_mutator(Node *node,
|
|||
HandleRIRAttributeRule_mutator, context);
|
||||
MUTATE(newnode->havingQual, query->havingQual, Node *,
|
||||
HandleRIRAttributeRule_mutator, context);
|
||||
MUTATE(newnode->jointree, query->jointree, List *,
|
||||
HandleRIRAttributeRule_mutator, context);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
|
||||
|
@ -798,9 +931,13 @@ HandleRIRAttributeRule(Query *parsetree,
|
|||
parsetree->targetList = (List *)
|
||||
HandleRIRAttributeRule_mutator((Node *) parsetree->targetList,
|
||||
&context);
|
||||
parsetree->qual = HandleRIRAttributeRule_mutator(parsetree->qual,
|
||||
&context);
|
||||
/* XXX original code did not fix havingQual ... oversight? */
|
||||
parsetree->havingQual = HandleRIRAttributeRule_mutator(parsetree->havingQual,
|
||||
&context);
|
||||
parsetree->qual =
|
||||
HandleRIRAttributeRule_mutator(parsetree->qual,
|
||||
&context);
|
||||
parsetree->havingQual =
|
||||
HandleRIRAttributeRule_mutator(parsetree->havingQual,
|
||||
&context);
|
||||
parsetree->jointree = (List *)
|
||||
HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
|
||||
&context);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/**********************************************************************
|
||||
* get_ruledef.c - Function to get a rules definition text
|
||||
* out of its tuple
|
||||
* ruleutils.c - Functions to convert stored expressions/querytrees
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.60 2000/09/12 04:15:58 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.61 2000/09/12 21:07:05 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
|
@ -43,6 +43,7 @@
|
|||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "commands/view.h"
|
||||
#include "executor/spi.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "optimizer/clauses.h"
|
||||
|
@ -50,8 +51,8 @@
|
|||
#include "parser/keywords.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "commands/view.h"
|
||||
|
||||
|
||||
/* ----------
|
||||
|
@ -65,12 +66,6 @@ typedef struct
|
|||
bool varprefix; /* TRUE to print prefixes on Vars */
|
||||
} deparse_context;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Index rt_index;
|
||||
int levelsup;
|
||||
} check_if_rte_used_context;
|
||||
|
||||
|
||||
/* ----------
|
||||
* Global data
|
||||
|
@ -108,13 +103,13 @@ static void get_func_expr(Expr *expr, deparse_context *context);
|
|||
static void get_tle_expr(TargetEntry *tle, deparse_context *context);
|
||||
static void get_const_expr(Const *constval, deparse_context *context);
|
||||
static void get_sublink_expr(Node *node, deparse_context *context);
|
||||
static void get_from_clause(Query *query, deparse_context *context);
|
||||
static void get_from_clause_item(Node *jtnode, Query *query,
|
||||
deparse_context *context);
|
||||
static bool tleIsArrayAssign(TargetEntry *tle);
|
||||
static char *quote_identifier(char *ident);
|
||||
static char *get_relation_name(Oid relid);
|
||||
static char *get_attribute_name(Oid relid, int2 attnum);
|
||||
static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
|
||||
static bool check_if_rte_used_walker(Node *node,
|
||||
check_if_rte_used_context *context);
|
||||
|
||||
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
|
||||
|
||||
|
@ -230,13 +225,13 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
|
|||
Name vname = PG_GETARG_NAME(0);
|
||||
text *ruledef;
|
||||
Datum args[1];
|
||||
char nulls[2];
|
||||
char nulls[1];
|
||||
int spirc;
|
||||
HeapTuple ruletup;
|
||||
TupleDesc rulettc;
|
||||
StringInfoData buf;
|
||||
int len;
|
||||
char *name;
|
||||
char *name;
|
||||
|
||||
/* ----------
|
||||
* We need the view name somewhere deep down
|
||||
|
@ -276,7 +271,6 @@ pg_get_viewdef(PG_FUNCTION_ARGS)
|
|||
name = MakeRetrieveViewRuleName(rulename);
|
||||
args[0] = PointerGetDatum(name);
|
||||
nulls[0] = ' ';
|
||||
nulls[1] = '\0';
|
||||
spirc = SPI_execp(plan_getview, args, nulls, 1);
|
||||
if (spirc != SPI_OK_SELECT)
|
||||
elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
|
||||
|
@ -883,60 +877,8 @@ get_select_query_def(Query *query, deparse_context *context)
|
|||
{
|
||||
StringInfo buf = context->buf;
|
||||
char *sep;
|
||||
TargetEntry *tle;
|
||||
RangeTblEntry *rte;
|
||||
bool *rt_used;
|
||||
int rt_length;
|
||||
int rt_numused = 0;
|
||||
bool rt_constonly = TRUE;
|
||||
int i;
|
||||
List *l;
|
||||
|
||||
/* ----------
|
||||
* First we need to know which and how many of the
|
||||
* range table entries in the query are used in the target list
|
||||
* or queries qualification
|
||||
* ----------
|
||||
*/
|
||||
rt_length = length(query->rtable);
|
||||
rt_used = palloc(sizeof(bool) * rt_length);
|
||||
for (i = 0; i < rt_length; i++)
|
||||
{
|
||||
if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
|
||||
check_if_rte_used(query->qual, i + 1, 0) ||
|
||||
check_if_rte_used(query->havingQual, i + 1, 0))
|
||||
{
|
||||
rt_used[i] = TRUE;
|
||||
rt_numused++;
|
||||
}
|
||||
else
|
||||
rt_used[i] = FALSE;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Now check if any of the used rangetable entries is different
|
||||
* from *NEW* and *OLD*. If so we must provide the FROM clause
|
||||
* later.
|
||||
* ----------
|
||||
*/
|
||||
i = 0;
|
||||
foreach(l, query->rtable)
|
||||
{
|
||||
if (!rt_used[i++])
|
||||
continue;
|
||||
|
||||
rte = (RangeTblEntry *) lfirst(l);
|
||||
if (rte->ref == NULL)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
|
||||
rt_constonly = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Build up the query string - first we say SELECT
|
||||
* ----------
|
||||
|
@ -947,9 +889,9 @@ get_select_query_def(Query *query, deparse_context *context)
|
|||
sep = " ";
|
||||
foreach(l, query->targetList)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
bool tell_as = false;
|
||||
|
||||
tle = (TargetEntry *) lfirst(l);
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
|
||||
|
@ -962,6 +904,7 @@ get_select_query_def(Query *query, deparse_context *context)
|
|||
else
|
||||
{
|
||||
Var *var = (Var *) (tle->expr);
|
||||
RangeTblEntry *rte;
|
||||
char *attname;
|
||||
|
||||
rte = get_rte_for_var(var, context);
|
||||
|
@ -975,60 +918,8 @@ get_select_query_def(Query *query, deparse_context *context)
|
|||
quote_identifier(tle->resdom->resname));
|
||||
}
|
||||
|
||||
/* If we need other tables than *NEW* or *OLD* add the FROM clause */
|
||||
if (!rt_constonly && rt_numused > 0)
|
||||
{
|
||||
sep = " FROM ";
|
||||
i = 0;
|
||||
foreach(l, query->rtable)
|
||||
{
|
||||
if (rt_used[i++])
|
||||
{
|
||||
rte = (RangeTblEntry *) lfirst(l);
|
||||
|
||||
if (rte->ref == NULL)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
appendStringInfo(buf, "%s%s",
|
||||
only_marker(rte),
|
||||
quote_identifier(rte->relname));
|
||||
|
||||
/*
|
||||
* NOTE: SQL92 says you can't write column aliases unless
|
||||
* you write a table alias --- so, if there's an alias
|
||||
* list, make sure we emit a table alias even if it's the
|
||||
* same as the table's real name.
|
||||
*/
|
||||
if ((rte->ref != NULL)
|
||||
&& ((strcmp(rte->relname, rte->ref->relname) != 0)
|
||||
|| (rte->ref->attrs != NIL)))
|
||||
{
|
||||
appendStringInfo(buf, " %s",
|
||||
quote_identifier(rte->ref->relname));
|
||||
if (rte->ref->attrs != NIL)
|
||||
{
|
||||
List *col;
|
||||
|
||||
appendStringInfo(buf, " (");
|
||||
foreach(col, rte->ref->attrs)
|
||||
{
|
||||
if (col != rte->ref->attrs)
|
||||
appendStringInfo(buf, ", ");
|
||||
appendStringInfo(buf, "%s",
|
||||
quote_identifier(strVal(lfirst(col))));
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add the FROM clause if needed */
|
||||
get_from_clause(query, context);
|
||||
|
||||
/* Add the WHERE clause if given */
|
||||
if (query->qual != NULL)
|
||||
|
@ -1066,52 +957,32 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||
{
|
||||
StringInfo buf = context->buf;
|
||||
char *sep;
|
||||
TargetEntry *tle;
|
||||
RangeTblEntry *rte;
|
||||
bool *rt_used;
|
||||
int rt_length;
|
||||
int rt_numused = 0;
|
||||
bool rt_constonly = TRUE;
|
||||
RangeTblEntry *rte;
|
||||
int i;
|
||||
List *l;
|
||||
|
||||
/* ----------
|
||||
* We need to know if other tables than *NEW* or *OLD*
|
||||
* are used in the query. If not, it's an INSERT ... VALUES,
|
||||
* otherwise an INSERT ... SELECT.
|
||||
* otherwise an INSERT ... SELECT. (Pretty klugy ... fix this
|
||||
* when we redesign querytrees!)
|
||||
* ----------
|
||||
*/
|
||||
rt_length = length(query->rtable);
|
||||
rt_used = palloc(sizeof(bool) * rt_length);
|
||||
for (i = 0; i < rt_length; i++)
|
||||
{
|
||||
if (check_if_rte_used((Node *) (query->targetList), i + 1, 0) ||
|
||||
check_if_rte_used(query->qual, i + 1, 0) ||
|
||||
check_if_rte_used(query->havingQual, i + 1, 0))
|
||||
{
|
||||
rt_used[i] = TRUE;
|
||||
rt_numused++;
|
||||
}
|
||||
else
|
||||
rt_used[i] = FALSE;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
foreach(l, query->rtable)
|
||||
{
|
||||
if (!rt_used[i++])
|
||||
continue;
|
||||
|
||||
rte = (RangeTblEntry *) lfirst(l);
|
||||
if (rte->ref == NULL)
|
||||
i++;
|
||||
if (strcmp(rte->eref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*NEW*") == 0)
|
||||
if (strcmp(rte->eref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->ref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
|
||||
rt_constonly = FALSE;
|
||||
break;
|
||||
if (rangeTableEntry_used((Node *) query, i, 0))
|
||||
{
|
||||
rt_constonly = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------
|
||||
|
@ -1122,11 +993,11 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||
appendStringInfo(buf, "INSERT INTO %s",
|
||||
quote_identifier(rte->relname));
|
||||
|
||||
/* Add the target list */
|
||||
/* Add the insert-column-names list */
|
||||
sep = " (";
|
||||
foreach(l, query->targetList)
|
||||
{
|
||||
tle = (TargetEntry *) lfirst(l);
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
|
@ -1141,7 +1012,7 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||
sep = "";
|
||||
foreach(l, query->targetList)
|
||||
{
|
||||
tle = (TargetEntry *) lfirst(l);
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
|
@ -1195,6 +1066,9 @@ get_update_query_def(Query *query, deparse_context *context)
|
|||
get_tle_expr(tle, context);
|
||||
}
|
||||
|
||||
/* Add the FROM clause if needed */
|
||||
get_from_clause(query, context);
|
||||
|
||||
/* Finally add a WHERE clause if given */
|
||||
if (query->qual != NULL)
|
||||
{
|
||||
|
@ -1281,16 +1155,13 @@ get_rule_expr(Node *node, deparse_context *context)
|
|||
|
||||
if (context->varprefix)
|
||||
{
|
||||
if (rte->ref == NULL)
|
||||
appendStringInfo(buf, "%s.",
|
||||
quote_identifier(rte->relname));
|
||||
else if (strcmp(rte->ref->relname, "*NEW*") == 0)
|
||||
if (strcmp(rte->eref->relname, "*NEW*") == 0)
|
||||
appendStringInfo(buf, "new.");
|
||||
else if (strcmp(rte->ref->relname, "*OLD*") == 0)
|
||||
else if (strcmp(rte->eref->relname, "*OLD*") == 0)
|
||||
appendStringInfo(buf, "old.");
|
||||
else
|
||||
appendStringInfo(buf, "%s.",
|
||||
quote_identifier(rte->ref->relname));
|
||||
quote_identifier(rte->eref->relname));
|
||||
}
|
||||
appendStringInfo(buf, "%s",
|
||||
quote_identifier(get_attribute_name(rte->relid,
|
||||
|
@ -1860,6 +1731,165 @@ get_sublink_expr(Node *node, deparse_context *context)
|
|||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* get_from_clause - Parse back a FROM clause
|
||||
* ----------
|
||||
*/
|
||||
static void
|
||||
get_from_clause(Query *query, deparse_context *context)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
char *sep;
|
||||
List *l;
|
||||
|
||||
/*
|
||||
* We use the query's jointree as a guide to what to print. However,
|
||||
* we must ignore auto-added RTEs that are marked not inFromCl.
|
||||
* Also ignore the rule pseudo-RTEs for NEW and OLD.
|
||||
*/
|
||||
sep = " FROM ";
|
||||
|
||||
foreach(l, query->jointree)
|
||||
{
|
||||
Node *jtnode = (Node *) lfirst(l);
|
||||
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, query->rtable);
|
||||
|
||||
if (!rte->inFromCl)
|
||||
continue;
|
||||
if (strcmp(rte->eref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->eref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
get_from_clause_item(jtnode, query, context);
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, query->rtable);
|
||||
|
||||
appendStringInfo(buf, "%s%s",
|
||||
only_marker(rte),
|
||||
quote_identifier(rte->relname));
|
||||
if (rte->alias != NULL)
|
||||
{
|
||||
appendStringInfo(buf, " %s",
|
||||
quote_identifier(rte->alias->relname));
|
||||
if (rte->alias->attrs != NIL)
|
||||
{
|
||||
List *col;
|
||||
|
||||
appendStringInfo(buf, " (");
|
||||
foreach(col, rte->alias->attrs)
|
||||
{
|
||||
if (col != rte->alias->attrs)
|
||||
appendStringInfo(buf, ", ");
|
||||
appendStringInfo(buf, "%s",
|
||||
quote_identifier(strVal(lfirst(col))));
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
appendStringInfoChar(buf, '(');
|
||||
get_from_clause_item(j->larg, query, context);
|
||||
if (j->isNatural)
|
||||
appendStringInfo(buf, " NATURAL");
|
||||
switch (j->jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
if (j->quals)
|
||||
appendStringInfo(buf, " JOIN ");
|
||||
else
|
||||
appendStringInfo(buf, " CROSS JOIN ");
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
appendStringInfo(buf, " LEFT JOIN ");
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
appendStringInfo(buf, " FULL JOIN ");
|
||||
break;
|
||||
case JOIN_RIGHT:
|
||||
appendStringInfo(buf, " RIGHT JOIN ");
|
||||
break;
|
||||
case JOIN_UNION:
|
||||
appendStringInfo(buf, " UNION JOIN ");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "get_from_clause_item: unknown join type %d",
|
||||
(int) j->jointype);
|
||||
}
|
||||
get_from_clause_item(j->rarg, query, context);
|
||||
if (! j->isNatural)
|
||||
{
|
||||
if (j->using)
|
||||
{
|
||||
List *col;
|
||||
|
||||
appendStringInfo(buf, " USING (");
|
||||
foreach(col, j->using)
|
||||
{
|
||||
if (col != j->using)
|
||||
appendStringInfo(buf, ", ");
|
||||
appendStringInfo(buf, "%s",
|
||||
quote_identifier(strVal(lfirst(col))));
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
else if (j->quals)
|
||||
{
|
||||
appendStringInfo(buf, " ON (");
|
||||
get_rule_expr(j->quals, context);
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
/* Yes, it's correct to put alias after the right paren ... */
|
||||
if (j->alias != NULL)
|
||||
{
|
||||
appendStringInfo(buf, " %s",
|
||||
quote_identifier(j->alias->relname));
|
||||
if (j->alias->attrs != NIL)
|
||||
{
|
||||
List *col;
|
||||
|
||||
appendStringInfo(buf, " (");
|
||||
foreach(col, j->alias->attrs)
|
||||
{
|
||||
if (col != j->alias->attrs)
|
||||
appendStringInfo(buf, ", ");
|
||||
appendStringInfo(buf, "%s",
|
||||
quote_identifier(strVal(lfirst(col))));
|
||||
}
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "get_from_clause_item: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* tleIsArrayAssign - check for array assignment
|
||||
* ----------
|
||||
|
@ -1990,56 +2020,3 @@ get_attribute_name(Oid relid, int2 attnum)
|
|||
attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
|
||||
return pstrdup(NameStr(attStruct->attname));
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* check_if_rte_used
|
||||
* Check a targetlist or qual to see if a given rangetable entry
|
||||
* is used in it
|
||||
* ----------
|
||||
*/
|
||||
static bool
|
||||
check_if_rte_used(Node *node, Index rt_index, int levelsup)
|
||||
{
|
||||
check_if_rte_used_context context;
|
||||
|
||||
context.rt_index = rt_index;
|
||||
context.levelsup = levelsup;
|
||||
return check_if_rte_used_walker(node, &context);
|
||||
}
|
||||
|
||||
static bool
|
||||
check_if_rte_used_walker(Node *node,
|
||||
check_if_rte_used_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
return var->varno == context->rt_index &&
|
||||
var->varlevelsup == context->levelsup;
|
||||
}
|
||||
if (IsA(node, SubLink))
|
||||
{
|
||||
SubLink *sublink = (SubLink *) node;
|
||||
Query *query = (Query *) sublink->subselect;
|
||||
|
||||
/* Recurse into subquery; expression_tree_walker will not */
|
||||
if (check_if_rte_used((Node *) (query->targetList),
|
||||
context->rt_index, context->levelsup + 1) ||
|
||||
check_if_rte_used(query->qual,
|
||||
context->rt_index, context->levelsup + 1) ||
|
||||
check_if_rte_used(query->havingQual,
|
||||
context->rt_index, context->levelsup + 1))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* fall through to let expression_tree_walker examine lefthand
|
||||
* args
|
||||
*/
|
||||
}
|
||||
return expression_tree_walker(node, check_if_rte_used_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: catversion.h,v 1.44 2000/09/12 04:49:15 momjian Exp $
|
||||
* $Id: catversion.h,v 1.45 2000/09/12 21:07:06 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -53,6 +53,6 @@
|
|||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200009111
|
||||
#define CATALOG_VERSION_NO 200009121
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execdebug.h,v 1.13 2000/06/15 00:52:07 momjian Exp $
|
||||
* $Id: execdebug.h,v 1.14 2000/09/12 21:07:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -128,16 +128,6 @@
|
|||
#undef EXEC_MERGEJOINDEBUG
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* EXEC_MERGEJOINPFREE is a flag which causes merge joins
|
||||
* to pfree intermittant tuples (which is the proper thing)
|
||||
* Not defining this means we avoid menory management problems
|
||||
* at the cost of doing deallocation of stuff only at the
|
||||
* end of the transaction
|
||||
* ----------------
|
||||
#undef EXEC_MERGEJOINPFREE
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* EXEC_DEBUGINTERACTIVE is a flag which enables the
|
||||
* user to issue "DEBUG" commands from an interactive
|
||||
|
@ -170,11 +160,10 @@
|
|||
* only as necessary -cim 10/26/89
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#define T_OR_F(b) (b ? "true" : "false")
|
||||
#define T_OR_F(b) ((b) ? "true" : "false")
|
||||
#define NULL_OR_TUPLE(slot) (TupIsNull(slot) ? "null" : "a tuple")
|
||||
|
||||
|
||||
/* #define EXEC_TUPLECOUNT - XXX take out for now for executor stubbing -- jolly*/
|
||||
/* ----------------
|
||||
* tuple count debugging defines
|
||||
* ----------------
|
||||
|
@ -326,28 +315,31 @@ extern int NIndexTupleInserted;
|
|||
#define MJ1_printf(s, p) printf(s, p)
|
||||
#define MJ2_printf(s, p1, p2) printf(s, p1, p2)
|
||||
#define MJ_debugtup(tuple, type) debugtup(tuple, type, NULL)
|
||||
#define MJ_dump(context, state) ExecMergeTupleDump(econtext, state)
|
||||
#define MJ_dump(state) ExecMergeTupleDump(state)
|
||||
#define MJ_DEBUG_QUAL(clause, res) \
|
||||
MJ2_printf(" ExecQual(%s, econtext) returns %s\n", \
|
||||
CppAsString(clause), T_OR_F(res));
|
||||
|
||||
#define MJ_DEBUG_MERGE_COMPARE(qual, res) \
|
||||
MJ2_printf(" MergeCompare(mergeclauses, %s, ..) returns %s\n", \
|
||||
MJ2_printf(" MergeCompare(mergeclauses, %s, ...) returns %s\n", \
|
||||
CppAsString(qual), T_OR_F(res));
|
||||
|
||||
#define MJ_DEBUG_PROC_NODE(slot) \
|
||||
MJ2_printf(" %s = ExecProcNode(innerPlan) returns %s\n", \
|
||||
MJ2_printf(" %s = ExecProcNode(...) returns %s\n", \
|
||||
CppAsString(slot), NULL_OR_TUPLE(slot));
|
||||
|
||||
#else
|
||||
|
||||
#define MJ_nodeDisplay(l)
|
||||
#define MJ_printf(s)
|
||||
#define MJ1_printf(s, p)
|
||||
#define MJ2_printf(s, p1, p2)
|
||||
#define MJ_debugtup(tuple, type)
|
||||
#define MJ_dump(context, state)
|
||||
#define MJ_dump(state)
|
||||
#define MJ_DEBUG_QUAL(clause, res)
|
||||
#define MJ_DEBUG_MERGE_COMPARE(qual, res)
|
||||
#define MJ_DEBUG_PROC_NODE(slot)
|
||||
|
||||
#endif /* EXEC_MERGEJOINDEBUG */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execdefs.h,v 1.6 2000/01/26 05:58:05 momjian Exp $
|
||||
* $Id: execdefs.h,v 1.7 2000/09/12 21:07:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -42,9 +42,13 @@
|
|||
#define EXEC_MJ_NEXTOUTER 5
|
||||
#define EXEC_MJ_TESTOUTER 6
|
||||
#define EXEC_MJ_NEXTINNER 7
|
||||
#define EXEC_MJ_SKIPINNER 8
|
||||
#define EXEC_MJ_SKIPOUTER 9
|
||||
#define EXEC_MJ_FILLINNER 10
|
||||
#define EXEC_MJ_FILLOUTER 11
|
||||
#define EXEC_MJ_SKIPOUTER_BEGIN 8
|
||||
#define EXEC_MJ_SKIPOUTER_TEST 9
|
||||
#define EXEC_MJ_SKIPOUTER_ADVANCE 10
|
||||
#define EXEC_MJ_SKIPINNER_BEGIN 11
|
||||
#define EXEC_MJ_SKIPINNER_TEST 12
|
||||
#define EXEC_MJ_SKIPINNER_ADVANCE 13
|
||||
#define EXEC_MJ_ENDOUTER 14
|
||||
#define EXEC_MJ_ENDINNER 15
|
||||
|
||||
#endif /* EXECDEFS_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: executor.h,v 1.50 2000/08/24 23:34:09 tgl Exp $
|
||||
* $Id: executor.h,v 1.51 2000/09/12 21:07:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -117,7 +117,9 @@ extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
|
|||
extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
|
||||
extern void ExecInitScanTupleSlot(EState *estate,
|
||||
CommonScanState *commonscanstate);
|
||||
extern void ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate);
|
||||
extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
|
||||
extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
|
||||
TupleDesc tupType);
|
||||
|
||||
extern TupleDesc ExecGetTupType(Plan *node);
|
||||
extern TupleDesc ExecTypeFromTL(List *targetList);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execnodes.h,v 1.48 2000/08/24 03:29:13 tgl Exp $
|
||||
* $Id: execnodes.h,v 1.49 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -469,11 +469,18 @@ typedef CommonState JoinState;
|
|||
|
||||
/* ----------------
|
||||
* NestLoopState information
|
||||
*
|
||||
* NeedNewOuter true if need new outer tuple on next call
|
||||
* MatchedOuter true if found a join match for current outer tuple
|
||||
* NullInnerTupleSlot prepared null tuple for left outer joins
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct NestLoopState
|
||||
{
|
||||
JoinState jstate; /* its first field is NodeTag */
|
||||
bool nl_NeedNewOuter;
|
||||
bool nl_MatchedOuter;
|
||||
TupleTableSlot *nl_NullInnerTupleSlot;
|
||||
} NestLoopState;
|
||||
|
||||
/* ----------------
|
||||
|
@ -482,7 +489,13 @@ typedef struct NestLoopState
|
|||
* OuterSkipQual outerKey1 < innerKey1 ...
|
||||
* InnerSkipQual outerKey1 > innerKey1 ...
|
||||
* JoinState current "state" of join. see executor.h
|
||||
* MatchedOuter true if found a join match for current outer tuple
|
||||
* MatchedInner true if found a join match for current inner tuple
|
||||
* OuterTupleSlot pointer to slot in tuple table for cur outer tuple
|
||||
* InnerTupleSlot pointer to slot in tuple table for cur inner tuple
|
||||
* MarkedTupleSlot pointer to slot in tuple table for marked tuple
|
||||
* NullOuterTupleSlot prepared null tuple for right outer joins
|
||||
* NullInnerTupleSlot prepared null tuple for left outer joins
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct MergeJoinState
|
||||
|
@ -491,7 +504,13 @@ typedef struct MergeJoinState
|
|||
List *mj_OuterSkipQual;
|
||||
List *mj_InnerSkipQual;
|
||||
int mj_JoinState;
|
||||
bool mj_MatchedOuter;
|
||||
bool mj_MatchedInner;
|
||||
TupleTableSlot *mj_OuterTupleSlot;
|
||||
TupleTableSlot *mj_InnerTupleSlot;
|
||||
TupleTableSlot *mj_MarkedTupleSlot;
|
||||
TupleTableSlot *mj_NullOuterTupleSlot;
|
||||
TupleTableSlot *mj_NullInnerTupleSlot;
|
||||
} MergeJoinState;
|
||||
|
||||
/* ----------------
|
||||
|
@ -506,6 +525,10 @@ typedef struct MergeJoinState
|
|||
* hj_InnerHashKey the inner hash key in the hashjoin condition
|
||||
* hj_OuterTupleSlot tuple slot for outer tuples
|
||||
* hj_HashTupleSlot tuple slot for hashed tuples
|
||||
* hj_NullInnerTupleSlot prepared null tuple for left outer joins
|
||||
* hj_NeedNewOuter true if need new outer tuple on next call
|
||||
* hj_MatchedOuter true if found a join match for current outer
|
||||
* hj_hashdone true if hash-table-build phase is done
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct HashJoinState
|
||||
|
@ -517,6 +540,10 @@ typedef struct HashJoinState
|
|||
Node *hj_InnerHashKey;
|
||||
TupleTableSlot *hj_OuterTupleSlot;
|
||||
TupleTableSlot *hj_HashTupleSlot;
|
||||
TupleTableSlot *hj_NullInnerTupleSlot;
|
||||
bool hj_NeedNewOuter;
|
||||
bool hj_MatchedOuter;
|
||||
bool hj_hashdone;
|
||||
} HashJoinState;
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodes.h,v 1.75 2000/08/24 03:29:13 tgl Exp $
|
||||
* $Id: nodes.h,v 1.76 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -68,6 +68,8 @@ typedef enum NodeTag
|
|||
T_ArrayRef,
|
||||
T_Iter,
|
||||
T_RelabelType,
|
||||
T_RangeTblRef,
|
||||
T_JoinExpr,
|
||||
|
||||
/*---------------------
|
||||
* TAGS FOR PLANNER NODES (relation.h)
|
||||
|
@ -204,7 +206,7 @@ typedef enum NodeTag
|
|||
T_A_Indices,
|
||||
T_ResTarget,
|
||||
T_TypeCast,
|
||||
T_RelExpr,
|
||||
T_RangeSubselect,
|
||||
T_SortGroupBy,
|
||||
T_RangeVar,
|
||||
T_TypeName,
|
||||
|
@ -217,14 +219,14 @@ typedef enum NodeTag
|
|||
T_SortClause,
|
||||
T_GroupClause,
|
||||
T_SubSelectXXX, /* not used anymore; this tag# is available */
|
||||
T_JoinExpr,
|
||||
T_oldJoinExprXXX, /* not used anymore; this tag# is available */
|
||||
T_CaseExpr,
|
||||
T_CaseWhen,
|
||||
T_RowMark,
|
||||
T_FkConstraint,
|
||||
|
||||
/*---------------------
|
||||
* TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (cf. fmgr.h)
|
||||
* TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
|
||||
*---------------------
|
||||
*/
|
||||
T_TriggerData = 800, /* in commands/trigger.h */
|
||||
|
@ -310,7 +312,7 @@ typedef double Cost; /* execution cost (in page-access units) */
|
|||
|
||||
/*
|
||||
* CmdType -
|
||||
* enums for type of operation to aid debugging
|
||||
* enums for type of operation represented by a Query
|
||||
*
|
||||
* ??? could have put this in parsenodes.h but many files not in the
|
||||
* optimizer also need this...
|
||||
|
@ -329,4 +331,40 @@ typedef enum CmdType
|
|||
} CmdType;
|
||||
|
||||
|
||||
/*
|
||||
* JoinType -
|
||||
* enums for types of relation joins
|
||||
*
|
||||
* JoinType determines the exact semantics of joining two relations using
|
||||
* a matching qualification. For example, it tells what to do with a tuple
|
||||
* that has no match in the other relation.
|
||||
*
|
||||
* This is needed in both parsenodes.h and plannodes.h, so put it here...
|
||||
*/
|
||||
typedef enum JoinType
|
||||
{
|
||||
/*
|
||||
* The canonical kinds of joins
|
||||
*/
|
||||
JOIN_INNER, /* matching tuple pairs only */
|
||||
JOIN_LEFT, /* pairs + unmatched outer tuples */
|
||||
JOIN_FULL, /* pairs + unmatched outer + unmatched inner */
|
||||
JOIN_RIGHT, /* pairs + unmatched inner tuples */
|
||||
/*
|
||||
* SQL92 considers UNION JOIN to be a kind of join, so list it here for
|
||||
* parser convenience, even though it's not implemented like a join in
|
||||
* the executor. (The planner must convert it to an Append plan.)
|
||||
*/
|
||||
JOIN_UNION
|
||||
/*
|
||||
* Eventually we will have some additional join types for efficient
|
||||
* support of queries like WHERE foo IN (SELECT bar FROM ...).
|
||||
*/
|
||||
} JoinType;
|
||||
|
||||
#define IS_OUTER_JOIN(jointype) \
|
||||
((jointype) == JOIN_LEFT || \
|
||||
(jointype) == JOIN_FULL || \
|
||||
(jointype) == JOIN_RIGHT)
|
||||
|
||||
#endif /* NODES_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parsenodes.h,v 1.112 2000/09/12 05:09:50 momjian Exp $
|
||||
* $Id: parsenodes.h,v 1.113 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -40,7 +40,7 @@ typedef struct Query
|
|||
Node *utilityStmt; /* non-null if this is a non-optimizable
|
||||
* statement */
|
||||
|
||||
int resultRelation; /* target relation (index to rtable) */
|
||||
int resultRelation; /* target relation (index into rtable) */
|
||||
char *into; /* portal (cursor) name */
|
||||
bool isPortal; /* is this a retrieve into portal? */
|
||||
bool isBinary; /* binary portal? */
|
||||
|
@ -50,6 +50,8 @@ typedef struct Query
|
|||
bool hasSubLinks; /* has subquery SubLink */
|
||||
|
||||
List *rtable; /* list of range table entries */
|
||||
List *jointree; /* table join tree (from the FROM clause) */
|
||||
|
||||
List *targetList; /* target list (of TargetEntry) */
|
||||
Node *qual; /* qualifications applied to tuples */
|
||||
List *rowMark; /* list of RowMark entries */
|
||||
|
@ -1057,16 +1059,6 @@ typedef struct ResTarget
|
|||
* assign */
|
||||
} ResTarget;
|
||||
|
||||
/*
|
||||
* RelExpr - relation expressions
|
||||
*/
|
||||
typedef struct RelExpr
|
||||
{
|
||||
NodeTag type;
|
||||
char *relname; /* the relation name */
|
||||
bool inh; /* inheritance query */
|
||||
} RelExpr;
|
||||
|
||||
/*
|
||||
* SortGroupBy - for ORDER BY clause
|
||||
*/
|
||||
|
@ -1083,10 +1075,21 @@ typedef struct SortGroupBy
|
|||
typedef struct RangeVar
|
||||
{
|
||||
NodeTag type;
|
||||
RelExpr *relExpr; /* the relation expression */
|
||||
Attr *name; /* the name to be referenced (optional) */
|
||||
char *relname; /* the relation name */
|
||||
bool inh; /* expand rel by inheritance? */
|
||||
Attr *name; /* optional table alias & column aliases */
|
||||
} RangeVar;
|
||||
|
||||
/*
|
||||
* RangeSubselect - subquery appearing in a FROM clause
|
||||
*/
|
||||
typedef struct RangeSubselect
|
||||
{
|
||||
NodeTag type;
|
||||
Node *subquery; /* the untransformed sub-select clause */
|
||||
Attr *name; /* optional table alias & column aliases */
|
||||
} RangeSubselect;
|
||||
|
||||
/*
|
||||
* IndexElem - index parameters (used in CREATE INDEX)
|
||||
*
|
||||
|
@ -1114,20 +1117,6 @@ typedef struct DefElem
|
|||
Node *arg; /* a (Value *) or a (TypeName *) */
|
||||
} DefElem;
|
||||
|
||||
/*
|
||||
* JoinExpr - for JOIN expressions
|
||||
*/
|
||||
typedef struct JoinExpr
|
||||
{
|
||||
NodeTag type;
|
||||
int jointype;
|
||||
bool isNatural; /* Natural join? Will need to shape table */
|
||||
Node *larg; /* RangeVar or join expression */
|
||||
Node *rarg; /* RangeVar or join expression */
|
||||
Attr *alias; /* table and column aliases, if any */
|
||||
List *quals; /* qualifiers on join, if any */
|
||||
} JoinExpr;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Nodes for a Query tree
|
||||
|
@ -1155,11 +1144,12 @@ typedef struct TargetEntry
|
|||
* Some of the following are only used in one of
|
||||
* the parsing, optimizing, execution stages.
|
||||
*
|
||||
* eref is the expanded table name and columns for the underlying
|
||||
* relation. Note that for outer join syntax, allowed reference names
|
||||
* could be modified as one evaluates the nested clauses (e.g.
|
||||
* "SELECT ... FROM t1 NATURAL JOIN t2 WHERE ..." forbids explicit mention
|
||||
* of a table name in any reference to the join column.
|
||||
* alias is an Attr node representing the AS alias-clause attached to the
|
||||
* FROM expression, or NULL if no clause.
|
||||
*
|
||||
* eref is the table reference name and column reference names (either
|
||||
* real or aliases). This is filled in during parse analysis. Note that
|
||||
* system columns (OID etc) are not included in the column list.
|
||||
*
|
||||
* inFromCl marks those range variables that are listed in the FROM clause.
|
||||
* In SQL, the query can only refer to range variables listed in the
|
||||
|
@ -1170,29 +1160,17 @@ typedef struct TargetEntry
|
|||
* implicitly-added RTE shouldn't change the namespace for unqualified
|
||||
* column names processed later, and it also shouldn't affect the
|
||||
* expansion of '*'.
|
||||
*
|
||||
* inJoinSet marks those range variables that the planner should join
|
||||
* over even if they aren't explicitly referred to in the query. For
|
||||
* example, "SELECT COUNT(1) FROM tx" should produce the number of rows
|
||||
* in tx. A more subtle example uses a POSTQUEL implicit RTE:
|
||||
* SELECT COUNT(1) FROM tx WHERE TRUE OR (tx.f1 = ty.f2)
|
||||
* Here we should get the product of the sizes of tx and ty. However,
|
||||
* the query optimizer can simplify the WHERE clause to "TRUE", so
|
||||
* ty will no longer be referred to explicitly; without a flag forcing
|
||||
* it to be included in the join, we will get the wrong answer. So,
|
||||
* a POSTQUEL implicit RTE must be marked inJoinSet but not inFromCl.
|
||||
*--------------------
|
||||
*/
|
||||
typedef struct RangeTblEntry
|
||||
{
|
||||
NodeTag type;
|
||||
char *relname; /* real name of the relation */
|
||||
Attr *ref; /* reference names (given in FROM clause) */
|
||||
Attr *eref; /* expanded reference names */
|
||||
Oid relid; /* OID of the relation */
|
||||
Attr *alias; /* user-written alias clause, if any */
|
||||
Attr *eref; /* expanded reference names */
|
||||
bool inh; /* inheritance requested? */
|
||||
bool inFromCl; /* present in FROM clause */
|
||||
bool inJoinSet; /* planner must include this rel */
|
||||
bool skipAcl; /* skip ACL check in executor */
|
||||
} RangeTblEntry;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pg_list.h,v 1.18 2000/06/09 01:44:26 momjian Exp $
|
||||
* $Id: pg_list.h,v 1.19 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -101,6 +101,7 @@ extern List *nconc(List *list1, List *list2);
|
|||
extern List *lcons(void *datum, List *list);
|
||||
extern List *lconsi(int datum, List *list);
|
||||
extern bool member(void *datum, List *list);
|
||||
extern bool ptrMember(void *datum, List *list);
|
||||
extern bool intMember(int datum, List *list);
|
||||
extern Value *makeInteger(long i);
|
||||
extern Value *makeFloat(char *numericStr);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: plannodes.h,v 1.41 2000/07/12 02:37:33 tgl Exp $
|
||||
* $Id: plannodes.h,v 1.42 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -82,7 +82,7 @@ typedef struct Plan
|
|||
* individual nodes point to one EState
|
||||
* for the whole top-level plan */
|
||||
List *targetlist;
|
||||
List *qual; /* Node* or List* ?? */
|
||||
List *qual; /* implicitly-ANDed qual conditions */
|
||||
struct Plan *lefttree;
|
||||
struct Plan *righttree;
|
||||
List *extParam; /* indices of _all_ _external_ PARAM_EXEC
|
||||
|
@ -210,9 +210,26 @@ typedef struct TidScan
|
|||
|
||||
/* ----------------
|
||||
* Join node
|
||||
*
|
||||
* jointype: rule for joining tuples from left and right subtrees
|
||||
* joinqual: qual conditions that came from JOIN/ON or JOIN/USING
|
||||
* (plan.qual contains conditions that came from WHERE)
|
||||
*
|
||||
* When jointype is INNER, joinqual and plan.qual are semantically
|
||||
* interchangeable. For OUTER jointypes, the two are *not* interchangeable;
|
||||
* only joinqual is used to determine whether a match has been found for
|
||||
* the purpose of deciding whether to generate null-extended tuples.
|
||||
* (But plan.qual is still applied before actually returning a tuple.)
|
||||
* For an outer join, only joinquals are allowed to be used as the merge
|
||||
* or hash condition of a merge or hash join.
|
||||
* ----------------
|
||||
*/
|
||||
typedef Plan Join;
|
||||
typedef struct Join
|
||||
{
|
||||
Plan plan;
|
||||
JoinType jointype;
|
||||
List *joinqual; /* JOIN quals (in addition to plan.qual) */
|
||||
} Join;
|
||||
|
||||
/* ----------------
|
||||
* nest loop join node
|
||||
|
@ -245,7 +262,6 @@ typedef struct HashJoin
|
|||
List *hashclauses;
|
||||
Oid hashjoinop;
|
||||
HashJoinState *hashjoinstate;
|
||||
bool hashdone;
|
||||
} HashJoin;
|
||||
|
||||
/* ---------------
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* primnodes.h
|
||||
* Definitions for parse tree/query tree ("primitive") nodes.
|
||||
* Definitions for "primitive" node types, those that are used in more
|
||||
* than one of the parse/plan/execute stages of the query pipeline.
|
||||
* Currently, these are mostly nodes for executable expressions
|
||||
* and join trees.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: primnodes.h,v 1.47 2000/08/24 03:29:13 tgl Exp $
|
||||
* $Id: primnodes.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -98,6 +101,12 @@ typedef struct Fjoin
|
|||
BoolPtr fj_alwaysDone;
|
||||
} Fjoin;
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* node types for executable expressions
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* Expr
|
||||
* typeOid - oid of the type of this expression
|
||||
|
@ -155,7 +164,7 @@ typedef struct Var
|
|||
AttrNumber varattno;
|
||||
Oid vartype;
|
||||
int32 vartypmod;
|
||||
Index varlevelsup; /* erased by upper optimizer */
|
||||
Index varlevelsup;
|
||||
Index varnoold; /* mainly for debugging --- see above */
|
||||
AttrNumber varoattno;
|
||||
} Var;
|
||||
|
@ -480,4 +489,76 @@ typedef struct RelabelType
|
|||
int32 resulttypmod;
|
||||
} RelabelType;
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* node types for join trees
|
||||
*
|
||||
* The leaves of a join tree structure are RangeTblRef nodes. Above
|
||||
* these, JoinExpr nodes can appear to denote a specific kind of join
|
||||
* or qualified join. A join tree can also contain List nodes --- a list
|
||||
* implies an unqualified cross-product join of its members. The planner
|
||||
* is allowed to combine the elements of a list using whatever join order
|
||||
* seems good to it. At present, JoinExpr nodes are always joined in
|
||||
* exactly the order implied by the tree structure (except the planner
|
||||
* may choose to swap inner and outer members of a join pair).
|
||||
*
|
||||
* NOTE: currently, the planner only supports a List at the top level of
|
||||
* a join tree. Should generalize this to allow Lists at lower levels.
|
||||
*
|
||||
* NOTE: the qualification expressions present in JoinExpr nodes are
|
||||
* *in addition to* the query's main WHERE clause. For outer joins there
|
||||
* is a real semantic difference between a join qual and a WHERE clause,
|
||||
* though if all joins are inner joins they are interchangeable.
|
||||
*
|
||||
* NOTE: in the raw output of gram.y, a join tree contains RangeVar and
|
||||
* RangeSubselect nodes, which are both replaced by RangeTblRef nodes
|
||||
* during the parse analysis phase.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* RangeTblRef - reference to an entry in the query's rangetable
|
||||
*
|
||||
* We could use direct pointers to the RT entries and skip having these
|
||||
* nodes, but multiple pointers to the same node in a querytree cause
|
||||
* lots of headaches, so it seems better to store an index into the RT.
|
||||
*/
|
||||
typedef struct RangeTblRef
|
||||
{
|
||||
NodeTag type;
|
||||
int rtindex;
|
||||
} RangeTblRef;
|
||||
|
||||
/*----------
|
||||
* JoinExpr - for SQL JOIN expressions
|
||||
*
|
||||
* isNatural, using, and quals are interdependent. The user can write only
|
||||
* one of NATURAL, USING(), or ON() (this is enforced by the grammar).
|
||||
* If he writes NATURAL then parse analysis generates the equivalent USING()
|
||||
* list, and from that fills in "quals" with the right equality comparisons.
|
||||
* If he writes USING() then "quals" is filled with equality comparisons.
|
||||
* If he writes ON() then only "quals" is set. Note that NATURAL/USING
|
||||
* are not equivalent to ON() since they also affect the output column list.
|
||||
*
|
||||
* alias is an Attr node representing the AS alias-clause attached to the
|
||||
* join expression, or NULL if no clause. During parse analysis, colnames
|
||||
* is filled with a list of String nodes giving the column names (real or
|
||||
* alias) of the output of the join, and colvars is filled with a list of
|
||||
* expressions that can be copied to reference the output columns.
|
||||
*----------
|
||||
*/
|
||||
typedef struct JoinExpr
|
||||
{
|
||||
NodeTag type;
|
||||
JoinType jointype; /* type of join */
|
||||
bool isNatural; /* Natural join? Will need to shape table */
|
||||
Node *larg; /* left subtree */
|
||||
Node *rarg; /* right subtree */
|
||||
List *using; /* USING clause, if any (list of String) */
|
||||
Node *quals; /* qualifiers on join, if any */
|
||||
struct Attr *alias; /* user-written alias clause, if any */
|
||||
List *colnames; /* output column names (list of String) */
|
||||
List *colvars; /* output column nodes (list of expressions) */
|
||||
} JoinExpr;
|
||||
|
||||
#endif /* PRIMNODES_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: relation.h,v 1.47 2000/04/12 17:16:40 momjian Exp $
|
||||
* $Id: relation.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -73,6 +73,9 @@ typedef enum CostSelector
|
|||
* participates (only used for base rels)
|
||||
* baserestrictcost - Estimated cost of evaluating the baserestrictinfo
|
||||
* clauses at a single tuple (only used for base rels)
|
||||
* outerjoinset - If the rel appears within the nullable side of an outer
|
||||
* join, the list of all relids participating in the highest
|
||||
* such outer join; else NIL (only used for base rels)
|
||||
* joininfo - List of JoinInfo nodes, containing info about each join
|
||||
* clause in which this relation participates
|
||||
* innerjoin - List of Path nodes that represent indices that may be used
|
||||
|
@ -94,6 +97,10 @@ typedef enum CostSelector
|
|||
* We store baserestrictcost in the RelOptInfo (for base relations) because
|
||||
* we know we will need it at least once (to price the sequential scan)
|
||||
* and may need it multiple times to price index scans.
|
||||
*
|
||||
* outerjoinset is used to ensure correct placement of WHERE clauses that
|
||||
* apply to outer-joined relations; we must not apply such WHERE clauses
|
||||
* until after the outer join is performed.
|
||||
*/
|
||||
|
||||
typedef struct RelOptInfo
|
||||
|
@ -124,6 +131,7 @@ typedef struct RelOptInfo
|
|||
List *baserestrictinfo; /* RestrictInfo structures (if
|
||||
* base rel) */
|
||||
Cost baserestrictcost; /* cost of evaluating the above */
|
||||
Relids outerjoinset; /* integer list of base relids */
|
||||
List *joininfo; /* JoinInfo structures */
|
||||
List *innerjoin; /* potential indexscans for nestloop joins */
|
||||
|
||||
|
@ -263,6 +271,9 @@ typedef struct Path
|
|||
* that refer to values of other rels, so those other rels must be
|
||||
* included in the outer joinrel in order to make a usable join.
|
||||
*
|
||||
* 'alljoinquals' is also used only for inner paths of nestloop joins.
|
||||
* This flag is TRUE iff all the indexquals came from JOIN/ON conditions.
|
||||
*
|
||||
* 'rows' is the estimated result tuple count for the indexscan. This
|
||||
* is the same as path.parent->rows for a simple indexscan, but it is
|
||||
* different for a nestloop inner path, because the additional indexquals
|
||||
|
@ -277,6 +288,7 @@ typedef struct IndexPath
|
|||
List *indexqual;
|
||||
ScanDirection indexscandir;
|
||||
Relids joinrelids; /* other rels mentioned in indexqual */
|
||||
bool alljoinquals; /* all indexquals derived from JOIN conds? */
|
||||
double rows; /* estimated number of result tuples */
|
||||
} IndexPath;
|
||||
|
||||
|
@ -295,8 +307,11 @@ typedef struct JoinPath
|
|||
{
|
||||
Path path;
|
||||
|
||||
JoinType jointype;
|
||||
|
||||
Path *outerjoinpath; /* path for the outer side of the join */
|
||||
Path *innerjoinpath; /* path for the inner side of the join */
|
||||
|
||||
List *joinrestrictinfo; /* RestrictInfos to apply to join */
|
||||
|
||||
/*
|
||||
|
@ -375,11 +390,12 @@ typedef struct HashPath
|
|||
* The clause cannot actually be applied until we have built a join rel
|
||||
* containing all the base rels it references, however.
|
||||
*
|
||||
* When we construct a join rel that describes exactly the set of base rels
|
||||
* referenced in a multi-relation restriction clause, we place that clause
|
||||
* into the joinrestrictinfo lists of paths for the join rel. It will be
|
||||
* applied at that join level, and will not propagate any further up the
|
||||
* join tree. (Note: the "predicate migration" code was once intended to
|
||||
* When we construct a join rel that includes all the base rels referenced
|
||||
* in a multi-relation restriction clause, we place that clause into the
|
||||
* joinrestrictinfo lists of paths for the join rel, if neither left nor
|
||||
* right sub-path includes all base rels referenced in the clause. The clause
|
||||
* will be applied at that join level, and will not propagate any further up
|
||||
* the join tree. (Note: the "predicate migration" code was once intended to
|
||||
* push restriction clauses up and down the plan tree based on evaluation
|
||||
* costs, but it's dead code and is unlikely to be resurrected in the
|
||||
* foreseeable future.)
|
||||
|
@ -394,18 +410,30 @@ typedef struct HashPath
|
|||
* or hashjoin clauses are fairly limited --- the code for each kind of
|
||||
* path is responsible for identifying the restrict clauses it can use
|
||||
* and ignoring the rest. Clauses not implemented by an indexscan,
|
||||
* mergejoin, or hashjoin will be placed in the qpqual field of the
|
||||
* final Plan node, where they will be enforced by general-purpose
|
||||
* mergejoin, or hashjoin will be placed in the plan qual or joinqual field
|
||||
* of the final Plan node, where they will be enforced by general-purpose
|
||||
* qual-expression-evaluation code. (But we are still entitled to count
|
||||
* their selectivity when estimating the result tuple count, if we
|
||||
* can guess what it is...)
|
||||
*
|
||||
* When dealing with outer joins we must distinguish between qual clauses
|
||||
* that came from WHERE and those that came from JOIN/ON or JOIN/USING.
|
||||
* (For inner joins there's no semantic difference and we can treat the
|
||||
* clauses interchangeably.) Both kinds of quals are stored as RestrictInfo
|
||||
* nodes during planning, but there's a flag to indicate where they came from.
|
||||
* Note also that when outer joins are present, a qual clause may be treated
|
||||
* as referencing more rels than it really does. This trick ensures that the
|
||||
* qual will be evaluated at the right level of the join tree --- we don't
|
||||
* want quals from WHERE to be evaluated until after the outer join is done.
|
||||
*/
|
||||
|
||||
typedef struct RestrictInfo
|
||||
{
|
||||
NodeTag type;
|
||||
|
||||
Expr *clause; /* the represented clause of WHERE cond */
|
||||
Expr *clause; /* the represented clause of WHERE or JOIN */
|
||||
|
||||
bool isjoinqual; /* TRUE if clause came from JOIN/ON */
|
||||
|
||||
/* only used if clause is an OR clause: */
|
||||
List *subclauseindices; /* indexes matching subclauses */
|
||||
|
@ -437,7 +465,7 @@ typedef struct RestrictInfo
|
|||
typedef struct JoinInfo
|
||||
{
|
||||
NodeTag type;
|
||||
Relids unjoined_relids;/* some rels not yet part of my RelOptInfo */
|
||||
Relids unjoined_relids; /* some rels not yet part of my RelOptInfo */
|
||||
List *jinfo_restrictinfo; /* relevant RestrictInfos */
|
||||
} JoinInfo;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: clauses.h,v 1.38 2000/08/13 02:50:26 tgl Exp $
|
||||
* $Id: clauses.h,v 1.39 2000/09/12 21:07:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -73,9 +73,11 @@ extern void CommuteClause(Expr *clause);
|
|||
extern Node *eval_const_expressions(Node *node);
|
||||
|
||||
extern bool expression_tree_walker(Node *node, bool (*walker) (),
|
||||
void *context);
|
||||
void *context);
|
||||
extern Node *expression_tree_mutator(Node *node, Node *(*mutator) (),
|
||||
void *context);
|
||||
void *context);
|
||||
extern bool query_tree_walker(Query *query, bool (*walker) (),
|
||||
void *context);
|
||||
|
||||
#define is_subplan(clause) ((clause) != NULL && \
|
||||
IsA(clause, Expr) && \
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pathnode.h,v 1.27 2000/04/12 17:16:42 momjian Exp $
|
||||
* $Id: pathnode.h,v 1.28 2000/09/12 21:07:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -34,26 +34,29 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
|
|||
extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
|
||||
|
||||
extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
List *pathkeys);
|
||||
JoinType jointype,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
List *pathkeys);
|
||||
|
||||
extern MergePath *create_mergejoin_path(RelOptInfo *joinrel,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
List *pathkeys,
|
||||
List *mergeclauses,
|
||||
List *outersortkeys,
|
||||
List *innersortkeys);
|
||||
JoinType jointype,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
List *pathkeys,
|
||||
List *mergeclauses,
|
||||
List *outersortkeys,
|
||||
List *innersortkeys);
|
||||
|
||||
extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
List *hashclauses,
|
||||
Selectivity innerdisbursion);
|
||||
JoinType jointype,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
List *hashclauses,
|
||||
Selectivity innerdisbursion);
|
||||
|
||||
/*
|
||||
* prototypes for relnode.c
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: paths.h,v 1.46 2000/07/24 03:10:54 tgl Exp $
|
||||
* $Id: paths.h,v 1.47 2000/09/12 21:07:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -61,21 +61,23 @@ extern void create_tidscan_paths(Query *root, RelOptInfo *rel);
|
|||
* routines to create join paths
|
||||
*/
|
||||
extern void add_paths_to_joinrel(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
List *restrictlist);
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
JoinType jointype,
|
||||
List *restrictlist);
|
||||
|
||||
/*
|
||||
* joinrels.c
|
||||
* routines to determine which relations to join
|
||||
*/
|
||||
extern void make_rels_by_joins(Query *root, int level);
|
||||
extern RelOptInfo *make_rels_by_clause_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels);
|
||||
extern RelOptInfo *make_rels_by_clauseless_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels);
|
||||
extern List *make_rels_by_joins(Query *root, int level, List **joinrels);
|
||||
extern List *make_rels_by_clause_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels);
|
||||
extern List *make_rels_by_clauseless_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels);
|
||||
extern RelOptInfo *make_rel_from_jointree(Query *root, Node *jtnode);
|
||||
|
||||
/*
|
||||
* pathkeys.c
|
||||
|
@ -110,7 +112,7 @@ extern List *make_pathkeys_for_sortclauses(List *sortclauses,
|
|||
extern List *find_mergeclauses_for_pathkeys(List *pathkeys,
|
||||
List *restrictinfos);
|
||||
extern List *make_pathkeys_for_mergeclauses(Query *root,
|
||||
List *mergeclauses,
|
||||
List *tlist);
|
||||
List *mergeclauses,
|
||||
RelOptInfo *rel);
|
||||
|
||||
#endif /* PATHS_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: planmain.h,v 1.43 2000/07/24 03:10:54 tgl Exp $
|
||||
* $Id: planmain.h,v 1.44 2000/09/12 21:07:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -20,8 +20,7 @@
|
|||
/*
|
||||
* prototypes for plan/planmain.c
|
||||
*/
|
||||
extern Plan *query_planner(Query *root, List *tlist, List *qual,
|
||||
double tuple_fraction);
|
||||
extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction);
|
||||
|
||||
/*
|
||||
* prototypes for plan/createplan.c
|
||||
|
@ -40,9 +39,10 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
|||
/*
|
||||
* prototypes for plan/initsplan.c
|
||||
*/
|
||||
extern void make_var_only_tlist(Query *root, List *tlist);
|
||||
extern void build_base_rel_tlists(Query *root, List *tlist);
|
||||
extern Relids add_join_quals_to_rels(Query *root, Node *jtnode);
|
||||
extern void add_restrict_and_join_to_rels(Query *root, List *clauses);
|
||||
extern void add_missing_rels_to_query(Query *root);
|
||||
extern List *add_missing_rels_to_query(Query *root, Node *jtnode);
|
||||
extern void process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
Oid sortop1, Oid sortop2);
|
||||
|
||||
|
@ -58,6 +58,7 @@ extern void fix_opids(Node *node);
|
|||
* prep/prepkeyset.c
|
||||
*/
|
||||
extern bool _use_keyset_query_optimizer;
|
||||
|
||||
extern void transformKeySetQuery(Query *origNode);
|
||||
|
||||
#endif /* PLANMAIN_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: restrictinfo.h,v 1.8 2000/01/26 05:58:21 momjian Exp $
|
||||
* $Id: restrictinfo.h,v 1.9 2000/09/12 21:07:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -18,5 +18,7 @@
|
|||
|
||||
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
|
||||
extern List *get_actual_clauses(List *restrictinfo_list);
|
||||
extern void get_actual_join_clauses(List *restrictinfo_list,
|
||||
List **joinquals, List **otherquals);
|
||||
|
||||
#endif /* RESTRICTINFO_H */
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* gramparse.h
|
||||
* scanner support routines. used by both the bootstrap lexer
|
||||
* as well as the normal lexer
|
||||
* Declarations for routines exported from lexer and parser files.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: gramparse.h,v 1.12 2000/04/12 17:16:44 momjian Exp $
|
||||
* $Id: gramparse.h,v 1.13 2000/09/12 21:07:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef GRAMPARSE_H
|
||||
#define GRAMPARSE_H /* include once only */
|
||||
#define GRAMPARSE_H
|
||||
|
||||
/* from parser.c */
|
||||
extern int yylex(void);
|
||||
|
||||
/* from scan.l */
|
||||
extern void init_io(void);
|
||||
extern int yylex(void);
|
||||
extern void scanner_init(void);
|
||||
extern int base_yylex(void);
|
||||
extern void yyerror(const char *message);
|
||||
|
||||
/* from gram.y */
|
||||
extern Oid param_type(int t);
|
||||
extern void parser_init(Oid *typev, int nargs);
|
||||
extern Oid param_type(int t);
|
||||
extern int yyparse(void);
|
||||
|
||||
#endif /* GRAMPARSE_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_clause.h,v 1.18 2000/06/09 01:44:29 momjian Exp $
|
||||
* $Id: parse_clause.h,v 1.19 2000/09/12 21:07:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -17,7 +17,8 @@
|
|||
#include "parser/parse_node.h"
|
||||
|
||||
extern void makeRangeTable(ParseState *pstate, List *frmList);
|
||||
extern void setTargetTable(ParseState *pstate, char *relname, bool inh);
|
||||
extern void setTargetTable(ParseState *pstate, char *relname,
|
||||
bool inh, bool inJoinSet);
|
||||
extern Node *transformWhereClause(ParseState *pstate, Node *where);
|
||||
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
|
||||
List *targetlist);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_func.h,v 1.26 2000/08/20 00:44:17 tgl Exp $
|
||||
* $Id: parse_func.h,v 1.27 2000/09/12 21:07:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -39,11 +39,11 @@ typedef struct _CandidateList
|
|||
} *CandidateList;
|
||||
|
||||
extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
|
||||
int *curr_resno, int precedence);
|
||||
int precedence);
|
||||
extern Node *ParseFuncOrColumn(ParseState *pstate,
|
||||
char *funcname, List *fargs,
|
||||
bool agg_star, bool agg_distinct,
|
||||
int *curr_resno, int precedence);
|
||||
char *funcname, List *fargs,
|
||||
bool agg_star, bool agg_distinct,
|
||||
int precedence);
|
||||
|
||||
extern bool func_get_detail(char *funcname, int nargs, Oid *argtypes,
|
||||
Oid *funcid, Oid *rettype,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_node.h,v 1.20 2000/05/12 01:33:52 tgl Exp $
|
||||
* $Id: parse_node.h,v 1.21 2000/09/12 21:07:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -16,36 +16,28 @@
|
|||
#include "nodes/parsenodes.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
/* State information used during parse analysis
|
||||
* p_join_quals is a list of untransformed qualification expressions
|
||||
* (implicitly ANDed together) found in the FROM clause.
|
||||
* Needs to be available later to merge with other qualifiers from the
|
||||
* WHERE clause.
|
||||
/*
|
||||
* State information used during parse analysis
|
||||
*/
|
||||
typedef struct ParseState
|
||||
{
|
||||
int p_last_resno;
|
||||
List *p_rtable;
|
||||
struct ParseState *parentParseState;
|
||||
struct ParseState *parentParseState; /* stack link */
|
||||
List *p_rtable; /* range table so far */
|
||||
List *p_jointree; /* join tree so far */
|
||||
int p_last_resno; /* last targetlist resno assigned */
|
||||
bool p_hasAggs;
|
||||
bool p_hasSubLinks;
|
||||
bool p_is_insert;
|
||||
bool p_is_update;
|
||||
bool p_is_rule;
|
||||
bool p_in_where_clause;
|
||||
Relation p_target_relation;
|
||||
RangeTblEntry *p_target_rangetblentry;
|
||||
List *p_shape;
|
||||
List *p_alias;
|
||||
List *p_join_quals;
|
||||
} ParseState;
|
||||
|
||||
extern ParseState *make_parsestate(ParseState *parentParseState);
|
||||
extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
|
||||
extern Node *make_operand(char *opname, Node *tree,
|
||||
Oid orig_typeId, Oid target_typeId);
|
||||
extern Var *make_var(ParseState *pstate, Oid relid, char *refname,
|
||||
char *attrname);
|
||||
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
|
||||
extern ArrayRef *transformArraySubscripts(ParseState *pstate,
|
||||
Node *arrayBase,
|
||||
List *indirection,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_relation.h,v 1.18 2000/06/08 22:37:53 momjian Exp $
|
||||
* $Id: parse_relation.h,v 1.19 2000/09/12 21:07:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -16,23 +16,35 @@
|
|||
|
||||
#include "parser/parse_node.h"
|
||||
|
||||
extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate, char *refname);
|
||||
extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
char *refname,
|
||||
int *sublevels_up);
|
||||
extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate,
|
||||
char *refname);
|
||||
extern int refnameRangeTablePosn(ParseState *pstate,
|
||||
char *refname,
|
||||
int *sublevels_up);
|
||||
extern RangeTblEntry *colnameRangeTableEntry(ParseState *pstate, char *colname);
|
||||
char *refname,
|
||||
int *sublevels_up);
|
||||
extern int RTERangeTablePosn(ParseState *pstate,
|
||||
RangeTblEntry *rte,
|
||||
int *sublevels_up);
|
||||
extern JoinExpr *scanJoinTreeForRefname(Node *jtnode, char *refname);
|
||||
extern Node *colnameToVar(ParseState *pstate, char *colname);
|
||||
extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
|
||||
char *colname, bool implicitRTEOK);
|
||||
extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
|
||||
char *relname,
|
||||
Attr *ref,
|
||||
bool inh,
|
||||
bool inFromCl,
|
||||
bool inJoinSet);
|
||||
extern Attr *expandTable(ParseState *pstate, char *refname, bool getaliases);
|
||||
extern List *expandAll(ParseState *pstate, char *relname, Attr *ref,
|
||||
int *this_resno);
|
||||
char *relname,
|
||||
Attr *alias,
|
||||
bool inh,
|
||||
bool inFromCl);
|
||||
extern void addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte);
|
||||
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
|
||||
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
List **colnames, List **colvars);
|
||||
extern List *expandRelAttrs(ParseState *pstate, RangeTblEntry *rte);
|
||||
extern List *expandJoinAttrs(ParseState *pstate, JoinExpr *join,
|
||||
int sublevels_up);
|
||||
extern int attnameAttNum(Relation rd, char *a);
|
||||
extern int specialAttNum(char *a);
|
||||
extern Oid attnumTypeId(Relation rd, int attid);
|
||||
extern void warnAutoRange(ParseState *pstate, char *refname);
|
||||
|
||||
#endif /* PARSE_RELATION_H */
|
||||
|
|
|
@ -8,41 +8,26 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parsetree.h,v 1.10 2000/06/12 19:40:51 momjian Exp $
|
||||
* $Id: parsetree.h,v 1.11 2000/09/12 21:07:12 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PARSETREE_H
|
||||
#define PARSETREE_H /* include once only */
|
||||
#define PARSETREE_H
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/pg_list.h"
|
||||
|
||||
/* ----------------
|
||||
* need pg_list.h for definitions of CAR(), etc. macros
|
||||
* need pg_list.h for definitions of nth(), etc.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* range table macros
|
||||
*
|
||||
* parse tree:
|
||||
* (root targetlist qual)
|
||||
* ^^^^
|
||||
* parse root:
|
||||
* (numlevels cmdtype resrel rangetable priority ruleinfo nestdotinfo)
|
||||
* ^^^^^^^^^^
|
||||
* range table:
|
||||
* (rtentry ...)
|
||||
* rtentry:
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
#define rt_relname(rt_entry) \
|
||||
((!strcmp(((rt_entry)->ref->relname),"*OLD*") ||\
|
||||
!strcmp(((rt_entry)->ref->relname),"*NEW*")) ? ((rt_entry)->ref->relname) : \
|
||||
((char *)(rt_entry)->relname))
|
||||
|
||||
/*
|
||||
* rt_fetch
|
||||
* rt_store
|
||||
|
@ -51,22 +36,18 @@
|
|||
*
|
||||
*/
|
||||
#define rt_fetch(rangetable_index, rangetable) \
|
||||
((RangeTblEntry*)nth((rangetable_index)-1, rangetable))
|
||||
((RangeTblEntry*) nth((rangetable_index)-1, rangetable))
|
||||
|
||||
#define rt_store(rangetable_index, rangetable, rt) \
|
||||
set_nth(rangetable, (rangetable_index)-1, rt)
|
||||
|
||||
/*
|
||||
* getrelid
|
||||
* getrelname
|
||||
*
|
||||
* Given the range index of a relation, return the corresponding
|
||||
* relation id or relation name.
|
||||
* relation OID.
|
||||
*/
|
||||
#define getrelid(rangeindex,rangetable) \
|
||||
((RangeTblEntry*)nth((rangeindex)-1, rangetable))->relid
|
||||
|
||||
#define getrelname(rangeindex, rangetable) \
|
||||
rt_relname((RangeTblEntry*)nth((rangeindex)-1, rangetable))
|
||||
(rt_fetch(rangeindex, rangetable)->relid)
|
||||
|
||||
#endif /* PARSETREE_H */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: rewriteHandler.h,v 1.12 2000/01/26 05:58:30 momjian Exp $
|
||||
* $Id: rewriteHandler.h,v 1.13 2000/09/12 21:07:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -16,9 +16,8 @@
|
|||
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
struct _rewrite_meta_knowledge
|
||||
typedef struct RewriteInfo
|
||||
{
|
||||
List *rt;
|
||||
int rt_index;
|
||||
bool instead_flag;
|
||||
int event;
|
||||
|
@ -28,9 +27,7 @@ struct _rewrite_meta_knowledge
|
|||
Query *rule_action;
|
||||
Node *rule_qual;
|
||||
bool nothing;
|
||||
};
|
||||
|
||||
typedef struct _rewrite_meta_knowledge RewriteInfo;
|
||||
} RewriteInfo;
|
||||
|
||||
|
||||
extern List *QueryRewrite(Query *parsetree);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: rewriteManip.h,v 1.21 2000/04/12 17:16:50 momjian Exp $
|
||||
* $Id: rewriteManip.h,v 1.22 2000/09/12 21:07:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
@ -22,6 +22,12 @@ extern void ChangeVarNodes(Node *node, int old_varno, int new_varno,
|
|||
int sublevels_up);
|
||||
extern void IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
|
||||
int min_sublevels_up);
|
||||
|
||||
extern bool rangeTableEntry_used(Node *node, int rt_index,
|
||||
int sublevels_up);
|
||||
extern bool attribute_used(Node *node, int rt_index, int attno,
|
||||
int sublevels_up);
|
||||
|
||||
extern void AddQual(Query *parsetree, Node *qual);
|
||||
extern void AddHavingQual(Query *parsetree, Node *havingQual);
|
||||
extern void AddNotQual(Query *parsetree, Node *qual);
|
||||
|
|
|
@ -156,29 +156,29 @@ SELECT COALESCE(a.f, b.i, b.j)
|
|||
FROM CASE_TBL a, CASE2_TBL b;
|
||||
case
|
||||
-------
|
||||
10.1
|
||||
10.1
|
||||
10.1
|
||||
10.1
|
||||
10.1
|
||||
10.1
|
||||
20.2
|
||||
20.2
|
||||
20.2
|
||||
20.2
|
||||
20.2
|
||||
20.2
|
||||
-30.3
|
||||
-30.3
|
||||
-30.3
|
||||
-30.3
|
||||
-30.3
|
||||
-30.3
|
||||
1
|
||||
10.1
|
||||
20.2
|
||||
-30.3
|
||||
2
|
||||
10.1
|
||||
20.2
|
||||
-30.3
|
||||
3
|
||||
10.1
|
||||
20.2
|
||||
-30.3
|
||||
2
|
||||
10.1
|
||||
20.2
|
||||
-30.3
|
||||
1
|
||||
10.1
|
||||
20.2
|
||||
-30.3
|
||||
-6
|
||||
(24 rows)
|
||||
|
||||
|
@ -197,28 +197,28 @@ SELECT '' AS Five, NULLIF(a.i,b.i) AS "NULLIF(a.i,b.i)",
|
|||
five | NULLIF(a.i,b.i) | NULLIF(b.i,4)
|
||||
------+-----------------+---------------
|
||||
| | 1
|
||||
| 2 | 1
|
||||
| 3 | 1
|
||||
| 4 | 1
|
||||
| 1 | 2
|
||||
| | 2
|
||||
| 3 | 2
|
||||
| 4 | 2
|
||||
| 1 | 3
|
||||
| 2 | 3
|
||||
| | 3
|
||||
| 4 | 3
|
||||
| 1 | 2
|
||||
| | 2
|
||||
| 3 | 2
|
||||
| 4 | 2
|
||||
| | 1
|
||||
| 2 | 1
|
||||
| 3 | 1
|
||||
| 4 | 1
|
||||
| 1 |
|
||||
| 2 | 1
|
||||
| | 2
|
||||
| 2 | 3
|
||||
| | 2
|
||||
| 2 | 1
|
||||
| 2 |
|
||||
| 3 | 1
|
||||
| 3 | 2
|
||||
| | 3
|
||||
| 3 | 2
|
||||
| 3 | 1
|
||||
| 3 |
|
||||
| 4 | 1
|
||||
| 4 | 2
|
||||
| 4 | 3
|
||||
| 4 | 2
|
||||
| 4 | 1
|
||||
| 4 |
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,-0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,-0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,-0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,-0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,-0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -163,28 +163,28 @@ SELECT '' AS twentyfour, b.f1 + p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+-------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (-8,2),(-10,0)
|
||||
| (-7,3),(-9,1)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-7,3),(-7,3)
|
||||
| (-1,6),(-3,4)
|
||||
| (0,7),(-2,5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (0,7),(0,7)
|
||||
| (7.1,36.5),(5.1,34.5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-3,-10),(-5,-12)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (12,12),(10,10)
|
||||
| (3,3),(1,1)
|
||||
| (-7,3),(-9,1)
|
||||
| (0,7),(-2,5)
|
||||
| (8.1,37.5),(6.1,35.5)
|
||||
| (-2,-9),(-4,-11)
|
||||
| (13,13),(11,11)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (-7.5,3.5),(-7.5,2.5)
|
||||
| (-0.5,7.5),(-0.5,6.5)
|
||||
| (7.6,38),(7.6,37)
|
||||
| (-2.5,-8.5),(-2.5,-9.5)
|
||||
| (12.5,13.5),(12.5,12.5)
|
||||
| (3,3),(3,3)
|
||||
| (-7,3),(-7,3)
|
||||
| (0,7),(0,7)
|
||||
| (8.1,37.5),(8.1,37.5)
|
||||
| (-2,-9),(-2,-9)
|
||||
| (13,13),(13,13)
|
||||
(24 rows)
|
||||
|
||||
|
@ -193,28 +193,28 @@ SELECT '' AS twentyfour, b.f1 - p.f1 AS translation
|
|||
twentyfour | translation
|
||||
------------+---------------------------
|
||||
| (2,2),(0,0)
|
||||
| (3,3),(1,1)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (3,3),(3,3)
|
||||
| (12,2),(10,0)
|
||||
| (13,3),(11,1)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (13,3),(13,3)
|
||||
| (5,-2),(3,-4)
|
||||
| (6,-1),(4,-3)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (6,-1),(6,-1)
|
||||
| (-3.1,-32.5),(-5.1,-34.5)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (7,14),(5,12)
|
||||
| (8,15),(6,13)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (8,15),(8,15)
|
||||
| (-8,-8),(-10,-10)
|
||||
| (3,3),(1,1)
|
||||
| (13,3),(11,1)
|
||||
| (6,-1),(4,-3)
|
||||
| (-2.1,-31.5),(-4.1,-33.5)
|
||||
| (8,15),(6,13)
|
||||
| (-7,-7),(-9,-9)
|
||||
| (2.5,3.5),(2.5,2.5)
|
||||
| (12.5,3.5),(12.5,2.5)
|
||||
| (5.5,-0.5),(5.5,-1.5)
|
||||
| (-2.6,-31),(-2.6,-32)
|
||||
| (7.5,15.5),(7.5,14.5)
|
||||
| (-7.5,-6.5),(-7.5,-7.5)
|
||||
| (3,3),(3,3)
|
||||
| (13,3),(13,3)
|
||||
| (6,-1),(6,-1)
|
||||
| (-2.1,-31.5),(-2.1,-31.5)
|
||||
| (8,15),(8,15)
|
||||
| (-7,-7),(-7,-7)
|
||||
(24 rows)
|
||||
|
||||
|
@ -223,29 +223,29 @@ SELECT '' AS twentyfour, b.f1 * p.f1 AS rotation
|
|||
FROM BOX_TBL b, POINT_TBL p;
|
||||
twentyfour | rotation
|
||||
------------+-----------------------------
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-0,0),(-20,-20)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-0,2),(-14,0)
|
||||
| (-7,3),(-21,1)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-21,3),(-21,3)
|
||||
| (0,79.2),(-58.8,0)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (14,-0),(0,-34)
|
||||
| (21,-17),(7,-51)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,40),(0,0)
|
||||
| (0,0),(0,0)
|
||||
| (-10,-10),(-30,-30)
|
||||
| (-7,3),(-21,1)
|
||||
| (-29.4,118.8),(-88.2,39.6)
|
||||
| (21,-17),(7,-51)
|
||||
| (0,60),(0,20)
|
||||
| (0,0),(0,0)
|
||||
| (-25,-25),(-25,-35)
|
||||
| (-17.5,2.5),(-21.5,-0.5)
|
||||
| (-73.5,104.1),(-108,99)
|
||||
| (29.5,-42.5),(17.5,-47.5)
|
||||
| (0,60),(-10,50)
|
||||
| (0,0),(0,0)
|
||||
| (-30,-30),(-30,-30)
|
||||
| (-21,3),(-21,3)
|
||||
| (-88.2,118.8),(-88.2,118.8)
|
||||
| (21,-51),(21,-51)
|
||||
| (0,60),(0,60)
|
||||
(24 rows)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--
|
||||
-- JOIN
|
||||
-- Test join clauses
|
||||
-- Test JOIN clauses
|
||||
--
|
||||
CREATE TABLE J1_TBL (
|
||||
i integer,
|
||||
|
@ -28,6 +28,7 @@ INSERT INTO J2_TBL VALUES (1, -1);
|
|||
INSERT INTO J2_TBL VALUES (2, 2);
|
||||
INSERT INTO J2_TBL VALUES (3, -3);
|
||||
INSERT INTO J2_TBL VALUES (2, 4);
|
||||
INSERT INTO J2_TBL VALUES (5, -5);
|
||||
--
|
||||
-- CORRELATION NAMES
|
||||
-- Make sure that table/column aliases are supported
|
||||
|
@ -78,22 +79,26 @@ SELECT '' AS "xxx", *
|
|||
xxx | a | b | c | d | e
|
||||
-----+---+---+-------+---+----
|
||||
| 1 | 3 | one | 1 | -1
|
||||
| 2 | 2 | two | 1 | -1
|
||||
| 3 | 1 | three | 1 | -1
|
||||
| 4 | 0 | four | 1 | -1
|
||||
| 1 | 3 | one | 2 | 2
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 3 | 1 | three | 2 | 2
|
||||
| 4 | 0 | four | 2 | 2
|
||||
| 1 | 3 | one | 3 | -3
|
||||
| 2 | 2 | two | 3 | -3
|
||||
| 3 | 1 | three | 3 | -3
|
||||
| 4 | 0 | four | 3 | -3
|
||||
| 1 | 3 | one | 2 | 4
|
||||
| 1 | 3 | one | 5 | -5
|
||||
| 2 | 2 | two | 1 | -1
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 2 | 2 | two | 3 | -3
|
||||
| 2 | 2 | two | 2 | 4
|
||||
| 2 | 2 | two | 5 | -5
|
||||
| 3 | 1 | three | 1 | -1
|
||||
| 3 | 1 | three | 2 | 2
|
||||
| 3 | 1 | three | 3 | -3
|
||||
| 3 | 1 | three | 2 | 4
|
||||
| 3 | 1 | three | 5 | -5
|
||||
| 4 | 0 | four | 1 | -1
|
||||
| 4 | 0 | four | 2 | 2
|
||||
| 4 | 0 | four | 3 | -3
|
||||
| 4 | 0 | four | 2 | 4
|
||||
(16 rows)
|
||||
| 4 | 0 | four | 5 | -5
|
||||
(20 rows)
|
||||
|
||||
SELECT '' AS "xxx", t1.a, t2.e
|
||||
FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e)
|
||||
|
@ -116,58 +121,218 @@ SELECT '' AS "xxx", *
|
|||
xxx | i | j | t | i | k
|
||||
-----+---+---+-------+---+----
|
||||
| 1 | 3 | one | 1 | -1
|
||||
| 2 | 2 | two | 1 | -1
|
||||
| 3 | 1 | three | 1 | -1
|
||||
| 4 | 0 | four | 1 | -1
|
||||
| 1 | 3 | one | 2 | 2
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 3 | 1 | three | 2 | 2
|
||||
| 4 | 0 | four | 2 | 2
|
||||
| 1 | 3 | one | 3 | -3
|
||||
| 2 | 2 | two | 3 | -3
|
||||
| 3 | 1 | three | 3 | -3
|
||||
| 4 | 0 | four | 3 | -3
|
||||
| 1 | 3 | one | 2 | 4
|
||||
| 1 | 3 | one | 5 | -5
|
||||
| 2 | 2 | two | 1 | -1
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 2 | 2 | two | 3 | -3
|
||||
| 2 | 2 | two | 2 | 4
|
||||
| 2 | 2 | two | 5 | -5
|
||||
| 3 | 1 | three | 1 | -1
|
||||
| 3 | 1 | three | 2 | 2
|
||||
| 3 | 1 | three | 3 | -3
|
||||
| 3 | 1 | three | 2 | 4
|
||||
| 3 | 1 | three | 5 | -5
|
||||
| 4 | 0 | four | 1 | -1
|
||||
| 4 | 0 | four | 2 | 2
|
||||
| 4 | 0 | four | 3 | -3
|
||||
| 4 | 0 | four | 2 | 4
|
||||
(16 rows)
|
||||
| 4 | 0 | four | 5 | -5
|
||||
(20 rows)
|
||||
|
||||
-- ambiguous column
|
||||
SELECT '' AS "xxx", i, k, t
|
||||
FROM J1_TBL CROSS JOIN J2_TBL;
|
||||
ERROR: Column 'i' is ambiguous
|
||||
ERROR: Column reference "i" is ambiguous
|
||||
-- resolve previous ambiguity by specifying the table name
|
||||
SELECT '' AS "xxx", t1.i, k, t
|
||||
FROM J1_TBL t1 CROSS JOIN J2_TBL t2;
|
||||
xxx | i | k | t
|
||||
-----+---+----+-------
|
||||
| 1 | -1 | one
|
||||
| 2 | -1 | two
|
||||
| 3 | -1 | three
|
||||
| 4 | -1 | four
|
||||
| 1 | 2 | one
|
||||
| 2 | 2 | two
|
||||
| 3 | 2 | three
|
||||
| 4 | 2 | four
|
||||
| 1 | -3 | one
|
||||
| 2 | -3 | two
|
||||
| 3 | -3 | three
|
||||
| 4 | -3 | four
|
||||
| 1 | 4 | one
|
||||
| 1 | -5 | one
|
||||
| 2 | -1 | two
|
||||
| 2 | 2 | two
|
||||
| 2 | -3 | two
|
||||
| 2 | 4 | two
|
||||
| 2 | -5 | two
|
||||
| 3 | -1 | three
|
||||
| 3 | 2 | three
|
||||
| 3 | -3 | three
|
||||
| 3 | 4 | three
|
||||
| 3 | -5 | three
|
||||
| 4 | -1 | four
|
||||
| 4 | 2 | four
|
||||
| 4 | -3 | four
|
||||
| 4 | 4 | four
|
||||
(16 rows)
|
||||
| 4 | -5 | four
|
||||
(20 rows)
|
||||
|
||||
SELECT '' AS "xxx", ii, tt, kk
|
||||
FROM (J1_TBL CROSS JOIN J2_TBL)
|
||||
AS tx (ii, jj, tt, ii2, kk);
|
||||
ERROR: JOIN table aliases are not supported
|
||||
xxx | ii | tt | kk
|
||||
-----+----+-------+----
|
||||
| 1 | one | -1
|
||||
| 1 | one | 2
|
||||
| 1 | one | -3
|
||||
| 1 | one | 4
|
||||
| 1 | one | -5
|
||||
| 2 | two | -1
|
||||
| 2 | two | 2
|
||||
| 2 | two | -3
|
||||
| 2 | two | 4
|
||||
| 2 | two | -5
|
||||
| 3 | three | -1
|
||||
| 3 | three | 2
|
||||
| 3 | three | -3
|
||||
| 3 | three | 4
|
||||
| 3 | three | -5
|
||||
| 4 | four | -1
|
||||
| 4 | four | 2
|
||||
| 4 | four | -3
|
||||
| 4 | four | 4
|
||||
| 4 | four | -5
|
||||
(20 rows)
|
||||
|
||||
SELECT '' AS "xxx", tx.ii, tx.jj, tx.kk
|
||||
FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e))
|
||||
AS tx (ii, jj, tt, ii2, kk);
|
||||
ERROR: JOIN table aliases are not supported
|
||||
xxx | ii | jj | kk
|
||||
-----+----+----+----
|
||||
| 1 | 3 | -1
|
||||
| 1 | 3 | 2
|
||||
| 1 | 3 | -3
|
||||
| 1 | 3 | 4
|
||||
| 1 | 3 | -5
|
||||
| 2 | 2 | -1
|
||||
| 2 | 2 | 2
|
||||
| 2 | 2 | -3
|
||||
| 2 | 2 | 4
|
||||
| 2 | 2 | -5
|
||||
| 3 | 1 | -1
|
||||
| 3 | 1 | 2
|
||||
| 3 | 1 | -3
|
||||
| 3 | 1 | 4
|
||||
| 3 | 1 | -5
|
||||
| 4 | 0 | -1
|
||||
| 4 | 0 | 2
|
||||
| 4 | 0 | -3
|
||||
| 4 | 0 | 4
|
||||
| 4 | 0 | -5
|
||||
(20 rows)
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b;
|
||||
xxx | i | j | t | i | k | i | k
|
||||
-----+---+---+-------+---+----+---+----
|
||||
| 1 | 3 | one | 1 | -1 | 1 | -1
|
||||
| 1 | 3 | one | 1 | -1 | 2 | 2
|
||||
| 1 | 3 | one | 1 | -1 | 3 | -3
|
||||
| 1 | 3 | one | 1 | -1 | 2 | 4
|
||||
| 1 | 3 | one | 1 | -1 | 5 | -5
|
||||
| 1 | 3 | one | 2 | 2 | 1 | -1
|
||||
| 1 | 3 | one | 2 | 2 | 2 | 2
|
||||
| 1 | 3 | one | 2 | 2 | 3 | -3
|
||||
| 1 | 3 | one | 2 | 2 | 2 | 4
|
||||
| 1 | 3 | one | 2 | 2 | 5 | -5
|
||||
| 1 | 3 | one | 3 | -3 | 1 | -1
|
||||
| 1 | 3 | one | 3 | -3 | 2 | 2
|
||||
| 1 | 3 | one | 3 | -3 | 3 | -3
|
||||
| 1 | 3 | one | 3 | -3 | 2 | 4
|
||||
| 1 | 3 | one | 3 | -3 | 5 | -5
|
||||
| 1 | 3 | one | 2 | 4 | 1 | -1
|
||||
| 1 | 3 | one | 2 | 4 | 2 | 2
|
||||
| 1 | 3 | one | 2 | 4 | 3 | -3
|
||||
| 1 | 3 | one | 2 | 4 | 2 | 4
|
||||
| 1 | 3 | one | 2 | 4 | 5 | -5
|
||||
| 1 | 3 | one | 5 | -5 | 1 | -1
|
||||
| 1 | 3 | one | 5 | -5 | 2 | 2
|
||||
| 1 | 3 | one | 5 | -5 | 3 | -3
|
||||
| 1 | 3 | one | 5 | -5 | 2 | 4
|
||||
| 1 | 3 | one | 5 | -5 | 5 | -5
|
||||
| 2 | 2 | two | 1 | -1 | 1 | -1
|
||||
| 2 | 2 | two | 1 | -1 | 2 | 2
|
||||
| 2 | 2 | two | 1 | -1 | 3 | -3
|
||||
| 2 | 2 | two | 1 | -1 | 2 | 4
|
||||
| 2 | 2 | two | 1 | -1 | 5 | -5
|
||||
| 2 | 2 | two | 2 | 2 | 1 | -1
|
||||
| 2 | 2 | two | 2 | 2 | 2 | 2
|
||||
| 2 | 2 | two | 2 | 2 | 3 | -3
|
||||
| 2 | 2 | two | 2 | 2 | 2 | 4
|
||||
| 2 | 2 | two | 2 | 2 | 5 | -5
|
||||
| 2 | 2 | two | 3 | -3 | 1 | -1
|
||||
| 2 | 2 | two | 3 | -3 | 2 | 2
|
||||
| 2 | 2 | two | 3 | -3 | 3 | -3
|
||||
| 2 | 2 | two | 3 | -3 | 2 | 4
|
||||
| 2 | 2 | two | 3 | -3 | 5 | -5
|
||||
| 2 | 2 | two | 2 | 4 | 1 | -1
|
||||
| 2 | 2 | two | 2 | 4 | 2 | 2
|
||||
| 2 | 2 | two | 2 | 4 | 3 | -3
|
||||
| 2 | 2 | two | 2 | 4 | 2 | 4
|
||||
| 2 | 2 | two | 2 | 4 | 5 | -5
|
||||
| 2 | 2 | two | 5 | -5 | 1 | -1
|
||||
| 2 | 2 | two | 5 | -5 | 2 | 2
|
||||
| 2 | 2 | two | 5 | -5 | 3 | -3
|
||||
| 2 | 2 | two | 5 | -5 | 2 | 4
|
||||
| 2 | 2 | two | 5 | -5 | 5 | -5
|
||||
| 3 | 1 | three | 1 | -1 | 1 | -1
|
||||
| 3 | 1 | three | 1 | -1 | 2 | 2
|
||||
| 3 | 1 | three | 1 | -1 | 3 | -3
|
||||
| 3 | 1 | three | 1 | -1 | 2 | 4
|
||||
| 3 | 1 | three | 1 | -1 | 5 | -5
|
||||
| 3 | 1 | three | 2 | 2 | 1 | -1
|
||||
| 3 | 1 | three | 2 | 2 | 2 | 2
|
||||
| 3 | 1 | three | 2 | 2 | 3 | -3
|
||||
| 3 | 1 | three | 2 | 2 | 2 | 4
|
||||
| 3 | 1 | three | 2 | 2 | 5 | -5
|
||||
| 3 | 1 | three | 3 | -3 | 1 | -1
|
||||
| 3 | 1 | three | 3 | -3 | 2 | 2
|
||||
| 3 | 1 | three | 3 | -3 | 3 | -3
|
||||
| 3 | 1 | three | 3 | -3 | 2 | 4
|
||||
| 3 | 1 | three | 3 | -3 | 5 | -5
|
||||
| 3 | 1 | three | 2 | 4 | 1 | -1
|
||||
| 3 | 1 | three | 2 | 4 | 2 | 2
|
||||
| 3 | 1 | three | 2 | 4 | 3 | -3
|
||||
| 3 | 1 | three | 2 | 4 | 2 | 4
|
||||
| 3 | 1 | three | 2 | 4 | 5 | -5
|
||||
| 3 | 1 | three | 5 | -5 | 1 | -1
|
||||
| 3 | 1 | three | 5 | -5 | 2 | 2
|
||||
| 3 | 1 | three | 5 | -5 | 3 | -3
|
||||
| 3 | 1 | three | 5 | -5 | 2 | 4
|
||||
| 3 | 1 | three | 5 | -5 | 5 | -5
|
||||
| 4 | 0 | four | 1 | -1 | 1 | -1
|
||||
| 4 | 0 | four | 1 | -1 | 2 | 2
|
||||
| 4 | 0 | four | 1 | -1 | 3 | -3
|
||||
| 4 | 0 | four | 1 | -1 | 2 | 4
|
||||
| 4 | 0 | four | 1 | -1 | 5 | -5
|
||||
| 4 | 0 | four | 2 | 2 | 1 | -1
|
||||
| 4 | 0 | four | 2 | 2 | 2 | 2
|
||||
| 4 | 0 | four | 2 | 2 | 3 | -3
|
||||
| 4 | 0 | four | 2 | 2 | 2 | 4
|
||||
| 4 | 0 | four | 2 | 2 | 5 | -5
|
||||
| 4 | 0 | four | 3 | -3 | 1 | -1
|
||||
| 4 | 0 | four | 3 | -3 | 2 | 2
|
||||
| 4 | 0 | four | 3 | -3 | 3 | -3
|
||||
| 4 | 0 | four | 3 | -3 | 2 | 4
|
||||
| 4 | 0 | four | 3 | -3 | 5 | -5
|
||||
| 4 | 0 | four | 2 | 4 | 1 | -1
|
||||
| 4 | 0 | four | 2 | 4 | 2 | 2
|
||||
| 4 | 0 | four | 2 | 4 | 3 | -3
|
||||
| 4 | 0 | four | 2 | 4 | 2 | 4
|
||||
| 4 | 0 | four | 2 | 4 | 5 | -5
|
||||
| 4 | 0 | four | 5 | -5 | 1 | -1
|
||||
| 4 | 0 | four | 5 | -5 | 2 | 2
|
||||
| 4 | 0 | four | 5 | -5 | 3 | -3
|
||||
| 4 | 0 | four | 5 | -5 | 2 | 4
|
||||
| 4 | 0 | four | 5 | -5 | 5 | -5
|
||||
(100 rows)
|
||||
|
||||
--
|
||||
--
|
||||
-- Inner joins (equi-joins)
|
||||
|
@ -249,14 +414,6 @@ SELECT '' AS "xxx", *
|
|||
| 4 | 0 | four | 2
|
||||
(2 rows)
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
|
||||
xxx | a | b | c | d
|
||||
-----+---+---+------+---
|
||||
| 2 | 2 | two | 2
|
||||
| 4 | 0 | four | 2
|
||||
(2 rows)
|
||||
|
||||
-- mismatch number of columns
|
||||
-- currently, Postgres will fill in with underlying names
|
||||
SELECT '' AS "xxx", *
|
||||
|
@ -290,28 +447,6 @@ SELECT '' AS "xxx", *
|
|||
| 4 | 0 | four | 2 | 4
|
||||
(2 rows)
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL CROSS JOIN J2_TBL;
|
||||
xxx | i | j | t | i | k
|
||||
-----+---+---+-------+---+----
|
||||
| 1 | 3 | one | 1 | -1
|
||||
| 2 | 2 | two | 1 | -1
|
||||
| 3 | 1 | three | 1 | -1
|
||||
| 4 | 0 | four | 1 | -1
|
||||
| 1 | 3 | one | 2 | 2
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 3 | 1 | three | 2 | 2
|
||||
| 4 | 0 | four | 2 | 2
|
||||
| 1 | 3 | one | 3 | -3
|
||||
| 2 | 2 | two | 3 | -3
|
||||
| 3 | 1 | three | 3 | -3
|
||||
| 4 | 0 | four | 3 | -3
|
||||
| 1 | 3 | one | 2 | 4
|
||||
| 2 | 2 | two | 2 | 4
|
||||
| 3 | 1 | three | 2 | 4
|
||||
| 4 | 0 | four | 2 | 4
|
||||
(16 rows)
|
||||
|
||||
--
|
||||
-- Non-equi-joins
|
||||
--
|
||||
|
@ -320,8 +455,8 @@ SELECT '' AS "xxx", *
|
|||
xxx | i | j | t | i | k
|
||||
-----+---+---+-------+---+---
|
||||
| 1 | 3 | one | 2 | 2
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 1 | 3 | one | 2 | 4
|
||||
| 2 | 2 | two | 2 | 2
|
||||
| 2 | 2 | two | 2 | 4
|
||||
| 3 | 1 | three | 2 | 4
|
||||
| 4 | 0 | four | 2 | 4
|
||||
|
@ -330,21 +465,48 @@ SELECT '' AS "xxx", *
|
|||
--
|
||||
-- Outer joins
|
||||
--
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL OUTER JOIN J2_TBL USING (i);
|
||||
ERROR: OUTER JOIN is not yet supported
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i);
|
||||
ERROR: OUTER JOIN is not yet supported
|
||||
xxx | i | j | t | k
|
||||
-----+---+---+-------+----
|
||||
| 1 | 3 | one | -1
|
||||
| 2 | 2 | two | 2
|
||||
| 2 | 2 | two | 4
|
||||
| 3 | 1 | three | -3
|
||||
| 4 | 0 | four |
|
||||
(5 rows)
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i);
|
||||
ERROR: OUTER JOIN is not yet supported
|
||||
xxx | i | j | t | k
|
||||
-----+---+---+-------+----
|
||||
| 1 | 3 | one | -1
|
||||
| 2 | 2 | two | 2
|
||||
| 2 | 2 | two | 4
|
||||
| 3 | 1 | three | -3
|
||||
| 5 | | | -5
|
||||
(5 rows)
|
||||
|
||||
-- Note that OUTER is a noise word
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i);
|
||||
ERROR: OUTER JOIN is not yet supported
|
||||
FROM J1_TBL FULL JOIN J2_TBL USING (i);
|
||||
xxx | i | j | t | k
|
||||
-----+---+---+-------+----
|
||||
| 1 | 3 | one | -1
|
||||
| 2 | 2 | two | 2
|
||||
| 2 | 2 | two | 4
|
||||
| 3 | 1 | three | -3
|
||||
| 4 | 0 | four |
|
||||
| 5 | | | -5
|
||||
(6 rows)
|
||||
|
||||
--
|
||||
-- More complicated constructs
|
||||
--
|
||||
-- UNION JOIN isn't implemented yet
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL UNION JOIN J2_TBL;
|
||||
ERROR: UNION JOIN is not implemented yet
|
||||
--
|
||||
-- Clean up
|
||||
--
|
||||
|
|
|
@ -154,36 +154,36 @@ SELECT '' AS thirty, p1.f1 AS point1, p2.f1 AS point2
|
|||
WHERE (p1.f1 <-> p2.f1) > 3;
|
||||
thirty | point1 | point2
|
||||
--------+------------+------------
|
||||
| (-10,0) | (0,0)
|
||||
| (-3,4) | (0,0)
|
||||
| (5.1,34.5) | (0,0)
|
||||
| (-5,-12) | (0,0)
|
||||
| (10,10) | (0,0)
|
||||
| (0,0) | (-10,0)
|
||||
| (-3,4) | (-10,0)
|
||||
| (5.1,34.5) | (-10,0)
|
||||
| (-5,-12) | (-10,0)
|
||||
| (10,10) | (-10,0)
|
||||
| (0,0) | (-3,4)
|
||||
| (-10,0) | (-3,4)
|
||||
| (5.1,34.5) | (-3,4)
|
||||
| (-5,-12) | (-3,4)
|
||||
| (10,10) | (-3,4)
|
||||
| (0,0) | (5.1,34.5)
|
||||
| (-10,0) | (5.1,34.5)
|
||||
| (-3,4) | (5.1,34.5)
|
||||
| (-5,-12) | (5.1,34.5)
|
||||
| (10,10) | (5.1,34.5)
|
||||
| (0,0) | (-5,-12)
|
||||
| (-10,0) | (-5,-12)
|
||||
| (-3,4) | (-5,-12)
|
||||
| (5.1,34.5) | (-5,-12)
|
||||
| (10,10) | (-5,-12)
|
||||
| (0,0) | (10,10)
|
||||
| (-10,0) | (0,0)
|
||||
| (-10,0) | (-3,4)
|
||||
| (-10,0) | (5.1,34.5)
|
||||
| (-10,0) | (-5,-12)
|
||||
| (-10,0) | (10,10)
|
||||
| (-3,4) | (0,0)
|
||||
| (-3,4) | (-10,0)
|
||||
| (-3,4) | (5.1,34.5)
|
||||
| (-3,4) | (-5,-12)
|
||||
| (-3,4) | (10,10)
|
||||
| (5.1,34.5) | (0,0)
|
||||
| (5.1,34.5) | (-10,0)
|
||||
| (5.1,34.5) | (-3,4)
|
||||
| (5.1,34.5) | (-5,-12)
|
||||
| (5.1,34.5) | (10,10)
|
||||
| (-5,-12) | (0,0)
|
||||
| (-5,-12) | (-10,0)
|
||||
| (-5,-12) | (-3,4)
|
||||
| (-5,-12) | (5.1,34.5)
|
||||
| (-5,-12) | (10,10)
|
||||
| (10,10) | (0,0)
|
||||
| (10,10) | (-10,0)
|
||||
| (10,10) | (-3,4)
|
||||
| (10,10) | (5.1,34.5)
|
||||
| (10,10) | (-5,-12)
|
||||
(30 rows)
|
||||
|
||||
-- put distance result into output to allow sorting with GEQ optimizer - tgl 97/05/10
|
||||
|
|
|
@ -1158,6 +1158,47 @@ SELECT count(*) FROM shoe;
|
|||
4
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Simple test of qualified ON INSERT ... this did not work in 7.0 ...
|
||||
--
|
||||
create table foo (f1 int);
|
||||
create table foo2 (f1 int);
|
||||
create rule foorule as on insert to foo where f1 < 100
|
||||
do instead nothing;
|
||||
insert into foo values(1);
|
||||
insert into foo values(1001);
|
||||
select * from foo;
|
||||
f1
|
||||
------
|
||||
1001
|
||||
(1 row)
|
||||
|
||||
drop rule foorule;
|
||||
-- this should fail because f1 is not exposed for unqualified reference:
|
||||
create rule foorule as on insert to foo where f1 < 100
|
||||
do instead insert into foo2 values (f1);
|
||||
ERROR: Attribute 'f1' not found
|
||||
-- this is the correct way:
|
||||
create rule foorule as on insert to foo where f1 < 100
|
||||
do instead insert into foo2 values (new.f1);
|
||||
insert into foo values(2);
|
||||
insert into foo values(100);
|
||||
select * from foo;
|
||||
f1
|
||||
------
|
||||
1001
|
||||
100
|
||||
(2 rows)
|
||||
|
||||
select * from foo2;
|
||||
f1
|
||||
----
|
||||
2
|
||||
(1 row)
|
||||
|
||||
drop rule foorule;
|
||||
drop table foo;
|
||||
drop table foo2;
|
||||
--
|
||||
-- Check that ruleutils are working
|
||||
--
|
||||
|
@ -1200,7 +1241,7 @@ SELECT tablename, rulename, definition FROM pg_rules
|
|||
rtest_order1 | rtest_order_r1 | CREATE RULE rtest_order_r1 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 1 - this should run 3rd or 4th'::text);
|
||||
rtest_order1 | rtest_order_r2 | CREATE RULE rtest_order_r2 AS ON INSERT TO rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 2 - this should run 1st'::text);
|
||||
rtest_order1 | rtest_order_r3 | CREATE RULE rtest_order_r3 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 3 - this should run 3rd or 4th'::text);
|
||||
rtest_order1 | rtest_order_r4 | CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE (rtest_order2.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 4 - this should run 2nd'::text);
|
||||
rtest_order1 | rtest_order_r4 | CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 4 - this should run 2nd'::text);
|
||||
rtest_person | rtest_pers_del | CREATE RULE rtest_pers_del AS ON DELETE TO rtest_person DO DELETE FROM rtest_admin WHERE (rtest_admin.pname = old.pname);
|
||||
rtest_person | rtest_pers_upd | CREATE RULE rtest_pers_upd AS ON UPDATE TO rtest_person DO UPDATE rtest_admin SET pname = new.pname WHERE (rtest_admin.pname = old.pname);
|
||||
rtest_system | rtest_sys_del | CREATE RULE rtest_sys_del AS ON DELETE TO rtest_system DO (DELETE FROM rtest_interface WHERE (rtest_interface.sysname = old.sysname); DELETE FROM rtest_admin WHERE (rtest_admin.sysname = old.sysname); );
|
||||
|
|
|
@ -120,7 +120,7 @@ ERROR: GROUP BY position 3 is not in target list
|
|||
SELECT count(*) FROM test_missing_target x, test_missing_target y
|
||||
WHERE x.a = y.a
|
||||
GROUP BY b ORDER BY b;
|
||||
ERROR: Column 'b' is ambiguous
|
||||
ERROR: Column reference "b" is ambiguous
|
||||
-- order w/ target under ambiguous condition
|
||||
-- failure NOT expected
|
||||
SELECT a, a FROM test_missing_target
|
||||
|
@ -282,7 +282,7 @@ SELECT count(b) FROM test_missing_target
|
|||
SELECT count(x.a) FROM test_missing_target x, test_missing_target y
|
||||
WHERE x.a = y.a
|
||||
GROUP BY b/2 ORDER BY b/2;
|
||||
ERROR: Column 'b' is ambiguous
|
||||
ERROR: Column reference "b" is ambiguous
|
||||
-- group w/ existing GROUP BY target under ambiguous condition
|
||||
SELECT x.b/2, count(x.b) FROM test_missing_target x, test_missing_target y
|
||||
WHERE x.a = y.a
|
||||
|
@ -299,7 +299,7 @@ SELECT x.b/2, count(x.b) FROM test_missing_target x, test_missing_target y
|
|||
SELECT count(b) FROM test_missing_target x, test_missing_target y
|
||||
WHERE x.a = y.a
|
||||
GROUP BY x.b/2;
|
||||
ERROR: Column 'b' is ambiguous
|
||||
ERROR: Column reference "b" is ambiguous
|
||||
-- group w/o existing GROUP BY target under ambiguous condition
|
||||
-- into a table
|
||||
SELECT count(x.b) INTO TABLE test_missing_target3
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--
|
||||
-- JOIN
|
||||
-- Test join clauses
|
||||
-- Test JOIN clauses
|
||||
--
|
||||
|
||||
CREATE TABLE J1_TBL (
|
||||
|
@ -34,6 +34,7 @@ INSERT INTO J2_TBL VALUES (1, -1);
|
|||
INSERT INTO J2_TBL VALUES (2, 2);
|
||||
INSERT INTO J2_TBL VALUES (3, -3);
|
||||
INSERT INTO J2_TBL VALUES (2, 4);
|
||||
INSERT INTO J2_TBL VALUES (5, -5);
|
||||
|
||||
--
|
||||
-- CORRELATION NAMES
|
||||
|
@ -86,6 +87,9 @@ SELECT '' AS "xxx", tx.ii, tx.jj, tx.kk
|
|||
FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e))
|
||||
AS tx (ii, jj, tt, ii2, kk);
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b;
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
|
@ -128,9 +132,6 @@ SELECT '' AS "xxx", *
|
|||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
|
||||
|
||||
-- mismatch number of columns
|
||||
-- currently, Postgres will fill in with underlying names
|
||||
SELECT '' AS "xxx", *
|
||||
|
@ -147,9 +148,6 @@ SELECT '' AS "xxx", *
|
|||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.k);
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL CROSS JOIN J2_TBL;
|
||||
|
||||
|
||||
--
|
||||
-- Non-equi-joins
|
||||
|
@ -163,23 +161,25 @@ SELECT '' AS "xxx", *
|
|||
-- Outer joins
|
||||
--
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL OUTER JOIN J2_TBL USING (i);
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i);
|
||||
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i);
|
||||
|
||||
-- Note that OUTER is a noise word
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i);
|
||||
FROM J1_TBL FULL JOIN J2_TBL USING (i);
|
||||
|
||||
|
||||
--
|
||||
-- More complicated constructs
|
||||
--
|
||||
|
||||
-- UNION JOIN isn't implemented yet
|
||||
SELECT '' AS "xxx", *
|
||||
FROM J1_TBL UNION JOIN J2_TBL;
|
||||
|
||||
--
|
||||
-- Clean up
|
||||
--
|
||||
|
|
|
@ -686,6 +686,39 @@ SELECT * FROM shoe ORDER BY shoename;
|
|||
SELECT count(*) FROM shoe;
|
||||
|
||||
|
||||
--
|
||||
-- Simple test of qualified ON INSERT ... this did not work in 7.0 ...
|
||||
--
|
||||
create table foo (f1 int);
|
||||
create table foo2 (f1 int);
|
||||
|
||||
create rule foorule as on insert to foo where f1 < 100
|
||||
do instead nothing;
|
||||
|
||||
insert into foo values(1);
|
||||
insert into foo values(1001);
|
||||
select * from foo;
|
||||
|
||||
drop rule foorule;
|
||||
|
||||
-- this should fail because f1 is not exposed for unqualified reference:
|
||||
create rule foorule as on insert to foo where f1 < 100
|
||||
do instead insert into foo2 values (f1);
|
||||
-- this is the correct way:
|
||||
create rule foorule as on insert to foo where f1 < 100
|
||||
do instead insert into foo2 values (new.f1);
|
||||
|
||||
insert into foo values(2);
|
||||
insert into foo values(100);
|
||||
|
||||
select * from foo;
|
||||
select * from foo2;
|
||||
|
||||
drop rule foorule;
|
||||
drop table foo;
|
||||
drop table foo2;
|
||||
|
||||
|
||||
--
|
||||
-- Check that ruleutils are working
|
||||
--
|
||||
|
|
Loading…
Reference in New Issue