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:
Tom Lane 2000-09-12 21:07:18 +00:00
parent b5c0ab278b
commit ed5003c584
93 changed files with 6386 additions and 4262 deletions

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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);
}
/* ----------------

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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,

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 */

View File

@ -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);
/*

View File

@ -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);
}
/*

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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));

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}
/*

View File

@ -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

View File

@ -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);
}

View File

@ -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];
}

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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);
}
/*

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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 */
/* ----------------------------------------------------------------

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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;
/* ---------------

View File

@ -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 */

View File

@ -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;

View File

@ -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) && \

View File

@ -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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
--

View File

@ -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

View File

@ -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); );

View File

@ -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

View File

@ -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
--

View File

@ -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
--