mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-13 19:57:53 +08:00
Subselects in FROM clause, per ISO syntax: FROM (SELECT ...) [AS] alias.
(Don't forget that an alias is required.) Views reimplemented as expanding to subselect-in-FROM. Grouping, aggregates, DISTINCT in views actually work now (he says optimistically). No UNION support in subselects/views yet, but I have some ideas about that. Rule-related permissions checking moved out of rewriter and into executor. INITDB REQUIRED!
This commit is contained in:
parent
6f64c2e54a
commit
3a94e789f5
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.144 2000/09/12 21:06:46 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.145 2000/09/29 18:21:25 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@ -1543,8 +1543,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
|
||||
rte->eref->relname = RelationGetRelationName(rel);
|
||||
rte->inh = false;
|
||||
rte->inFromCl = true;
|
||||
rte->skipAcl = false;
|
||||
adsrc = deparse_expression(expr, lcons(lcons(rte, NIL), NIL), false);
|
||||
adsrc = deparse_expression(expr, makeList1(makeList1(rte)), false);
|
||||
|
||||
values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
|
||||
values[Anum_pg_attrdef_adnum - 1] = attnum;
|
||||
@ -1626,8 +1625,7 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
|
||||
rte->eref->relname = RelationGetRelationName(rel);
|
||||
rte->inh = false;
|
||||
rte->inFromCl = true;
|
||||
rte->skipAcl = false;
|
||||
ccsrc = deparse_expression(expr, lcons(lcons(rte, NIL), NIL), false);
|
||||
ccsrc = deparse_expression(expr, makeList1(makeList1(rte)), false);
|
||||
|
||||
values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
|
||||
values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
|
||||
@ -1750,7 +1748,7 @@ AddRelationRawConstraints(Relation rel,
|
||||
pstate = make_parsestate(NULL);
|
||||
makeRangeTable(pstate, NULL);
|
||||
rte = addRangeTableEntry(pstate, relname, NULL, false, true);
|
||||
addRTEtoJoinTree(pstate, rte);
|
||||
addRTEtoJoinList(pstate, rte);
|
||||
|
||||
/*
|
||||
* Process column default expressions.
|
||||
@ -1774,6 +1772,14 @@ AddRelationRawConstraints(Relation rel,
|
||||
if (contain_var_clause(expr))
|
||||
elog(ERROR, "Cannot use attribute(s) in DEFAULT clause");
|
||||
|
||||
/*
|
||||
* No subplans or aggregates, either...
|
||||
*/
|
||||
if (contain_subplans(expr))
|
||||
elog(ERROR, "Cannot use subselect in DEFAULT clause");
|
||||
if (contain_agg_clause(expr))
|
||||
elog(ERROR, "Cannot use aggregate in DEFAULT clause");
|
||||
|
||||
/*
|
||||
* Check that it will be possible to coerce the expression to the
|
||||
* column's type. We store the expression without coercion,
|
||||
@ -1884,9 +1890,17 @@ AddRelationRawConstraints(Relation rel,
|
||||
* Make sure no outside relations are referred to.
|
||||
*/
|
||||
if (length(pstate->p_rtable) != 1)
|
||||
elog(ERROR, "Only relation '%s' can be referenced in CHECK",
|
||||
elog(ERROR, "Only relation \"%s\" can be referenced in CHECK",
|
||||
relname);
|
||||
|
||||
/*
|
||||
* No subplans or aggregates, either...
|
||||
*/
|
||||
if (contain_subplans(expr))
|
||||
elog(ERROR, "Cannot use subselect in CHECK clause");
|
||||
if (contain_agg_clause(expr))
|
||||
elog(ERROR, "Cannot use aggregate in CHECK clause");
|
||||
|
||||
/*
|
||||
* Might as well try to reduce any constant expressions.
|
||||
*/
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.103 2000/09/12 21:06:47 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.104 2000/09/29 18:21:26 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The PerformAddAttribute() code, like most of the relation
|
||||
@ -1135,7 +1135,7 @@ AlterTableAddConstraint(char *relationName,
|
||||
else
|
||||
name="<unnamed>";
|
||||
|
||||
constlist=lcons(constr, NIL);
|
||||
constlist = makeList1(constr);
|
||||
|
||||
rel = heap_openr(relationName, AccessExclusiveLock);
|
||||
|
||||
@ -1158,10 +1158,11 @@ AlterTableAddConstraint(char *relationName,
|
||||
makeRangeTable(pstate, NULL);
|
||||
rte = addRangeTableEntry(pstate, relationName, NULL,
|
||||
false, true);
|
||||
addRTEtoJoinTree(pstate, rte);
|
||||
addRTEtoJoinList(pstate, rte);
|
||||
|
||||
/* Convert the A_EXPR in raw_expr into an EXPR */
|
||||
expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST);
|
||||
expr = transformExpr(pstate, constr->raw_expr,
|
||||
EXPR_COLUMN_FIRST);
|
||||
|
||||
/*
|
||||
* Make sure it yields a boolean result.
|
||||
@ -1185,14 +1186,14 @@ AlterTableAddConstraint(char *relationName,
|
||||
/* And fix the opids */
|
||||
fix_opids(expr);
|
||||
|
||||
qual = lcons(expr, NIL);
|
||||
qual = makeList1(expr);
|
||||
|
||||
rte = makeNode(RangeTblEntry);
|
||||
rte->relname = relationName;
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->eref = makeNode(Attr);
|
||||
rte->eref->relname = relationName;
|
||||
rtlist = lcons(rte, NIL);
|
||||
rtlist = makeList1(rte);
|
||||
|
||||
/*
|
||||
* Scan through the rows now, making the necessary things
|
||||
|
@ -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.58 2000/09/12 21:06:47 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.59 2000/09/29 18:21:26 tgl Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -176,6 +176,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
|
||||
case T_IndexScan:
|
||||
pname = "Index Scan";
|
||||
break;
|
||||
case T_TidScan:
|
||||
pname = "Tid Scan";
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
pname = "Subquery Scan";
|
||||
break;
|
||||
case T_Material:
|
||||
pname = "Materialize";
|
||||
break;
|
||||
@ -194,9 +200,6 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
|
||||
case T_Hash:
|
||||
pname = "Hash";
|
||||
break;
|
||||
case T_TidScan:
|
||||
pname = "Tid Scan";
|
||||
break;
|
||||
default:
|
||||
pname = "???";
|
||||
break;
|
||||
@ -225,37 +228,27 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
|
||||
case T_TidScan:
|
||||
if (((Scan *) plan)->scanrelid > 0)
|
||||
{
|
||||
RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
|
||||
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
|
||||
es->rtable);
|
||||
|
||||
/* Assume it's on a real relation */
|
||||
Assert(rte->relname);
|
||||
|
||||
appendStringInfo(str, " on %s",
|
||||
stringStringInfo(rte->relname));
|
||||
if (rte->alias != NULL)
|
||||
{
|
||||
if ((strcmp(rte->alias->relname, rte->relname) != 0)
|
||||
|| (length(rte->alias->attrs) > 0))
|
||||
{
|
||||
appendStringInfo(str, " %s",
|
||||
stringStringInfo(rte->alias->relname));
|
||||
if (strcmp(rte->eref->relname, rte->relname) != 0)
|
||||
appendStringInfo(str, " %s",
|
||||
stringStringInfo(rte->eref->relname));
|
||||
}
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
if (((Scan *) plan)->scanrelid > 0)
|
||||
{
|
||||
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
|
||||
es->rtable);
|
||||
|
||||
if (length(rte->alias->attrs) > 0)
|
||||
{
|
||||
List *c;
|
||||
int firstEntry = true;
|
||||
|
||||
appendStringInfo(str, " (");
|
||||
foreach(c, rte->alias->attrs)
|
||||
{
|
||||
if (!firstEntry)
|
||||
{
|
||||
appendStringInfo(str, ", ");
|
||||
firstEntry = false;
|
||||
}
|
||||
appendStringInfo(str, "%s", strVal(lfirst(c)));
|
||||
}
|
||||
appendStringInfo(str, ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
appendStringInfo(str, " %s",
|
||||
stringStringInfo(rte->eref->relname));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -284,7 +277,8 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " -> ");
|
||||
explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 2, es);
|
||||
explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
|
||||
indent + 4, es);
|
||||
}
|
||||
es->rtable = saved_rtable;
|
||||
}
|
||||
@ -307,32 +301,12 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
|
||||
explain_outNode(str, innerPlan(plan), indent + 3, es);
|
||||
}
|
||||
|
||||
/* subPlan-s */
|
||||
if (plan->subPlan)
|
||||
if (IsA(plan, Append))
|
||||
{
|
||||
List *saved_rtable = es->rtable;
|
||||
List *lst;
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " SubPlan\n");
|
||||
foreach(lst, plan->subPlan)
|
||||
{
|
||||
es->rtable = ((SubPlan *) lfirst(lst))->rtable;
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " -> ");
|
||||
explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 4, es);
|
||||
}
|
||||
es->rtable = saved_rtable;
|
||||
}
|
||||
|
||||
if (nodeTag(plan) == T_Append)
|
||||
{
|
||||
List *saved_rtable = es->rtable;
|
||||
List *lst;
|
||||
int whichplan = 0;
|
||||
Append *appendplan = (Append *) plan;
|
||||
List *saved_rtable = es->rtable;
|
||||
int whichplan = 0;
|
||||
List *lst;
|
||||
|
||||
foreach(lst, appendplan->appendplans)
|
||||
{
|
||||
@ -351,14 +325,55 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " -> ");
|
||||
appendStringInfo(str, " -> ");
|
||||
|
||||
explain_outNode(str, subnode, indent + 4, es);
|
||||
explain_outNode(str, subnode, indent + 3, es);
|
||||
|
||||
whichplan++;
|
||||
}
|
||||
es->rtable = saved_rtable;
|
||||
}
|
||||
|
||||
if (IsA(plan, SubqueryScan))
|
||||
{
|
||||
SubqueryScan *subqueryscan = (SubqueryScan *) plan;
|
||||
Plan *subnode = subqueryscan->subplan;
|
||||
RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
|
||||
es->rtable);
|
||||
List *saved_rtable = es->rtable;
|
||||
|
||||
Assert(rte->subquery != NULL);
|
||||
es->rtable = rte->subquery->rtable;
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " -> ");
|
||||
|
||||
explain_outNode(str, subnode, indent + 3, es);
|
||||
|
||||
es->rtable = saved_rtable;
|
||||
}
|
||||
|
||||
/* subPlan-s */
|
||||
if (plan->subPlan)
|
||||
{
|
||||
List *saved_rtable = es->rtable;
|
||||
List *lst;
|
||||
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " SubPlan\n");
|
||||
foreach(lst, plan->subPlan)
|
||||
{
|
||||
es->rtable = ((SubPlan *) lfirst(lst))->rtable;
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " -> ");
|
||||
explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
|
||||
indent + 4, es);
|
||||
}
|
||||
es->rtable = saved_rtable;
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
|
@ -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.48 2000/09/12 21:06:47 tgl Exp $
|
||||
* $Id: view.c,v 1.49 2000/09/29 18:21:26 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -39,70 +39,60 @@
|
||||
static void
|
||||
DefineVirtualRelation(char *relname, List *tlist)
|
||||
{
|
||||
CreateStmt createStmt;
|
||||
CreateStmt *createStmt = makeNode(CreateStmt);
|
||||
List *attrList,
|
||||
*t;
|
||||
TargetEntry *entry;
|
||||
Resdom *res;
|
||||
char *resname;
|
||||
char *restypename;
|
||||
|
||||
/*
|
||||
* create a list with one entry per attribute of this relation. Each
|
||||
* entry is a two element list. The first element is the name of the
|
||||
* attribute (a string) and the second the name of the type (NOTE: a
|
||||
* string, not a type id!).
|
||||
* create a list of ColumnDef nodes based on the names and types of
|
||||
* the (non-junk) targetlist items from the view's SELECT list.
|
||||
*/
|
||||
attrList = NIL;
|
||||
if (tlist != NIL)
|
||||
foreach(t, tlist)
|
||||
{
|
||||
foreach(t, tlist)
|
||||
TargetEntry *entry = lfirst(t);
|
||||
Resdom *res = entry->resdom;
|
||||
|
||||
if (! res->resjunk)
|
||||
{
|
||||
char *resname = res->resname;
|
||||
char *restypename = typeidTypeName(res->restype);
|
||||
ColumnDef *def = makeNode(ColumnDef);
|
||||
TypeName *typename;
|
||||
|
||||
/*
|
||||
* find the names of the attribute & its type
|
||||
*/
|
||||
entry = lfirst(t);
|
||||
res = entry->resdom;
|
||||
resname = res->resname;
|
||||
restypename = typeidTypeName(res->restype);
|
||||
|
||||
typename = makeNode(TypeName);
|
||||
|
||||
typename->name = pstrdup(restypename);
|
||||
typename->typmod = res->restypmod;
|
||||
TypeName *typename = makeNode(TypeName);
|
||||
|
||||
def->colname = pstrdup(resname);
|
||||
|
||||
typename->name = pstrdup(restypename);
|
||||
typename->typmod = res->restypmod;
|
||||
def->typename = typename;
|
||||
|
||||
def->is_not_null = false;
|
||||
def->is_sequence = false;
|
||||
def->raw_default = NULL;
|
||||
def->cooked_default = NULL;
|
||||
def->constraints = NIL;
|
||||
|
||||
attrList = lappend(attrList, def);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (attrList == NIL)
|
||||
elog(ERROR, "attempted to define virtual relation with no attrs");
|
||||
|
||||
/*
|
||||
* now create the parametesr for keys/inheritance etc. All of them are
|
||||
* now create the parameters for keys/inheritance etc. All of them are
|
||||
* nil...
|
||||
*/
|
||||
createStmt.relname = relname;
|
||||
createStmt.istemp = false;
|
||||
createStmt.tableElts = attrList;
|
||||
/* createStmt.tableType = NULL;*/
|
||||
createStmt.inhRelnames = NIL;
|
||||
createStmt.constraints = NIL;
|
||||
createStmt->relname = relname;
|
||||
createStmt->istemp = false;
|
||||
createStmt->tableElts = attrList;
|
||||
createStmt->inhRelnames = NIL;
|
||||
createStmt->constraints = NIL;
|
||||
|
||||
/*
|
||||
* finally create the relation...
|
||||
*/
|
||||
DefineRelation(&createStmt, RELKIND_VIEW);
|
||||
DefineRelation(createStmt, RELKIND_VIEW);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@ -149,13 +139,12 @@ FormViewRetrieveRule(char *viewName, Query *viewParse)
|
||||
|
||||
attr = makeNode(Attr);
|
||||
attr->relname = pstrdup(viewName);
|
||||
/* attr->refname = pstrdup(viewName);*/
|
||||
rule->rulename = pstrdup(rname);
|
||||
rule->whereClause = NULL;
|
||||
rule->event = CMD_SELECT;
|
||||
rule->object = attr;
|
||||
rule->instead = true;
|
||||
rule->actions = lcons(viewParse, NIL);
|
||||
rule->actions = makeList1(viewParse);
|
||||
|
||||
return rule;
|
||||
}
|
||||
@ -231,6 +220,10 @@ UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
|
||||
rt_entry2 = addRangeTableEntry(NULL, viewName,
|
||||
makeAttr("*NEW*", NULL),
|
||||
false, false);
|
||||
/* Must override addRangeTableEntry's default access-check flags */
|
||||
rt_entry1->checkForRead = false;
|
||||
rt_entry2->checkForRead = false;
|
||||
|
||||
new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
|
||||
|
||||
/*
|
||||
|
@ -4,7 +4,7 @@
|
||||
# Makefile for executor
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.13 2000/08/31 16:09:56 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.14 2000/09/29 18:21:28 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -18,7 +18,7 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
|
||||
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
|
||||
nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSort.o \
|
||||
nodeUnique.o nodeGroup.o spi.o nodeSubplan.o \
|
||||
nodeTidscan.o
|
||||
nodeSubqueryscan.o nodeTidscan.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execAmi.c,v 1.51 2000/08/03 19:19:30 tgl Exp $
|
||||
* $Id: execAmi.c,v 1.52 2000/09/29 18:21:28 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -44,6 +44,7 @@
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
|
||||
static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
|
||||
@ -304,6 +305,14 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecTidReScan((TidScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_Material:
|
||||
ExecMaterialReScan((Material *) node, exprCtxt, parent);
|
||||
break;
|
||||
@ -348,10 +357,6 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
ExecReScanAppend((Append *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecTidReScan((TidScan *) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecReScan: node type %d not supported",
|
||||
nodeTag(node));
|
||||
|
@ -27,7 +27,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.127 2000/09/12 21:06:48 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.128 2000/09/29 18:21:28 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -69,13 +69,11 @@ static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
static TupleTableSlot *EvalPlanQualNext(EState *estate);
|
||||
static void EndEvalPlanQual(EState *estate);
|
||||
static void ExecCheckQueryPerms(CmdType operation, Query *parseTree,
|
||||
Plan *plan);
|
||||
static void ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned);
|
||||
static void ExecCheckRTPerms(List *rangeTable, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned);
|
||||
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
bool isResultRelation, bool resultIsScanned);
|
||||
Plan *plan);
|
||||
static void ExecCheckPlanPerms(Plan *plan, List *rangeTable,
|
||||
CmdType operation);
|
||||
static void ExecCheckRTPerms(List *rangeTable, CmdType operation);
|
||||
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
|
||||
|
||||
/* end of local decls */
|
||||
|
||||
@ -390,51 +388,15 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
|
||||
static void
|
||||
ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
|
||||
{
|
||||
List *rangeTable = parseTree->rtable;
|
||||
int resultRelation = parseTree->resultRelation;
|
||||
bool resultIsScanned = false;
|
||||
List *lp;
|
||||
|
||||
/*
|
||||
* 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((Node *) parseTree);
|
||||
|
||||
resultIsScanned = intMember(resultRelation, qvars);
|
||||
freeList(qvars);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check RTEs in the query's primary rangetable.
|
||||
*/
|
||||
ExecCheckRTPerms(rangeTable, operation, resultRelation, resultIsScanned);
|
||||
|
||||
/*
|
||||
* Check SELECT FOR UPDATE access rights.
|
||||
*/
|
||||
foreach(lp, parseTree->rowMark)
|
||||
{
|
||||
RowMark *rm = lfirst(lp);
|
||||
|
||||
if (!(rm->info & ROW_ACL_FOR_UPDATE))
|
||||
continue;
|
||||
|
||||
ExecCheckRTEPerms(rt_fetch(rm->rti, rangeTable),
|
||||
CMD_UPDATE, true, false);
|
||||
}
|
||||
ExecCheckRTPerms(parseTree->rtable, operation);
|
||||
|
||||
/*
|
||||
* Search for subplans and APPEND nodes to check their rangetables.
|
||||
*/
|
||||
ExecCheckPlanPerms(plan, operation, resultRelation, resultIsScanned);
|
||||
ExecCheckPlanPerms(plan, parseTree->rtable, operation);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -447,8 +409,7 @@ ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
|
||||
* in the query's main rangetable. But at the moment, they're not.
|
||||
*/
|
||||
static void
|
||||
ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned)
|
||||
ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
|
||||
{
|
||||
List *subp;
|
||||
|
||||
@ -461,28 +422,37 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) lfirst(subp);
|
||||
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
|
||||
ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
|
||||
ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
|
||||
}
|
||||
foreach(subp, plan->subPlan)
|
||||
{
|
||||
SubPlan *subplan = (SubPlan *) lfirst(subp);
|
||||
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT, 0, false);
|
||||
ExecCheckPlanPerms(subplan->plan, CMD_SELECT, 0, false);
|
||||
ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
|
||||
ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
|
||||
}
|
||||
|
||||
/* Check lower plan nodes */
|
||||
|
||||
ExecCheckPlanPerms(plan->lefttree, operation,
|
||||
resultRelation, resultIsScanned);
|
||||
ExecCheckPlanPerms(plan->righttree, operation,
|
||||
resultRelation, resultIsScanned);
|
||||
ExecCheckPlanPerms(plan->lefttree, rangeTable, operation);
|
||||
ExecCheckPlanPerms(plan->righttree, rangeTable, operation);
|
||||
|
||||
/* Do node-type-specific checks */
|
||||
|
||||
switch (nodeTag(plan))
|
||||
{
|
||||
case T_SubqueryScan:
|
||||
{
|
||||
SubqueryScan *scan = (SubqueryScan *) plan;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
/* Recursively check the subquery */
|
||||
rte = rt_fetch(scan->scan.scanrelid, rangeTable);
|
||||
Assert(rte->subquery != NULL);
|
||||
ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
|
||||
break;
|
||||
}
|
||||
case T_Append:
|
||||
{
|
||||
Append *app = (Append *) plan;
|
||||
@ -490,43 +460,31 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
|
||||
if (app->inheritrelid > 0)
|
||||
{
|
||||
/* Append implements expansion of inheritance */
|
||||
ExecCheckRTPerms(app->inheritrtable, operation);
|
||||
|
||||
/*
|
||||
* Append implements expansion of inheritance; all
|
||||
* members of inheritrtable list will be plugged into
|
||||
* same RTE slot. Therefore, they are either all
|
||||
* result relations or none.
|
||||
*/
|
||||
List *rtable;
|
||||
|
||||
foreach(rtable, app->inheritrtable)
|
||||
/* Check appended plans w/outer rangetable */
|
||||
foreach(appendplans, app->appendplans)
|
||||
{
|
||||
ExecCheckRTEPerms((RangeTblEntry *) lfirst(rtable),
|
||||
operation,
|
||||
(app->inheritrelid == resultRelation),
|
||||
resultIsScanned);
|
||||
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
|
||||
rangeTable,
|
||||
operation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Append implements UNION, which must be a SELECT */
|
||||
List *rtables;
|
||||
List *rtables = app->unionrtables;
|
||||
|
||||
foreach(rtables, app->unionrtables)
|
||||
/* Check appended plans with their rangetables */
|
||||
foreach(appendplans, app->appendplans)
|
||||
{
|
||||
ExecCheckRTPerms((List *) lfirst(rtables),
|
||||
CMD_SELECT, 0, false);
|
||||
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
|
||||
(List *) lfirst(rtables),
|
||||
CMD_SELECT);
|
||||
rtables = lnext(rtables);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check appended plans */
|
||||
foreach(appendplans, app->appendplans)
|
||||
{
|
||||
ExecCheckPlanPerms((Plan *) lfirst(appendplans),
|
||||
operation,
|
||||
resultRelation,
|
||||
resultIsScanned);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -538,28 +496,17 @@ ExecCheckPlanPerms(Plan *plan, CmdType operation,
|
||||
/*
|
||||
* ExecCheckRTPerms
|
||||
* Check access permissions for all relations listed in a range table.
|
||||
*
|
||||
* If resultRelation is not 0, it is the RT index of the relation to be
|
||||
* treated as the result relation. All other relations are assumed to be
|
||||
* read-only for the query.
|
||||
*/
|
||||
static void
|
||||
ExecCheckRTPerms(List *rangeTable, CmdType operation,
|
||||
int resultRelation, bool resultIsScanned)
|
||||
ExecCheckRTPerms(List *rangeTable, CmdType operation)
|
||||
{
|
||||
int rtindex = 0;
|
||||
List *lp;
|
||||
|
||||
foreach(lp, rangeTable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(lp);
|
||||
|
||||
++rtindex;
|
||||
|
||||
ExecCheckRTEPerms(rte,
|
||||
operation,
|
||||
(rtindex == resultRelation),
|
||||
resultIsScanned);
|
||||
ExecCheckRTEPerms(rte, operation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,45 +515,48 @@ ExecCheckRTPerms(List *rangeTable, CmdType operation,
|
||||
* Check access permissions for a single RTE.
|
||||
*/
|
||||
static void
|
||||
ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
bool isResultRelation, bool resultIsScanned)
|
||||
ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
|
||||
{
|
||||
char *relName;
|
||||
Oid userid;
|
||||
int32 aclcheck_result;
|
||||
|
||||
if (rte->skipAcl)
|
||||
{
|
||||
|
||||
/*
|
||||
* This happens if the access to this table is due to a view query
|
||||
* rewriting - the rewrite handler already checked the permissions
|
||||
* against the view owner, so we just skip this entry.
|
||||
*/
|
||||
/*
|
||||
* If it's a subquery RTE, ignore it --- it will be checked when
|
||||
* ExecCheckPlanPerms finds the SubqueryScan node for it.
|
||||
*/
|
||||
if (rte->subquery)
|
||||
return;
|
||||
}
|
||||
|
||||
relName = rte->relname;
|
||||
|
||||
/*
|
||||
* userid to check as: current user unless we have a setuid indication.
|
||||
*
|
||||
* Note: GetUserId() is presently fast enough that there's no harm
|
||||
* in calling it separately for each RTE. If that stops being true,
|
||||
* we could call it once in ExecCheckQueryPerms and pass the userid
|
||||
* down from there. But for now, no need for the extra clutter.
|
||||
*/
|
||||
userid = GetUserId();
|
||||
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
|
||||
|
||||
#define CHECK(MODE) pg_aclcheck(relName, userid, MODE)
|
||||
|
||||
if (isResultRelation)
|
||||
if (rte->checkForRead)
|
||||
{
|
||||
if (resultIsScanned)
|
||||
{
|
||||
aclcheck_result = CHECK(ACL_RD);
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
aclcheck_result = CHECK(ACL_RD);
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
|
||||
if (rte->checkForWrite)
|
||||
{
|
||||
/*
|
||||
* Note: write access in a SELECT context means SELECT FOR UPDATE.
|
||||
* Right now we don't distinguish that from true update as far as
|
||||
* permissions checks are concerned.
|
||||
*/
|
||||
switch (operation)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
@ -615,6 +565,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
aclcheck_result = CHECK(ACL_WR);
|
||||
break;
|
||||
case CMD_SELECT:
|
||||
case CMD_DELETE:
|
||||
case CMD_UPDATE:
|
||||
aclcheck_result = CHECK(ACL_WR);
|
||||
@ -625,13 +576,10 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
|
||||
aclcheck_result = ACLCHECK_OK; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
else
|
||||
aclcheck_result = CHECK(ACL_RD);
|
||||
|
||||
if (aclcheck_result != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
relName, aclcheck_error_strings[aclcheck_result]);
|
||||
}
|
||||
|
||||
|
||||
@ -755,26 +703,23 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
|
||||
/*
|
||||
* Have to lock relations selected for update
|
||||
*/
|
||||
estate->es_rowMark = NULL;
|
||||
if (parseTree->rowMark != NULL)
|
||||
estate->es_rowMark = NIL;
|
||||
if (parseTree->rowMarks != NIL)
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, parseTree->rowMark)
|
||||
foreach(l, parseTree->rowMarks)
|
||||
{
|
||||
RowMark *rm = lfirst(l);
|
||||
Oid relid;
|
||||
Index rti = lfirsti(l);
|
||||
Oid relid = getrelid(rti, rangeTable);
|
||||
Relation relation;
|
||||
execRowMark *erm;
|
||||
|
||||
if (!(rm->info & ROW_MARK_FOR_UPDATE))
|
||||
continue;
|
||||
relid = getrelid(rm->rti, rangeTable);
|
||||
relation = heap_open(relid, RowShareLock);
|
||||
erm = (execRowMark *) palloc(sizeof(execRowMark));
|
||||
erm->relation = relation;
|
||||
erm->rti = rm->rti;
|
||||
sprintf(erm->resname, "ctid%u", rm->rti);
|
||||
erm->rti = rti;
|
||||
sprintf(erm->resname, "ctid%u", rti);
|
||||
estate->es_rowMark = lappend(estate->es_rowMark, erm);
|
||||
}
|
||||
}
|
||||
@ -1097,7 +1042,7 @@ lnext: ;
|
||||
* ctid!! */
|
||||
tupleid = &tuple_ctid;
|
||||
}
|
||||
else if (estate->es_rowMark != NULL)
|
||||
else if (estate->es_rowMark != NIL)
|
||||
{
|
||||
List *l;
|
||||
|
||||
@ -1115,10 +1060,12 @@ lnext: ;
|
||||
erm->resname,
|
||||
&datum,
|
||||
&isNull))
|
||||
elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!", erm->resname);
|
||||
elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!",
|
||||
erm->resname);
|
||||
|
||||
if (isNull)
|
||||
elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!", erm->resname);
|
||||
elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!",
|
||||
erm->resname);
|
||||
|
||||
tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
|
||||
test = heap_mark4update(erm->relation, &tuple, &buffer);
|
||||
@ -1625,10 +1572,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
rte->eref = makeNode(Attr);
|
||||
rte->eref->relname = rte->relname;
|
||||
/* inh, inFromCl, skipAcl won't be used, leave them zero */
|
||||
/* other fields won't be used, leave them zero */
|
||||
|
||||
/* Set up single-entry range table */
|
||||
econtext->ecxt_range_table = lcons(rte, NIL);
|
||||
econtext->ecxt_range_table = makeList1(rte);
|
||||
|
||||
estate->es_result_relation_constraints =
|
||||
(List **) palloc(ncheck * sizeof(List *));
|
||||
|
@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.19 2000/08/13 02:50:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.20 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -90,6 +90,7 @@
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
#include "miscadmin.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
@ -153,6 +154,15 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitIndexScan((IndexScan *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecInitTidScan((TidScan *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
result = ExecInitSubqueryScan((SubqueryScan *) node, estate,
|
||||
parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -165,6 +175,14 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecInitHash((Hash *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecInitHashJoin((HashJoin *) node, estate, parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -189,18 +207,6 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
result = ExecInitAgg((Agg *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecInitHash((Hash *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecInitHashJoin((HashJoin *) node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecInitTidScan((TidScan *) node, estate, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecInitNode: node %d unsupported", nodeTag(node));
|
||||
result = FALSE;
|
||||
@ -272,6 +278,14 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecIndexScan((IndexScan *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
result = ExecSubqueryScan((SubqueryScan *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -284,6 +298,14 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecMergeJoin((MergeJoin *) node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -308,18 +330,6 @@ ExecProcNode(Plan *node, Plan *parent)
|
||||
result = ExecAgg((Agg *) node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
result = ExecTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecProcNode: node %d unsupported", nodeTag(node));
|
||||
result = NULL;
|
||||
@ -356,6 +366,12 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_IndexScan:
|
||||
return ExecCountSlotsIndexScan((IndexScan *) node);
|
||||
|
||||
case T_TidScan:
|
||||
return ExecCountSlotsTidScan((TidScan *) node);
|
||||
|
||||
case T_SubqueryScan:
|
||||
return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -366,6 +382,12 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_MergeJoin:
|
||||
return ExecCountSlotsMergeJoin((MergeJoin *) node);
|
||||
|
||||
case T_Hash:
|
||||
return ExecCountSlotsHash((Hash *) node);
|
||||
|
||||
case T_HashJoin:
|
||||
return ExecCountSlotsHashJoin((HashJoin *) node);
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -385,15 +407,6 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_Agg:
|
||||
return ExecCountSlotsAgg((Agg *) node);
|
||||
|
||||
case T_Hash:
|
||||
return ExecCountSlotsHash((Hash *) node);
|
||||
|
||||
case T_HashJoin:
|
||||
return ExecCountSlotsHashJoin((HashJoin *) node);
|
||||
|
||||
case T_TidScan:
|
||||
return ExecCountSlotsTidScan((TidScan *) node);
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecCountSlotsNode: node not yet supported: %d",
|
||||
nodeTag(node));
|
||||
@ -462,6 +475,14 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndIndexScan((IndexScan *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecEndTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
ExecEndSubqueryScan((SubqueryScan *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
@ -474,6 +495,14 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndMergeJoin((MergeJoin *) node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
ExecEndHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
ExecEndHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
@ -498,22 +527,6 @@ ExecEndNode(Plan *node, Plan *parent)
|
||||
ExecEndAgg((Agg *) node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* XXX add hooks to these
|
||||
* ----------------
|
||||
*/
|
||||
case T_Hash:
|
||||
ExecEndHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
ExecEndHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
ExecEndTidScan((TidScan *) node);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecEndNode: node %d unsupported", nodeTag(node));
|
||||
break;
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.39 2000/09/12 21:06:48 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.40 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -698,6 +698,22 @@ NodeGetResultTupleSlot(Plan *node)
|
||||
}
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
{
|
||||
CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
|
||||
|
||||
slot = scanstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
{
|
||||
CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
|
||||
|
||||
slot = scanstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
case T_Material:
|
||||
{
|
||||
MaterialState *matstate = ((Material *) node)->matstate;
|
||||
@ -762,14 +778,6 @@ NodeGetResultTupleSlot(Plan *node)
|
||||
}
|
||||
break;
|
||||
|
||||
case T_TidScan:
|
||||
{
|
||||
CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
|
||||
|
||||
slot = scanstate->cstate.cs_ResultTupleSlot;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ----------------
|
||||
* should never get here
|
||||
|
289
src/backend/executor/nodeSubqueryscan.c
Normal file
289
src/backend/executor/nodeSubqueryscan.c
Normal file
@ -0,0 +1,289 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSubqueryscan.c
|
||||
* Support routines for scanning subqueries (subselects in rangetable).
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.1 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecSubqueryScan scans a subquery.
|
||||
* ExecSubqueryNext retrieve next tuple in sequential order.
|
||||
* ExecInitSubqueryScan creates and initializes a subqueryscan node.
|
||||
* ExecEndSubqueryScan releases any storage allocated.
|
||||
* ExecSubqueryReScan rescans the relation
|
||||
*
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/execdefs.h"
|
||||
#include "executor/execdesc.h"
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "tcop/pquery.h"
|
||||
|
||||
static TupleTableSlot *SubqueryNext(SubqueryScan *node);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Scan Support
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------------------------------------------------------
|
||||
* SubqueryNext
|
||||
*
|
||||
* This is a workhorse for ExecSubqueryScan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
SubqueryNext(SubqueryScan *node)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
EState *estate;
|
||||
ScanDirection direction;
|
||||
int execdir;
|
||||
TupleTableSlot *slot;
|
||||
Const countOne;
|
||||
|
||||
/* ----------------
|
||||
* get information from the estate and scan state
|
||||
* ----------------
|
||||
*/
|
||||
estate = node->scan.plan.state;
|
||||
subquerystate = (SubqueryScanState *) node->scan.scanstate;
|
||||
direction = estate->es_direction;
|
||||
execdir = ScanDirectionIsBackward(direction) ? EXEC_BACK : EXEC_FOR;
|
||||
slot = subquerystate->csstate.css_ScanTupleSlot;
|
||||
|
||||
/*
|
||||
* Check if we are evaluating PlanQual for tuple of this relation.
|
||||
* Additional checking is not good, but no other way for now. We could
|
||||
* introduce new nodes for this case and handle SubqueryScan --> NewNode
|
||||
* switching in Init/ReScan plan...
|
||||
*/
|
||||
if (estate->es_evTuple != NULL &&
|
||||
estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
|
||||
{
|
||||
ExecClearTuple(slot);
|
||||
if (estate->es_evTupleNull[node->scan.scanrelid - 1])
|
||||
return slot; /* return empty slot */
|
||||
|
||||
/* probably ought to use ExecStoreTuple here... */
|
||||
slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
|
||||
slot->ttc_shouldFree = false;
|
||||
|
||||
/* Flag for the next call that no more tuples */
|
||||
estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
|
||||
return (slot);
|
||||
}
|
||||
|
||||
memset(&countOne, 0, sizeof(countOne));
|
||||
countOne.type = T_Const;
|
||||
countOne.consttype = INT4OID;
|
||||
countOne.constlen = sizeof(int4);
|
||||
countOne.constvalue = Int32GetDatum(1);
|
||||
countOne.constisnull = false;
|
||||
countOne.constbyval = true;
|
||||
countOne.constisset = false;
|
||||
countOne.constiscast = false;
|
||||
|
||||
/* ----------------
|
||||
* get the next tuple from the sub-query
|
||||
* ----------------
|
||||
*/
|
||||
slot = ExecutorRun(subquerystate->sss_SubQueryDesc,
|
||||
subquerystate->sss_SubEState,
|
||||
execdir,
|
||||
NULL, /* offset */
|
||||
(Node *) &countOne);
|
||||
|
||||
subquerystate->csstate.css_ScanTupleSlot = slot;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSubqueryScan(node)
|
||||
*
|
||||
* Scans the subquery sequentially and returns the next qualifying
|
||||
* tuple.
|
||||
* It calls the ExecScan() routine and passes it the access method
|
||||
* which retrieve tuples sequentially.
|
||||
*
|
||||
*/
|
||||
|
||||
TupleTableSlot *
|
||||
ExecSubqueryScan(SubqueryScan *node)
|
||||
{
|
||||
/* ----------------
|
||||
* use SubqueryNext as access method
|
||||
* ----------------
|
||||
*/
|
||||
return ExecScan(&node->scan, (ExecScanAccessMtd) SubqueryNext);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitSubqueryScan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
/* ----------------
|
||||
* SubqueryScan should not have any "normal" children.
|
||||
* ----------------
|
||||
*/
|
||||
Assert(outerPlan((Plan *) node) == NULL);
|
||||
Assert(innerPlan((Plan *) node) == NULL);
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->scan.plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new SubqueryScanState for node
|
||||
* ----------------
|
||||
*/
|
||||
subquerystate = makeNode(SubqueryScanState);
|
||||
node->scan.scanstate = (CommonScanState *) subquerystate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellaneous initialization
|
||||
*
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignExprContext(estate, &subquerystate->csstate.cstate);
|
||||
|
||||
#define SUBQUERYSCAN_NSLOTS 2
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &subquerystate->csstate.cstate);
|
||||
|
||||
/* ----------------
|
||||
* initialize subquery
|
||||
* ----------------
|
||||
*/
|
||||
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
|
||||
Assert(rte->subquery != NULL);
|
||||
|
||||
subquerystate->sss_SubQueryDesc = CreateQueryDesc(rte->subquery,
|
||||
node->subplan,
|
||||
None);
|
||||
subquerystate->sss_SubEState = CreateExecutorState();
|
||||
|
||||
ExecutorStart(subquerystate->sss_SubQueryDesc,
|
||||
subquerystate->sss_SubEState);
|
||||
|
||||
subquerystate->csstate.css_ScanTupleSlot = NULL;
|
||||
subquerystate->csstate.cstate.cs_TupFromTlist = false;
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan *) node, &subquerystate->csstate.cstate);
|
||||
ExecAssignProjectionInfo((Plan *) node, &subquerystate->csstate.cstate);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsSubqueryScan(SubqueryScan *node)
|
||||
{
|
||||
/*
|
||||
* The subplan has its own tuple table and must not be counted here!
|
||||
*/
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
SUBQUERYSCAN_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndSubqueryScan
|
||||
*
|
||||
* frees any storage allocated through C routines.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndSubqueryScan(SubqueryScan *node)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
|
||||
/* ----------------
|
||||
* get information from node
|
||||
* ----------------
|
||||
*/
|
||||
subquerystate = (SubqueryScanState *) node->scan.scanstate;
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info and the scan attribute info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(subquerystate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&subquerystate->csstate.cstate);
|
||||
ExecFreeExprContext(&subquerystate->csstate.cstate);
|
||||
|
||||
/* ----------------
|
||||
* close down subquery
|
||||
* ----------------
|
||||
*/
|
||||
ExecutorEnd(subquerystate->sss_SubQueryDesc,
|
||||
subquerystate->sss_SubEState);
|
||||
|
||||
/* XXX we seem to be leaking the querydesc and sub-EState... */
|
||||
|
||||
subquerystate->csstate.css_ScanTupleSlot = NULL;
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(subquerystate->csstate.cstate.cs_ResultTupleSlot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSubqueryReScan
|
||||
*
|
||||
* Rescans the relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
{
|
||||
SubqueryScanState *subquerystate;
|
||||
EState *estate;
|
||||
|
||||
subquerystate = (SubqueryScanState *) node->scan.scanstate;
|
||||
estate = node->scan.plan.state;
|
||||
|
||||
/* If this is re-scanning of PlanQual ... */
|
||||
if (estate->es_evTuple != NULL &&
|
||||
estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
|
||||
{
|
||||
estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ExecReScan(node->subplan, NULL, NULL);
|
||||
subquerystate->csstate.css_ScanTupleSlot = NULL;
|
||||
}
|
@ -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.122 2000/09/20 15:28:01 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.123 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -300,6 +300,31 @@ _copyTidScan(TidScan *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _copySubqueryScan
|
||||
* ----------------
|
||||
*/
|
||||
static SubqueryScan *
|
||||
_copySubqueryScan(SubqueryScan *from)
|
||||
{
|
||||
SubqueryScan *newnode = makeNode(SubqueryScan);
|
||||
|
||||
/* ----------------
|
||||
* copy node superclass fields
|
||||
* ----------------
|
||||
*/
|
||||
CopyPlanFields((Plan *) from, (Plan *) newnode);
|
||||
CopyScanFields((Scan *) from, (Scan *) newnode);
|
||||
|
||||
/* ----------------
|
||||
* copy remainder of node
|
||||
* ----------------
|
||||
*/
|
||||
Node_Copy(from, newnode, subplan);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------
|
||||
* CopyJoinFields
|
||||
@ -913,6 +938,17 @@ _copyRangeTblRef(RangeTblRef *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static FromExpr *
|
||||
_copyFromExpr(FromExpr *from)
|
||||
{
|
||||
FromExpr *newnode = makeNode(FromExpr);
|
||||
|
||||
Node_Copy(from, newnode, fromlist);
|
||||
Node_Copy(from, newnode, quals);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static JoinExpr *
|
||||
_copyJoinExpr(JoinExpr *from)
|
||||
{
|
||||
@ -1025,9 +1061,11 @@ _copyRelOptInfo(RelOptInfo *from)
|
||||
Node_Copy(from, newnode, cheapest_total_path);
|
||||
newnode->pruneable = from->pruneable;
|
||||
|
||||
newnode->issubquery = from->issubquery;
|
||||
newnode->indexed = from->indexed;
|
||||
newnode->pages = from->pages;
|
||||
newnode->tuples = from->tuples;
|
||||
Node_Copy(from, newnode, subplan);
|
||||
|
||||
Node_Copy(from, newnode, baserestrictinfo);
|
||||
newnode->baserestrictcost = from->baserestrictcost;
|
||||
@ -1306,7 +1344,7 @@ _copyRestrictInfo(RestrictInfo *from)
|
||||
* ----------------
|
||||
*/
|
||||
Node_Copy(from, newnode, clause);
|
||||
newnode->isjoinqual = from->isjoinqual;
|
||||
newnode->ispusheddown = from->ispusheddown;
|
||||
Node_Copy(from, newnode, subclauseindices);
|
||||
newnode->mergejoinoperator = from->mergejoinoperator;
|
||||
newnode->left_sortop = from->left_sortop;
|
||||
@ -1392,22 +1430,14 @@ _copyRangeTblEntry(RangeTblEntry *from)
|
||||
if (from->relname)
|
||||
newnode->relname = pstrdup(from->relname);
|
||||
newnode->relid = from->relid;
|
||||
Node_Copy(from, newnode, subquery);
|
||||
Node_Copy(from, newnode, alias);
|
||||
Node_Copy(from, newnode, eref);
|
||||
newnode->inh = from->inh;
|
||||
newnode->inFromCl = from->inFromCl;
|
||||
newnode->skipAcl = from->skipAcl;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static RowMark *
|
||||
_copyRowMark(RowMark *from)
|
||||
{
|
||||
RowMark *newnode = makeNode(RowMark);
|
||||
|
||||
newnode->rti = from->rti;
|
||||
newnode->info = from->info;
|
||||
newnode->checkForRead = from->checkForRead;
|
||||
newnode->checkForWrite = from->checkForWrite;
|
||||
newnode->checkAsUser = from->checkAsUser;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
@ -1674,8 +1704,8 @@ _copyQuery(Query *from)
|
||||
Node_Copy(from, newnode, jointree);
|
||||
|
||||
Node_Copy(from, newnode, targetList);
|
||||
Node_Copy(from, newnode, qual);
|
||||
Node_Copy(from, newnode, rowMark);
|
||||
|
||||
newnode->rowMarks = listCopy(from->rowMarks);
|
||||
|
||||
Node_Copy(from, newnode, distinctClause);
|
||||
Node_Copy(from, newnode, sortClause);
|
||||
@ -2493,6 +2523,9 @@ copyObject(void *from)
|
||||
case T_TidScan:
|
||||
retval = _copyTidScan(from);
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
retval = _copySubqueryScan(from);
|
||||
break;
|
||||
case T_Join:
|
||||
retval = _copyJoin(from);
|
||||
break;
|
||||
@ -2575,6 +2608,9 @@ copyObject(void *from)
|
||||
case T_RangeTblRef:
|
||||
retval = _copyRangeTblRef(from);
|
||||
break;
|
||||
case T_FromExpr:
|
||||
retval = _copyFromExpr(from);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
retval = _copyJoinExpr(from);
|
||||
break;
|
||||
@ -2881,9 +2917,6 @@ copyObject(void *from)
|
||||
case T_CaseWhen:
|
||||
retval = _copyCaseWhen(from);
|
||||
break;
|
||||
case T_RowMark:
|
||||
retval = _copyRowMark(from);
|
||||
break;
|
||||
case T_FkConstraint:
|
||||
retval = _copyFkConstraint(from);
|
||||
break;
|
||||
|
@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.73 2000/09/12 21:06:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.74 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -311,6 +311,17 @@ _equalRangeTblRef(RangeTblRef *a, RangeTblRef *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalFromExpr(FromExpr *a, FromExpr *b)
|
||||
{
|
||||
if (!equal(a->fromlist, b->fromlist))
|
||||
return false;
|
||||
if (!equal(a->quals, b->quals))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalJoinExpr(JoinExpr *a, JoinExpr *b)
|
||||
{
|
||||
@ -346,7 +357,7 @@ _equalRelOptInfo(RelOptInfo *a, RelOptInfo *b)
|
||||
|
||||
/*
|
||||
* We treat RelOptInfos as equal if they refer to the same base rels
|
||||
* joined in the same order. Is this sufficient?
|
||||
* joined in the same order. Is this appropriate/sufficient?
|
||||
*/
|
||||
return equali(a->relids, b->relids);
|
||||
}
|
||||
@ -495,7 +506,7 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
|
||||
{
|
||||
if (!equal(a->clause, b->clause))
|
||||
return false;
|
||||
if (a->isjoinqual != b->isjoinqual)
|
||||
if (a->ispusheddown != b->ispusheddown)
|
||||
return false;
|
||||
if (!equal(a->subclauseindices, b->subclauseindices))
|
||||
return false;
|
||||
@ -601,9 +612,7 @@ _equalQuery(Query *a, Query *b)
|
||||
return false;
|
||||
if (!equal(a->targetList, b->targetList))
|
||||
return false;
|
||||
if (!equal(a->qual, b->qual))
|
||||
return false;
|
||||
if (!equal(a->rowMark, b->rowMark))
|
||||
if (!equali(a->rowMarks, b->rowMarks))
|
||||
return false;
|
||||
if (!equal(a->distinctClause, b->distinctClause))
|
||||
return false;
|
||||
@ -1651,6 +1660,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
|
||||
return false;
|
||||
if (a->relid != b->relid)
|
||||
return false;
|
||||
if (!equal(a->subquery, b->subquery))
|
||||
return false;
|
||||
if (!equal(a->alias, b->alias))
|
||||
return false;
|
||||
if (!equal(a->eref, b->eref))
|
||||
@ -1659,7 +1670,11 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
|
||||
return false;
|
||||
if (a->inFromCl != b->inFromCl)
|
||||
return false;
|
||||
if (a->skipAcl != b->skipAcl)
|
||||
if (a->checkForRead != b->checkForRead)
|
||||
return false;
|
||||
if (a->checkForWrite != b->checkForWrite)
|
||||
return false;
|
||||
if (a->checkAsUser != b->checkAsUser)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -1676,17 +1691,6 @@ _equalSortClause(SortClause *a, SortClause *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalRowMark(RowMark *a, RowMark *b)
|
||||
{
|
||||
if (a->rti != b->rti)
|
||||
return false;
|
||||
if (a->info != b->info)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalFkConstraint(FkConstraint *a, FkConstraint *b)
|
||||
{
|
||||
@ -1835,6 +1839,9 @@ equal(void *a, void *b)
|
||||
case T_RangeTblRef:
|
||||
retval = _equalRangeTblRef(a, b);
|
||||
break;
|
||||
case T_FromExpr:
|
||||
retval = _equalFromExpr(a, b);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
retval = _equalJoinExpr(a, b);
|
||||
break;
|
||||
@ -2140,9 +2147,6 @@ equal(void *a, void *b)
|
||||
case T_CaseWhen:
|
||||
retval = _equalCaseWhen(a, b);
|
||||
break;
|
||||
case T_RowMark:
|
||||
retval = _equalRowMark(a, b);
|
||||
break;
|
||||
case T_FkConstraint:
|
||||
retval = _equalFkConstraint(a, b);
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.33 2000/09/12 21:06:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.34 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* XXX a few of the following functions are duplicated to handle
|
||||
@ -23,40 +23,9 @@
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
/*
|
||||
* makeList
|
||||
*
|
||||
* Take varargs, terminated by -1, and make a List
|
||||
*/
|
||||
List *
|
||||
makeList(void *elem,...)
|
||||
{
|
||||
va_list args;
|
||||
List *retval = NIL;
|
||||
List *temp = NIL;
|
||||
List *tempcons = NIL;
|
||||
|
||||
va_start(args, elem);
|
||||
|
||||
temp = elem;
|
||||
while (temp != (void *) -1)
|
||||
{
|
||||
temp = lcons(temp, NIL);
|
||||
if (tempcons == NIL)
|
||||
retval = temp;
|
||||
else
|
||||
lnext(tempcons) = temp;
|
||||
tempcons = temp;
|
||||
|
||||
temp = va_arg(args, void *);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* makeInteger
|
||||
@ -307,7 +276,7 @@ sameseti(List *list1, List *list2)
|
||||
* as were in the inputs.
|
||||
*/
|
||||
List *
|
||||
LispUnion(List *l1, List *l2)
|
||||
set_union(List *l1, List *l2)
|
||||
{
|
||||
List *retval = listCopy(l1);
|
||||
List *i;
|
||||
@ -321,7 +290,7 @@ LispUnion(List *l1, List *l2)
|
||||
}
|
||||
|
||||
List *
|
||||
LispUnioni(List *l1, List *l2)
|
||||
set_unioni(List *l1, List *l2)
|
||||
{
|
||||
List *retval = listCopy(l1);
|
||||
List *i;
|
||||
@ -385,7 +354,8 @@ intMember(int l1, List *l2)
|
||||
|
||||
/*
|
||||
* lremove
|
||||
* Removes 'elem' from the the linked list.
|
||||
* Removes 'elem' from the linked list (destructively changing the list!).
|
||||
*
|
||||
* This version matches 'elem' using simple pointer comparison.
|
||||
* See also LispRemove.
|
||||
*/
|
||||
@ -414,7 +384,8 @@ lremove(void *elem, List *list)
|
||||
|
||||
/*
|
||||
* LispRemove
|
||||
* Removes 'elem' from the the linked list.
|
||||
* Removes 'elem' from the linked list (destructively changing the list!).
|
||||
*
|
||||
* This version matches 'elem' using equal().
|
||||
* (If there is more than one equal list member, the first is removed.)
|
||||
* See also lremove.
|
||||
@ -442,10 +413,12 @@ LispRemove(void *elem, List *list)
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
/*
|
||||
* lremovei
|
||||
* lremove() for integer lists.
|
||||
*/
|
||||
List *
|
||||
intLispRemove(int elem, List *list)
|
||||
lremovei(int elem, List *list)
|
||||
{
|
||||
List *l;
|
||||
List *prev = NIL;
|
||||
@ -467,8 +440,6 @@ intLispRemove(int elem, List *list)
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ltruncate
|
||||
* Truncate a list to n elements.
|
||||
|
@ -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.126 2000/09/12 21:06:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.127 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
@ -220,6 +220,9 @@ _outQuery(StringInfo str, Query *node)
|
||||
|
||||
if (node->utilityStmt)
|
||||
{
|
||||
/*
|
||||
* Hack to make up for lack of outfuncs for utility-stmt nodes
|
||||
*/
|
||||
switch (nodeTag(node->utilityStmt))
|
||||
{
|
||||
case T_CreateStmt:
|
||||
@ -239,7 +242,7 @@ _outQuery(StringInfo str, Query *node)
|
||||
break;
|
||||
|
||||
case T_NotifyStmt:
|
||||
appendStringInfo(str, " :utility ");
|
||||
appendStringInfo(str, " :notify ");
|
||||
_outToken(str, ((NotifyStmt *) (node->utilityStmt))->relname);
|
||||
break;
|
||||
|
||||
@ -250,32 +253,34 @@ _outQuery(StringInfo str, Query *node)
|
||||
else
|
||||
appendStringInfo(str, " :utility <>");
|
||||
|
||||
appendStringInfo(str, " :resultRelation %u :into ",
|
||||
appendStringInfo(str, " :resultRelation %d :into ",
|
||||
node->resultRelation);
|
||||
_outToken(str, node->into);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :isPortal %s :isBinary %s :isTemp %s :unionall %s :distinctClause ",
|
||||
appendStringInfo(str, " :isPortal %s :isBinary %s :isTemp %s"
|
||||
" :unionall %s :hasAggs %s :hasSubLinks %s :rtable ",
|
||||
node->isPortal ? "true" : "false",
|
||||
node->isBinary ? "true" : "false",
|
||||
node->isTemp ? "true" : "false",
|
||||
node->unionall ? "true" : "false");
|
||||
_outNode(str, node->distinctClause);
|
||||
|
||||
appendStringInfo(str, " :sortClause ");
|
||||
_outNode(str, node->sortClause);
|
||||
|
||||
appendStringInfo(str, " :rtable ");
|
||||
node->unionall ? "true" : "false",
|
||||
node->hasAggs ? "true" : "false",
|
||||
node->hasSubLinks ? "true" : "false");
|
||||
_outNode(str, node->rtable);
|
||||
|
||||
appendStringInfo(str, " :jointree ");
|
||||
_outNode(str, node->jointree);
|
||||
|
||||
appendStringInfo(str, " :targetlist ");
|
||||
appendStringInfo(str, " :targetList ");
|
||||
_outNode(str, node->targetList);
|
||||
|
||||
appendStringInfo(str, " :qual ");
|
||||
_outNode(str, node->qual);
|
||||
appendStringInfo(str, " :rowMarks ");
|
||||
_outIntList(str, node->rowMarks);
|
||||
|
||||
appendStringInfo(str, " :distinctClause ");
|
||||
_outNode(str, node->distinctClause);
|
||||
|
||||
appendStringInfo(str, " :sortClause ");
|
||||
_outNode(str, node->sortClause);
|
||||
|
||||
appendStringInfo(str, " :groupClause ");
|
||||
_outNode(str, node->groupClause);
|
||||
@ -283,23 +288,17 @@ _outQuery(StringInfo str, Query *node)
|
||||
appendStringInfo(str, " :havingQual ");
|
||||
_outNode(str, node->havingQual);
|
||||
|
||||
appendStringInfo(str, " :hasAggs %s :hasSubLinks %s :unionClause ",
|
||||
node->hasAggs ? "true" : "false",
|
||||
node->hasSubLinks ? "true" : "false");
|
||||
_outNode(str, node->unionClause);
|
||||
|
||||
appendStringInfo(str, " :intersectClause ");
|
||||
_outNode(str, node->intersectClause);
|
||||
|
||||
appendStringInfo(str, " :unionClause ");
|
||||
_outNode(str, node->unionClause);
|
||||
|
||||
appendStringInfo(str, " :limitOffset ");
|
||||
_outNode(str, node->limitOffset);
|
||||
|
||||
appendStringInfo(str, " :limitCount ");
|
||||
_outNode(str, node->limitCount);
|
||||
|
||||
appendStringInfo(str, " :rowMark ");
|
||||
_outNode(str, node->rowMark);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -535,6 +534,19 @@ _outTidScan(StringInfo str, TidScan *node)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* SubqueryScan is a subclass of Scan
|
||||
*/
|
||||
static void
|
||||
_outSubqueryScan(StringInfo str, SubqueryScan *node)
|
||||
{
|
||||
appendStringInfo(str, " SUBQUERYSCAN ");
|
||||
_outPlanInfo(str, (Plan *) node);
|
||||
|
||||
appendStringInfo(str, " :scanrelid %u :subplan ", node->scan.scanrelid);
|
||||
_outNode(str, node->subplan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Material is a subclass of Plan
|
||||
*/
|
||||
@ -863,6 +875,18 @@ _outRangeTblRef(StringInfo str, RangeTblRef *node)
|
||||
node->rtindex);
|
||||
}
|
||||
|
||||
/*
|
||||
* FromExpr
|
||||
*/
|
||||
static void
|
||||
_outFromExpr(StringInfo str, FromExpr *node)
|
||||
{
|
||||
appendStringInfo(str, " FROMEXPR :fromlist ");
|
||||
_outNode(str, node->fromlist);
|
||||
appendStringInfo(str, " :quals ");
|
||||
_outNode(str, node->quals);
|
||||
}
|
||||
|
||||
/*
|
||||
* JoinExpr
|
||||
*/
|
||||
@ -916,36 +940,33 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
|
||||
appendStringInfo(str, " RELOPTINFO :relids ");
|
||||
_outIntList(str, node->relids);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :rows %.0f :width %d :indexed %s :pages %ld :tuples %.0f :targetlist ",
|
||||
appendStringInfo(str, " :rows %.0f :width %d :targetlist ",
|
||||
node->rows,
|
||||
node->width,
|
||||
node->indexed ? "true" : "false",
|
||||
node->pages,
|
||||
node->tuples);
|
||||
node->width);
|
||||
_outNode(str, node->targetlist);
|
||||
|
||||
appendStringInfo(str, " :pathlist ");
|
||||
_outNode(str, node->pathlist);
|
||||
|
||||
appendStringInfo(str, " :cheapest_startup_path ");
|
||||
_outNode(str, node->cheapest_startup_path);
|
||||
appendStringInfo(str, " :cheapest_total_path ");
|
||||
_outNode(str, node->cheapest_total_path);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :pruneable %s :baserestrictinfo ",
|
||||
node->pruneable ? "true" : "false");
|
||||
_outNode(str, node->baserestrictinfo);
|
||||
appendStringInfo(str, " :pruneable %s :issubquery %s :indexed %s :pages %ld :tuples %.0f :subplan ",
|
||||
node->pruneable ? "true" : "false",
|
||||
node->issubquery ? "true" : "false",
|
||||
node->indexed ? "true" : "false",
|
||||
node->pages,
|
||||
node->tuples);
|
||||
_outNode(str, node->subplan);
|
||||
|
||||
appendStringInfo(str,
|
||||
" :baserestrictcost %.2f :outerjoinset ",
|
||||
appendStringInfo(str, " :baserestrictinfo ");
|
||||
_outNode(str, node->baserestrictinfo);
|
||||
appendStringInfo(str, " :baserestrictcost %.2f :outerjoinset ",
|
||||
node->baserestrictcost);
|
||||
_outIntList(str, node->outerjoinset);
|
||||
|
||||
appendStringInfo(str, " :joininfo ");
|
||||
_outNode(str, node->joininfo);
|
||||
|
||||
appendStringInfo(str, " :innerjoin ");
|
||||
_outNode(str, node->innerjoin);
|
||||
}
|
||||
@ -977,21 +998,21 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
|
||||
{
|
||||
appendStringInfo(str, " RTE :relname ");
|
||||
_outToken(str, node->relname);
|
||||
appendStringInfo(str, " :relid %u :alias ",
|
||||
appendStringInfo(str, " :relid %u ",
|
||||
node->relid);
|
||||
appendStringInfo(str, " :subquery ");
|
||||
_outNode(str, node->subquery);
|
||||
appendStringInfo(str, " :alias ");
|
||||
_outNode(str, node->alias);
|
||||
appendStringInfo(str, " :eref ");
|
||||
_outNode(str, node->eref);
|
||||
appendStringInfo(str, " :inh %s :inFromCl %s :skipAcl %s",
|
||||
appendStringInfo(str, " :inh %s :inFromCl %s :checkForRead %s"
|
||||
" :checkForWrite %s :checkAsUser %u",
|
||||
node->inh ? "true" : "false",
|
||||
node->inFromCl ? "true" : "false",
|
||||
node->skipAcl ? "true" : "false");
|
||||
}
|
||||
|
||||
static void
|
||||
_outRowMark(StringInfo str, RowMark *node)
|
||||
{
|
||||
appendStringInfo(str, " ROWMARK :rti %u :info %u", node->rti, node->info);
|
||||
node->checkForRead ? "true" : "false",
|
||||
node->checkForWrite ? "true" : "false",
|
||||
node->checkAsUser);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1151,8 +1172,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
|
||||
appendStringInfo(str, " RESTRICTINFO :clause ");
|
||||
_outNode(str, node->clause);
|
||||
|
||||
appendStringInfo(str, " :isjoinqual %s :subclauseindices ",
|
||||
node->isjoinqual ? "true" : "false");
|
||||
appendStringInfo(str, " :ispusheddown %s :subclauseindices ",
|
||||
node->ispusheddown ? "true" : "false");
|
||||
_outNode(str, node->subclauseindices);
|
||||
|
||||
appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator);
|
||||
@ -1492,6 +1513,9 @@ _outNode(StringInfo str, void *obj)
|
||||
case T_TidScan:
|
||||
_outTidScan(str, obj);
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
_outSubqueryScan(str, obj);
|
||||
break;
|
||||
case T_Material:
|
||||
_outMaterial(str, obj);
|
||||
break;
|
||||
@ -1555,6 +1579,9 @@ _outNode(StringInfo str, void *obj)
|
||||
case T_RangeTblRef:
|
||||
_outRangeTblRef(str, obj);
|
||||
break;
|
||||
case T_FromExpr:
|
||||
_outFromExpr(str, obj);
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
_outJoinExpr(str, obj);
|
||||
break;
|
||||
@ -1573,9 +1600,6 @@ _outNode(StringInfo str, void *obj)
|
||||
case T_RangeTblEntry:
|
||||
_outRangeTblEntry(str, obj);
|
||||
break;
|
||||
case T_RowMark:
|
||||
_outRowMark(str, obj);
|
||||
break;
|
||||
case T_Path:
|
||||
_outPath(str, obj);
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.41 2000/09/25 18:14:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.42 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -39,7 +39,6 @@ print(void *obj)
|
||||
s = nodeToString(obj);
|
||||
printf("%s\n", s);
|
||||
fflush(stdout);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -132,10 +131,15 @@ print_rt(List *rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(l);
|
||||
|
||||
printf("%d\t%s(%s)\t%u\t%d\t%s\n",
|
||||
i, rte->relname, rte->eref->relname, rte->relid,
|
||||
rte->inFromCl,
|
||||
(rte->inh ? "inh" : ""));
|
||||
if (rte->relname)
|
||||
printf("%d\t%s (%s)\t%u",
|
||||
i, rte->relname, rte->eref->relname, rte->relid);
|
||||
else
|
||||
printf("%d\t[subquery] (%s)\t",
|
||||
i, rte->eref->relname);
|
||||
printf("\t%s\t%s\n",
|
||||
(rte->inh ? "inh" : ""),
|
||||
(rte->inFromCl ? "inFromCl" : ""));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@ -286,7 +290,7 @@ plannode_type(Plan *p)
|
||||
{
|
||||
switch (nodeTag(p))
|
||||
{
|
||||
case T_Plan:
|
||||
case T_Plan:
|
||||
return "PLAN";
|
||||
break;
|
||||
case T_Result:
|
||||
@ -304,6 +308,12 @@ plannode_type(Plan *p)
|
||||
case T_IndexScan:
|
||||
return "INDEXSCAN";
|
||||
break;
|
||||
case T_TidScan:
|
||||
return "TIDSCAN";
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
return "SUBQUERYSCAN";
|
||||
break;
|
||||
case T_Join:
|
||||
return "JOIN";
|
||||
break;
|
||||
@ -334,9 +344,6 @@ plannode_type(Plan *p)
|
||||
case T_Group:
|
||||
return "GROUP";
|
||||
break;
|
||||
case T_TidScan:
|
||||
return "TIDSCAN";
|
||||
break;
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
break;
|
||||
@ -372,10 +379,10 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
|
||||
}
|
||||
else if (IsA(p, IndexScan))
|
||||
{
|
||||
StrNCpy(extraInfo,
|
||||
((RangeTblEntry *) (nth(((IndexScan *) p)->scan.scanrelid - 1,
|
||||
parsetree->rtable)))->relname,
|
||||
NAMEDATALEN);
|
||||
RangeTblEntry *rte;
|
||||
|
||||
rte = rt_fetch(((IndexScan *) p)->scan.scanrelid, parsetree->rtable);
|
||||
StrNCpy(extraInfo, rte->relname, NAMEDATALEN);
|
||||
}
|
||||
else
|
||||
extraInfo[0] = '\0';
|
||||
@ -386,7 +393,7 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
|
||||
print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
|
||||
print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
|
||||
|
||||
if (nodeTag(p) == T_Append)
|
||||
if (IsA(p, Append))
|
||||
{
|
||||
List *lst;
|
||||
int whichplan = 0;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.96 2000/09/12 21:06:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.97 2000/09/29 18:21:29 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||
@ -70,13 +70,16 @@ _readQuery()
|
||||
local_node->commandType = atoi(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :utility */
|
||||
/* we can't get create or index here, can we? */
|
||||
|
||||
token = lsptok(NULL, &length); /* get the notify name if any */
|
||||
token = lsptok(NULL, &length);
|
||||
if (length == 0)
|
||||
local_node->utilityStmt = NULL;
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Hack to make up for lack of readfuncs for utility-stmt nodes
|
||||
*
|
||||
* we can't get create or index here, can we?
|
||||
*/
|
||||
NotifyStmt *n = makeNode(NotifyStmt);
|
||||
|
||||
n->relname = debackslash(token, length);
|
||||
@ -110,11 +113,13 @@ _readQuery()
|
||||
token = lsptok(NULL, &length); /* get unionall */
|
||||
local_node->unionall = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :distinctClause */
|
||||
local_node->distinctClause = nodeRead(true);
|
||||
token = lsptok(NULL, &length); /* skip the :hasAggs */
|
||||
token = lsptok(NULL, &length); /* get hasAggs */
|
||||
local_node->hasAggs = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :sortClause */
|
||||
local_node->sortClause = nodeRead(true);
|
||||
token = lsptok(NULL, &length); /* skip the :hasSubLinks */
|
||||
token = lsptok(NULL, &length); /* get hasSubLinks */
|
||||
local_node->hasSubLinks = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :rtable */
|
||||
local_node->rtable = nodeRead(true);
|
||||
@ -125,8 +130,14 @@ _readQuery()
|
||||
token = lsptok(NULL, &length); /* skip :targetlist */
|
||||
local_node->targetList = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :qual */
|
||||
local_node->qual = nodeRead(true);
|
||||
token = lsptok(NULL, &length); /* skip :rowMarks */
|
||||
local_node->rowMarks = toIntList(nodeRead(true));
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :distinctClause */
|
||||
local_node->distinctClause = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :sortClause */
|
||||
local_node->sortClause = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :groupClause */
|
||||
local_node->groupClause = nodeRead(true);
|
||||
@ -134,20 +145,11 @@ _readQuery()
|
||||
token = lsptok(NULL, &length); /* skip :havingQual */
|
||||
local_node->havingQual = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip the :hasAggs */
|
||||
token = lsptok(NULL, &length); /* get hasAggs */
|
||||
local_node->hasAggs = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* skip the :hasSubLinks */
|
||||
token = lsptok(NULL, &length); /* get hasSubLinks */
|
||||
local_node->hasSubLinks = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :unionClause */
|
||||
local_node->unionClause = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :intersectClause */
|
||||
local_node->intersectClause = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :unionClause */
|
||||
local_node->unionClause = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :limitOffset */
|
||||
local_node->limitOffset = nodeRead(true);
|
||||
@ -155,9 +157,6 @@ _readQuery()
|
||||
token = lsptok(NULL, &length); /* skip :limitCount */
|
||||
local_node->limitCount = nodeRead(true);
|
||||
|
||||
token = lsptok(NULL, &length); /* skip :rowMark */
|
||||
local_node->rowMark = nodeRead(true);
|
||||
|
||||
return local_node;
|
||||
}
|
||||
|
||||
@ -561,6 +560,29 @@ _readTidScan()
|
||||
return local_node;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _readSubqueryScan
|
||||
*
|
||||
* SubqueryScan is a subclass of Scan
|
||||
* ----------------
|
||||
*/
|
||||
static SubqueryScan *
|
||||
_readSubqueryScan()
|
||||
{
|
||||
SubqueryScan *local_node;
|
||||
char *token;
|
||||
int length;
|
||||
|
||||
local_node = makeNode(SubqueryScan);
|
||||
|
||||
_getScan((Scan *) local_node);
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :subplan */
|
||||
local_node->subplan = nodeRead(true); /* now read it */
|
||||
|
||||
return local_node;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _readSort
|
||||
*
|
||||
@ -1181,6 +1203,30 @@ _readRangeTblRef()
|
||||
return local_node;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _readFromExpr
|
||||
*
|
||||
* FromExpr is a subclass of Node
|
||||
* ----------------
|
||||
*/
|
||||
static FromExpr *
|
||||
_readFromExpr()
|
||||
{
|
||||
FromExpr *local_node;
|
||||
char *token;
|
||||
int length;
|
||||
|
||||
local_node = makeNode(FromExpr);
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :fromlist */
|
||||
local_node->fromlist = nodeRead(true); /* now read it */
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :quals */
|
||||
local_node->quals = nodeRead(true); /* now read it */
|
||||
|
||||
return local_node;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _readJoinExpr
|
||||
*
|
||||
@ -1293,22 +1339,6 @@ _readRelOptInfo()
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->width = atoi(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* get :indexed */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
|
||||
if (!strncmp(token, "true", 4))
|
||||
local_node->indexed = true;
|
||||
else
|
||||
local_node->indexed = false;
|
||||
|
||||
token = lsptok(NULL, &length); /* get :pages */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->pages = atol(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* get :tuples */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->tuples = atof(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* get :targetlist */
|
||||
local_node->targetlist = nodeRead(true); /* now read it */
|
||||
|
||||
@ -1325,6 +1355,25 @@ _readRelOptInfo()
|
||||
token = lsptok(NULL, &length); /* get :pruneable */
|
||||
local_node->pruneable = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* get :issubquery */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->issubquery = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* get :indexed */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->indexed = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* get :pages */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->pages = atol(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* get :tuples */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->tuples = atof(token);
|
||||
|
||||
token = lsptok(NULL, &length); /* get :subplan */
|
||||
local_node->subplan = nodeRead(true); /* now read it */
|
||||
|
||||
token = lsptok(NULL, &length); /* get :baserestrictinfo */
|
||||
local_node->baserestrictinfo = nodeRead(true); /* now read it */
|
||||
|
||||
@ -1409,6 +1458,9 @@ _readRangeTblEntry()
|
||||
token = lsptok(NULL, &length); /* get :relid */
|
||||
local_node->relid = strtoul(token, NULL, 10);
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :subquery */
|
||||
local_node->subquery = nodeRead(true); /* now read it */
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :alias */
|
||||
local_node->alias = nodeRead(true); /* now read it */
|
||||
|
||||
@ -1423,27 +1475,17 @@ _readRangeTblEntry()
|
||||
token = lsptok(NULL, &length); /* get :inFromCl */
|
||||
local_node->inFromCl = (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;
|
||||
token = lsptok(NULL, &length); /* eat :checkForRead */
|
||||
token = lsptok(NULL, &length); /* get :checkForRead */
|
||||
local_node->checkForRead = (token[0] == 't') ? true : false;
|
||||
|
||||
return local_node;
|
||||
}
|
||||
token = lsptok(NULL, &length); /* eat :checkForWrite */
|
||||
token = lsptok(NULL, &length); /* get :checkForWrite */
|
||||
local_node->checkForWrite = (token[0] == 't') ? true : false;
|
||||
|
||||
static RowMark *
|
||||
_readRowMark()
|
||||
{
|
||||
RowMark *local_node = makeNode(RowMark);
|
||||
char *token;
|
||||
int length;
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :rti */
|
||||
token = lsptok(NULL, &length); /* get :rti */
|
||||
local_node->rti = strtoul(token, NULL, 10);
|
||||
|
||||
token = lsptok(NULL, &length); /* eat :info */
|
||||
token = lsptok(NULL, &length); /* get :info */
|
||||
local_node->info = strtoul(token, NULL, 10);
|
||||
token = lsptok(NULL, &length); /* eat :checkAsUser */
|
||||
token = lsptok(NULL, &length); /* get :checkAsUser */
|
||||
local_node->checkAsUser = strtoul(token, NULL, 10);
|
||||
|
||||
return local_node;
|
||||
}
|
||||
@ -1768,9 +1810,9 @@ _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); /* get :ispusheddown */
|
||||
token = lsptok(NULL, &length); /* now read it */
|
||||
local_node->isjoinqual = (token[0] == 't') ? true : false;
|
||||
local_node->ispusheddown = (token[0] == 't') ? true : false;
|
||||
|
||||
token = lsptok(NULL, &length); /* get :subclauseindices */
|
||||
local_node->subclauseindices = nodeRead(true); /* now read it */
|
||||
@ -1879,6 +1921,8 @@ parsePlanString(void)
|
||||
return_value = _readIndexScan();
|
||||
else if (length == 7 && strncmp(token, "TIDSCAN", length) == 0)
|
||||
return_value = _readTidScan();
|
||||
else if (length == 12 && strncmp(token, "SUBQUERYSCAN", length) == 0)
|
||||
return_value = _readSubqueryScan();
|
||||
else if (length == 4 && strncmp(token, "SORT", length) == 0)
|
||||
return_value = _readSort();
|
||||
else if (length == 6 && strncmp(token, "AGGREG", length) == 0)
|
||||
@ -1891,6 +1935,8 @@ parsePlanString(void)
|
||||
return_value = _readRelabelType();
|
||||
else if (length == 11 && strncmp(token, "RANGETBLREF", length) == 0)
|
||||
return_value = _readRangeTblRef();
|
||||
else if (length == 8 && strncmp(token, "FROMEXPR", length) == 0)
|
||||
return_value = _readFromExpr();
|
||||
else if (length == 8 && strncmp(token, "JOINEXPR", length) == 0)
|
||||
return_value = _readJoinExpr();
|
||||
else if (length == 3 && strncmp(token, "AGG", length) == 0)
|
||||
@ -1953,10 +1999,8 @@ parsePlanString(void)
|
||||
return_value = _readCaseExpr();
|
||||
else if (length == 4 && strncmp(token, "WHEN", length) == 0)
|
||||
return_value = _readCaseWhen();
|
||||
else if (length == 7 && strncmp(token, "ROWMARK", length) == 0)
|
||||
return_value = _readRowMark();
|
||||
else
|
||||
elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
|
||||
elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
|
||||
|
||||
return (Node *) return_value;
|
||||
}
|
||||
|
@ -9,23 +9,63 @@ stuff. /geqo is the separate "genetic optimization" planner --- it does
|
||||
a semi-random search through the join tree space, rather than exhaustively
|
||||
considering all possible join trees. (But each join considered by /geqo
|
||||
is given to /path to create paths for, so we consider all possible
|
||||
implementation paths for each specific join even in GEQO mode.)
|
||||
implementation paths for each specific join pair even in GEQO mode.)
|
||||
|
||||
|
||||
Paths and Join Pairs
|
||||
--------------------
|
||||
|
||||
During the planning/optimizing process, we build "Path" trees representing
|
||||
the different ways of doing a query. We select the cheapest Path that
|
||||
generates the desired relation and turn it into a Plan to pass to the
|
||||
executor. (There is pretty much a one-to-one correspondence between the
|
||||
Path and Plan trees, but Path nodes omit info that won't be needed during
|
||||
planning, and include info needed for planning that won't be needed by the
|
||||
executor.)
|
||||
|
||||
The optimizer builds a RelOptInfo structure for each base relation used in
|
||||
the query. Base rels are either primitive tables, or subquery subselects
|
||||
that are planned via a separate recursive invocation of the planner. A
|
||||
RelOptInfo is also built for each join relation that is considered during
|
||||
planning. A join rel is simply a combination of base rels. There is only
|
||||
one join RelOptInfo for any given set of baserels --- for example, the join
|
||||
{A B C} is represented by the same RelOptInfo no matter whether we build it
|
||||
by joining A and B first and then adding C, or joining B and C first and
|
||||
then adding A, etc. These different means of building the joinrel are
|
||||
represented as Paths. For each RelOptInfo we build a list of Paths that
|
||||
represent plausible ways to implement the scan or join of that relation.
|
||||
Once we've considered all the plausible Paths for a rel, we select the one
|
||||
that is cheapest according to the planner's cost estimates. The final plan
|
||||
is derived from the cheapest Path for the RelOptInfo that includes all the
|
||||
base rels of the query.
|
||||
|
||||
Possible Paths for a primitive table relation include plain old sequential
|
||||
scan, plus index scans for any indexes that exist on the table. A subquery
|
||||
base relation just has one Path, a "SubqueryScan" path (which links to the
|
||||
subplan that was built by a recursive invocation of the planner).
|
||||
|
||||
Joins always occur using two RelOptInfos. One is outer, the other inner.
|
||||
Outers drive lookups of values in the inner. In a nested loop, lookups of
|
||||
values in the inner occur by scanning the inner path once per outer tuple
|
||||
to find each matching inner row. In a mergejoin, inner and outer rows are
|
||||
ordered, and are accessed in order, so only one scan is required to perform
|
||||
the entire join: both inner and outer paths are scanned in-sync. (There's
|
||||
not a lot of difference between inner and outer in a mergejoin...) In a
|
||||
hashjoin, the inner is scanned first and all its rows are entered in a
|
||||
hashtable, then the outer is scanned and for each row we lookup the join
|
||||
key in the hashtable.
|
||||
|
||||
A Path for a join relation is actually a tree structure, with the top
|
||||
Path node representing the join method. It has left and right subpaths
|
||||
that represent the scan or join methods used for the two input relations.
|
||||
|
||||
|
||||
Join Tree Construction
|
||||
----------------------
|
||||
|
||||
The optimizer generates optimal query plans by doing a more-or-less
|
||||
exhaustive search through the ways of executing the query. During
|
||||
the planning/optimizing process, we build "Path" trees representing
|
||||
the different ways of doing a query. We select the cheapest Path
|
||||
that generates the desired relation and turn it into a Plan to pass
|
||||
to the executor. (There is pretty much a one-to-one correspondence
|
||||
between the Path and Plan trees, but Path nodes omit info that won't
|
||||
be needed during planning, and include info needed for planning that
|
||||
won't be needed by the executor.)
|
||||
|
||||
The best Path tree is found by a recursive process:
|
||||
exhaustive search through the ways of executing the query. The best Path
|
||||
tree is found by a recursive process:
|
||||
|
||||
1) Take each base relation in the query, and make a RelOptInfo structure
|
||||
for it. Find each potentially useful way of accessing the relation,
|
||||
@ -44,46 +84,40 @@ 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
|
||||
single join relation.
|
||||
|
||||
2) Consider joining each RelOptInfo to each other RelOptInfo specified in
|
||||
its RelOptInfo.joininfo, and generate a Path for each possible join method.
|
||||
(If we have a RelOptInfo with no join clauses, we have no choice but to
|
||||
generate a clauseless Cartesian-product join; so we consider joining that
|
||||
rel to each other available rel. But in the presence of join clauses we
|
||||
will only consider joins that use available join clauses.)
|
||||
2) If the query's FROM clause contains explicit JOIN clauses, we join
|
||||
those pairs of relations in exactly the tree structure indicated by the
|
||||
JOIN clauses. (This is absolutely necessary when dealing with outer JOINs.
|
||||
For inner JOINs we have more flexibility in theory, but don't currently
|
||||
exploit it in practice.) For each such join pair, we generate a Path
|
||||
for each feasible join method, and select the cheapest Path. Note that
|
||||
the JOIN clause structure determines the join Path structure, but it
|
||||
doesn't constrain the join implementation method at each join (nestloop,
|
||||
merge, hash), nor does it say which rel is considered outer or inner at
|
||||
each join. We consider all these possibilities in building Paths.
|
||||
|
||||
At this stage each input RelOptInfo is a single relation, so we are joining
|
||||
every relation to the other relations as joined in the WHERE clause. We
|
||||
generate a new "join" RelOptInfo for each possible combination of two
|
||||
"base" RelOptInfos, and put all the plausible paths for that combination
|
||||
into the join RelOptInfo's pathlist. (As before, we keep only the cheapest
|
||||
alternative that generates any one sort ordering of the result.)
|
||||
3) At the top level of the FROM clause we will have a list of relations
|
||||
that are either base rels or joinrels constructed per JOIN directives.
|
||||
We can join these rels together in any order the planner sees fit.
|
||||
The standard (non-GEQO) planner does this as follows:
|
||||
|
||||
Joins always occur using two RelOptInfos. One is outer, the other inner.
|
||||
Outers drive lookups of values in the inner. In a nested loop, lookups of
|
||||
values in the inner occur by scanning the inner path once per outer tuple
|
||||
to find each matching inner row. In a mergejoin, inner and outer rows are
|
||||
ordered, and are accessed in order, so only one scan is required to perform
|
||||
the entire join: both inner and outer paths are scanned in-sync. (There's
|
||||
not a lot of difference between inner and outer in a mergejoin...) In a
|
||||
hashjoin, the inner is scanned first and all its rows are entered in a
|
||||
hashtable, then the outer is scanned and for each row we lookup the join
|
||||
key in the hashtable.
|
||||
Consider joining each RelOptInfo to each other RelOptInfo specified in its
|
||||
RelOptInfo.joininfo, and generate a Path for each possible join method for
|
||||
each such pair. (If we have a RelOptInfo with no join clauses, we have no
|
||||
choice but to generate a clauseless Cartesian-product join; so we consider
|
||||
joining that rel to each other available rel. But in the presence of join
|
||||
clauses we will only consider joins that use available join clauses.)
|
||||
|
||||
A Path for a join relation is actually a tree structure, with the top
|
||||
Path node representing the join method. It has left and right subpaths
|
||||
that represent the scan methods used for the two input relations.
|
||||
|
||||
3) If we only had two base relations, we are done: we just pick the
|
||||
cheapest path for the join RelOptInfo. If we had more than two, we now
|
||||
If we only had two relations in the FROM list, we are done: we just pick
|
||||
the cheapest path for the join RelOptInfo. If we had more than two, we now
|
||||
need to consider ways of joining join RelOptInfos to each other to make
|
||||
join RelOptInfos that represent more than two base relations.
|
||||
join RelOptInfos that represent more than two FROM items.
|
||||
|
||||
The join tree is constructed using a "dynamic programming" algorithm:
|
||||
in the first pass (already described) we consider ways to create join rels
|
||||
representing exactly two base relations. The second pass considers ways
|
||||
to make join rels that represent exactly three base relations; the next pass,
|
||||
four relations, etc. The last pass considers how to make the final join
|
||||
relation that includes all base rels --- obviously there can be only one
|
||||
representing exactly two FROM items. The second pass considers ways
|
||||
to make join rels that represent exactly three FROM items; the next pass,
|
||||
four items, etc. The last pass considers how to make the final join
|
||||
relation that includes all FROM items --- obviously there can be only one
|
||||
join rel at this top level, whereas there can be more than one join rel
|
||||
at lower levels. At each level we use joins that follow available join
|
||||
clauses, if possible, just as described for the first level.
|
||||
@ -114,32 +148,45 @@ For example:
|
||||
{1 2 3 4}
|
||||
|
||||
We consider left-handed plans (the outer rel of an upper join is a joinrel,
|
||||
but the inner is always a base rel); right-handed plans (outer rel is always
|
||||
a base rel); and bushy plans (both inner and outer can be joins themselves).
|
||||
For example, when building {1 2 3 4} we consider joining {1 2 3} to {4}
|
||||
(left-handed), {4} to {1 2 3} (right-handed), and {1 2} to {3 4} (bushy),
|
||||
among other choices. Although the jointree scanning code produces these
|
||||
potential join combinations one at a time, all the ways to produce the
|
||||
same set of joined base rels will share the same RelOptInfo, so the paths
|
||||
produced from different join combinations that produce equivalent joinrels
|
||||
will compete in add_path.
|
||||
but the inner is always a single FROM item); right-handed plans (outer rel
|
||||
is always a single item); and bushy plans (both inner and outer can be
|
||||
joins themselves). For example, when building {1 2 3 4} we consider
|
||||
joining {1 2 3} to {4} (left-handed), {4} to {1 2 3} (right-handed), and
|
||||
{1 2} to {3 4} (bushy), among other choices. Although the jointree
|
||||
scanning code produces these potential join combinations one at a time,
|
||||
all the ways to produce the same set of joined base rels will share the
|
||||
same RelOptInfo, so the paths produced from different join combinations
|
||||
that produce equivalent joinrels will compete in add_path.
|
||||
|
||||
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).
|
||||
|
||||
Pulling up subqueries
|
||||
---------------------
|
||||
|
||||
As we described above, a subquery appearing in the range table is planned
|
||||
independently and treated as a "black box" during planning of the outer
|
||||
query. This is necessary when the subquery uses features such as
|
||||
aggregates, GROUP, or DISTINCT. But if the subquery is just a simple
|
||||
scan or join, treating the subquery as a black box may produce a poor plan
|
||||
compared to considering it as part of the entire plan search space.
|
||||
Therefore, at the start of the planning process the planner looks for
|
||||
simple subqueries and pulls them up into the main query's jointree.
|
||||
|
||||
Pulling up a subquery may result in FROM-list joins appearing below the top
|
||||
of the join tree. Each FROM-list is planned using the dynamic-programming
|
||||
search method described above.
|
||||
|
||||
If pulling up a subquery produces a FROM-list as a direct child of another
|
||||
FROM-list (with no explicit JOIN directives between), then we can merge the
|
||||
two FROM-lists together. Once that's done, the subquery is an absolutely
|
||||
integral part of the outer query and will not constrain the join tree
|
||||
search space at all. However, that could result in unpleasant growth of
|
||||
planning time, since the dynamic-programming search has runtime exponential
|
||||
in the number of FROM-items considered. Therefore, we don't merge
|
||||
FROM-lists if the result would have too many FROM-items in one list.
|
||||
|
||||
|
||||
Optimizer Functions
|
||||
@ -151,6 +198,7 @@ planner()
|
||||
set up for recursive handling of subqueries
|
||||
do final cleanup after planning.
|
||||
-subquery_planner()
|
||||
pull up subqueries from rangetable, if possible
|
||||
simplify constant expressions
|
||||
canonicalize qual
|
||||
Attempt to reduce WHERE clause to either CNF or DNF canonical form.
|
||||
@ -167,13 +215,14 @@ planner()
|
||||
preprocess target list
|
||||
handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT
|
||||
--query_planner()
|
||||
pull out constants from target list
|
||||
get a target list that only contains column names, no expressions
|
||||
if none, then return
|
||||
pull out constant quals, which can be used to gate execution of the
|
||||
whole plan (if any are found, we make a top-level Result node
|
||||
to do the gating)
|
||||
make a simplified target list that only contains Vars, no expressions
|
||||
---subplanner()
|
||||
make list of base relations used in query
|
||||
split up the qual into restrictions (a=1) and joins (b=c)
|
||||
find relation clauses that can do merge sort and hash joins
|
||||
find qual clauses that enable merge and hash joins
|
||||
----make_one_rel()
|
||||
set_base_rel_pathlist()
|
||||
find scan and all index paths for each base relation
|
||||
@ -188,10 +237,11 @@ planner()
|
||||
Back at make_one_rel_by_joins(), apply set_cheapest() to extract the
|
||||
cheapest path for each newly constructed joinrel.
|
||||
Loop back if this wasn't the top join level.
|
||||
do group(GROUP)
|
||||
do aggregate
|
||||
put back constants
|
||||
re-flatten target list
|
||||
Back at query_planner:
|
||||
put back constant quals and non-simplified target list
|
||||
Back at union_planner:
|
||||
do grouping(GROUP)
|
||||
do aggregates
|
||||
make unique(DISTINCT)
|
||||
make sort(ORDER BY)
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: geqo_main.c,v 1.24 2000/09/19 18:42:33 tgl Exp $
|
||||
* $Id: geqo_main.c,v 1.25 2000/09/29 18:21:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -245,9 +245,9 @@ geqo(Query *root, int number_of_rels, List *initial_rels)
|
||||
best_tour = (Gene *) pool->data[0].string;
|
||||
|
||||
/* root->join_rel_list will be modified during this ! */
|
||||
best_rel = (RelOptInfo *) gimme_tree(root, initial_rels,
|
||||
best_tour, pool->string_length,
|
||||
0, NULL);
|
||||
best_rel = gimme_tree(root, initial_rels,
|
||||
best_tour, pool->string_length,
|
||||
0, NULL);
|
||||
|
||||
/* DBG: show the query plan
|
||||
print_plan(best_plan, root);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.64 2000/09/19 18:42:34 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.65 2000/09/29 18:21:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,6 +19,9 @@
|
||||
#include "optimizer/geqo.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
|
||||
bool enable_geqo = true;
|
||||
@ -26,7 +29,6 @@ int geqo_rels = DEFAULT_GEQO_RELS;
|
||||
|
||||
|
||||
static void set_base_rel_pathlist(Query *root);
|
||||
static List *build_jointree_rels(Query *root);
|
||||
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
|
||||
List *initial_rels);
|
||||
|
||||
@ -44,20 +46,7 @@ static void debug_print_rel(Query *root, RelOptInfo *rel);
|
||||
RelOptInfo *
|
||||
make_one_rel(Query *root)
|
||||
{
|
||||
int levels_needed;
|
||||
List *initial_rels;
|
||||
|
||||
/*
|
||||
* 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->jointree);
|
||||
|
||||
if (levels_needed <= 0)
|
||||
return NULL; /* nothing to do? */
|
||||
RelOptInfo *rel;
|
||||
|
||||
/*
|
||||
* Generate access paths for the base rels.
|
||||
@ -65,27 +54,18 @@ make_one_rel(Query *root)
|
||||
set_base_rel_pathlist(root);
|
||||
|
||||
/*
|
||||
* Construct a list of rels corresponding to the toplevel jointree nodes.
|
||||
* This may contain both base rels and rels constructed according to
|
||||
* explicit JOIN directives.
|
||||
* Generate access paths for the entire join tree.
|
||||
*/
|
||||
initial_rels = build_jointree_rels(root);
|
||||
Assert(root->jointree != NULL && IsA(root->jointree, FromExpr));
|
||||
|
||||
if (levels_needed == 1)
|
||||
{
|
||||
/*
|
||||
* Single jointree node, so we're done.
|
||||
*/
|
||||
return (RelOptInfo *) lfirst(initial_rels);
|
||||
}
|
||||
else
|
||||
{
|
||||
rel = make_fromexpr_rel(root, root->jointree);
|
||||
|
||||
/*
|
||||
* Generate join tree.
|
||||
*/
|
||||
return make_one_rel_by_joins(root, levels_needed, initial_rels);
|
||||
}
|
||||
/*
|
||||
* The result should join all the query's rels.
|
||||
*/
|
||||
Assert(length(rel->relids) == length(root->base_rel_list));
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -102,36 +82,67 @@ set_base_rel_pathlist(Query *root)
|
||||
foreach(rellist, root->base_rel_list)
|
||||
{
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(rellist);
|
||||
List *indices = find_relation_indices(root, rel);
|
||||
RangeTblEntry *rte;
|
||||
|
||||
/* Mark rel with estimated output rows, width, etc */
|
||||
set_baserel_size_estimates(root, rel);
|
||||
Assert(length(rel->relids) == 1); /* better be base rel */
|
||||
rte = rt_fetch(lfirsti(rel->relids), root->rtable);
|
||||
|
||||
/*
|
||||
* Generate paths and add them to the rel's pathlist.
|
||||
*
|
||||
* Note: add_path() will discard any paths that are dominated by
|
||||
* another available path, keeping only those paths that are
|
||||
* superior along at least one dimension of cost or sortedness.
|
||||
*/
|
||||
if (rel->issubquery)
|
||||
{
|
||||
/* Subquery --- generate a separate plan for it */
|
||||
|
||||
/* Consider sequential scan */
|
||||
add_path(rel, create_seqscan_path(rel));
|
||||
/*
|
||||
* XXX for now, we just apply any restrict clauses that came
|
||||
* from the outer query as qpquals of the SubqueryScan node.
|
||||
* Later, think about pushing them down into the subquery itself.
|
||||
*/
|
||||
|
||||
/* Consider TID scans */
|
||||
create_tidscan_paths(root, rel);
|
||||
/* Generate the plan for the subquery */
|
||||
rel->subplan = planner(rte->subquery);
|
||||
|
||||
/* Consider index paths for both simple and OR index clauses */
|
||||
create_index_paths(root, rel, indices,
|
||||
rel->baserestrictinfo,
|
||||
rel->joininfo);
|
||||
/* Copy number of output rows from subplan */
|
||||
rel->tuples = rel->subplan->plan_rows;
|
||||
|
||||
/*
|
||||
* Note: create_or_index_paths depends on create_index_paths to
|
||||
* have marked OR restriction clauses with relevant indices; this
|
||||
* is why it doesn't need to be given the list of indices.
|
||||
*/
|
||||
create_or_index_paths(root, rel, rel->baserestrictinfo);
|
||||
/* Mark rel with estimated output rows, width, etc */
|
||||
set_baserel_size_estimates(root, rel);
|
||||
|
||||
/* Generate appropriate path */
|
||||
add_path(rel, create_subqueryscan_path(rel));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Plain relation */
|
||||
List *indices = find_secondary_indexes(rte->relid);
|
||||
|
||||
/* Mark rel with estimated output rows, width, etc */
|
||||
set_baserel_size_estimates(root, rel);
|
||||
|
||||
/*
|
||||
* Generate paths and add them to the rel's pathlist.
|
||||
*
|
||||
* Note: add_path() will discard any paths that are dominated by
|
||||
* another available path, keeping only those paths that are
|
||||
* superior along at least one dimension of cost or sortedness.
|
||||
*/
|
||||
|
||||
/* Consider sequential scan */
|
||||
add_path(rel, create_seqscan_path(rel));
|
||||
|
||||
/* Consider TID scans */
|
||||
create_tidscan_paths(root, rel);
|
||||
|
||||
/* Consider index paths for both simple and OR index clauses */
|
||||
create_index_paths(root, rel, indices,
|
||||
rel->baserestrictinfo,
|
||||
rel->joininfo);
|
||||
|
||||
/*
|
||||
* Note: create_or_index_paths depends on create_index_paths to
|
||||
* have marked OR restriction clauses with relevant indices; this
|
||||
* is why it doesn't need to be given the list of indices.
|
||||
*/
|
||||
create_or_index_paths(root, rel, rel->baserestrictinfo);
|
||||
}
|
||||
|
||||
/* Now find the cheapest of the paths for this rel */
|
||||
set_cheapest(rel);
|
||||
@ -139,26 +150,57 @@ 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.
|
||||
* make_fromexpr_rel
|
||||
* Build access paths for a FromExpr jointree node.
|
||||
*/
|
||||
static List *
|
||||
build_jointree_rels(Query *root)
|
||||
RelOptInfo *
|
||||
make_fromexpr_rel(Query *root, FromExpr *from)
|
||||
{
|
||||
List *rels = NIL;
|
||||
int levels_needed;
|
||||
List *initial_rels = NIL;
|
||||
List *jt;
|
||||
|
||||
foreach(jt, root->jointree)
|
||||
/*
|
||||
* Count the number of child jointree nodes. This is the depth
|
||||
* of the dynamic-programming algorithm we must employ to consider
|
||||
* all ways of joining the child nodes.
|
||||
*/
|
||||
levels_needed = length(from->fromlist);
|
||||
|
||||
if (levels_needed <= 0)
|
||||
return NULL; /* nothing to do? */
|
||||
|
||||
/*
|
||||
* Construct a list of rels corresponding to the child jointree nodes.
|
||||
* This may contain both base rels and rels constructed according to
|
||||
* explicit JOIN directives.
|
||||
*/
|
||||
foreach(jt, from->fromlist)
|
||||
{
|
||||
Node *jtnode = (Node *) lfirst(jt);
|
||||
|
||||
rels = lappend(rels, make_rel_from_jointree(root, jtnode));
|
||||
initial_rels = lappend(initial_rels,
|
||||
make_jointree_rel(root, jtnode));
|
||||
}
|
||||
|
||||
if (levels_needed == 1)
|
||||
{
|
||||
/*
|
||||
* Single jointree node, so we're done.
|
||||
*/
|
||||
return (RelOptInfo *) lfirst(initial_rels);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Consider the different orders in which we could join the rels,
|
||||
* using either GEQO or regular optimizer.
|
||||
*/
|
||||
if (enable_geqo && levels_needed >= geqo_rels)
|
||||
return geqo(root, levels_needed, initial_rels);
|
||||
else
|
||||
return make_one_rel_by_joins(root, levels_needed, initial_rels);
|
||||
}
|
||||
return rels;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -182,14 +224,6 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
|
||||
int lev;
|
||||
RelOptInfo *rel;
|
||||
|
||||
/*******************************************
|
||||
* genetic query optimizer entry point *
|
||||
* <utesch@aut.tu-freiberg.de> *
|
||||
* rest will be skipped in case of GEQO *
|
||||
*******************************************/
|
||||
if (enable_geqo && levels_needed >= geqo_rels)
|
||||
return geqo(root, levels_needed, initial_rels);
|
||||
|
||||
/*
|
||||
* We employ a simple "dynamic programming" algorithm: we first find
|
||||
* all ways to build joins of two jointree items, then all ways to
|
||||
@ -243,12 +277,11 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
|
||||
}
|
||||
|
||||
/*
|
||||
* We should have a single rel at the final level,
|
||||
* representing the join of all the base rels.
|
||||
* We should have a single rel at the final level.
|
||||
*/
|
||||
Assert(length(joinitems[levels_needed]) == 1);
|
||||
|
||||
rel = (RelOptInfo *) lfirst(joinitems[levels_needed]);
|
||||
Assert(length(rel->relids) == length(root->base_rel_list));
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.62 2000/06/18 22:44:06 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.63 2000/09/29 18:21:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -115,6 +115,7 @@ cost_seqscan(Path *path, RelOptInfo *baserel)
|
||||
|
||||
/* Should only be applied to base relations */
|
||||
Assert(length(baserel->relids) == 1);
|
||||
Assert(!baserel->issubquery);
|
||||
|
||||
if (!enable_seqscan)
|
||||
startup_cost += disable_cost;
|
||||
@ -223,6 +224,7 @@ cost_index(Path *path, Query *root,
|
||||
/* Should only be applied to base relations */
|
||||
Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
|
||||
Assert(length(baserel->relids) == 1);
|
||||
Assert(!baserel->issubquery);
|
||||
|
||||
if (!enable_indexscan && !is_injoin)
|
||||
startup_cost += disable_cost;
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.96 2000/09/15 18:45:25 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.97 2000/09/29 18:21:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -464,7 +464,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
|
||||
else
|
||||
{
|
||||
/* we assume the caller passed a valid indexable qual */
|
||||
quals = lcons(orsubclause, NIL);
|
||||
quals = makeList1(orsubclause);
|
||||
}
|
||||
|
||||
return expand_indexqual_conditions(quals);
|
||||
@ -1504,7 +1504,7 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
|
||||
|
||||
indexquals = lappend(indexquals, clause->clause);
|
||||
if (! clause->isjoinqual)
|
||||
if (clause->ispusheddown)
|
||||
alljoinquals = false;
|
||||
}
|
||||
|
||||
@ -1516,8 +1516,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
* therefore, both indexid and indexqual should be single-element
|
||||
* lists.
|
||||
*/
|
||||
pathnode->indexid = lconsi(index->indexoid, NIL);
|
||||
pathnode->indexqual = lcons(indexquals, NIL);
|
||||
pathnode->indexid = makeListi1(index->indexoid);
|
||||
pathnode->indexqual = makeList1(indexquals);
|
||||
|
||||
/* We don't actually care what order the index scans in ... */
|
||||
pathnode->indexscandir = NoMovementScanDirection;
|
||||
@ -1541,7 +1541,7 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
*/
|
||||
pathnode->rows = rel->tuples *
|
||||
restrictlist_selectivity(root,
|
||||
LispUnion(rel->baserestrictinfo,
|
||||
set_union(rel->baserestrictinfo,
|
||||
clausegroup),
|
||||
lfirsti(rel->relids));
|
||||
/* Like costsize.c, force estimate to be at least one row */
|
||||
@ -2034,7 +2034,7 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
con = string_to_const(prefix, datatype);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID);
|
||||
expr = make_opclause(op, leftop, (Var *) con);
|
||||
result = lcons(expr, NIL);
|
||||
result = makeList1(expr);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2049,7 +2049,7 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
con = string_to_const(prefix, datatype);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID);
|
||||
expr = make_opclause(op, leftop, (Var *) con);
|
||||
result = lcons(expr, NIL);
|
||||
result = makeList1(expr);
|
||||
|
||||
/*
|
||||
* If we can create a string larger than the prefix, say "x <
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.56 2000/09/12 21:06:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.57 2000/09/29 18:21:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -643,10 +643,10 @@ hash_inner_and_outer(Query *root,
|
||||
continue; /* not hashjoinable */
|
||||
|
||||
/*
|
||||
* If processing an outer join, only use explicit join clauses for
|
||||
* If processing an outer join, only use its own join clauses for
|
||||
* hashing. For inner joins we need not be so picky.
|
||||
*/
|
||||
if (isouterjoin && !restrictinfo->isjoinqual)
|
||||
if (isouterjoin && restrictinfo->ispusheddown)
|
||||
continue;
|
||||
|
||||
clause = restrictinfo->clause;
|
||||
@ -665,7 +665,7 @@ hash_inner_and_outer(Query *root,
|
||||
continue; /* no good for these input relations */
|
||||
|
||||
/* always a one-element list of hash clauses */
|
||||
hashclauses = lcons(restrictinfo, NIL);
|
||||
hashclauses = makeList1(restrictinfo);
|
||||
|
||||
/* estimate disbursion of inner var for costing purposes */
|
||||
innerdisbursion = estimate_disbursion(root, inner);
|
||||
@ -820,7 +820,7 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
*right;
|
||||
|
||||
/*
|
||||
* If processing an outer join, only use explicit join clauses in the
|
||||
* If processing an outer join, only use its own 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
|
||||
@ -832,7 +832,7 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
*/
|
||||
if (isouterjoin)
|
||||
{
|
||||
if (!restrictinfo->isjoinqual)
|
||||
if (restrictinfo->ispusheddown)
|
||||
continue;
|
||||
switch (jointype)
|
||||
{
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.47 2000/09/12 21:06:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.48 2000/09/29 18:21:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -307,13 +307,13 @@ make_rels_by_clauseless_joins(Query *root,
|
||||
|
||||
|
||||
/*
|
||||
* make_rel_from_jointree
|
||||
* make_jointree_rel
|
||||
* 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)
|
||||
make_jointree_rel(Query *root, Node *jtnode)
|
||||
{
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
@ -321,6 +321,13 @@ make_rel_from_jointree(Query *root, Node *jtnode)
|
||||
|
||||
return get_base_rel(root, varno);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
|
||||
/* Recurse back to multi-way-join planner */
|
||||
return make_fromexpr_rel(root, f);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
@ -329,8 +336,8 @@ make_rel_from_jointree(Query *root, Node *jtnode)
|
||||
*rrel;
|
||||
|
||||
/* Recurse */
|
||||
lrel = make_rel_from_jointree(root, j->larg);
|
||||
rrel = make_rel_from_jointree(root, j->rarg);
|
||||
lrel = make_jointree_rel(root, j->larg);
|
||||
rrel = make_jointree_rel(root, j->rarg);
|
||||
|
||||
/* Make this join rel */
|
||||
rel = make_join_rel(root, lrel, rrel, j->jointype);
|
||||
@ -346,7 +353,7 @@ make_rel_from_jointree(Query *root, Node *jtnode)
|
||||
return rel;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "make_rel_from_jointree: unexpected node type %d",
|
||||
elog(ERROR, "make_jointree_rel: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
@ -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.25 2000/09/12 21:06:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.26 2000/09/29 18:21:32 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -122,7 +122,7 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
|
||||
newset = lcons(item1, lcons(item2, NIL));
|
||||
|
||||
/* Found a set to merge into our new set */
|
||||
newset = LispUnion(newset, curset);
|
||||
newset = set_union(newset, curset);
|
||||
|
||||
/*
|
||||
* Remove old set from equi_key_list. NOTE this does not
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.96 2000/09/12 21:06:53 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.97 2000/09/29 18:21:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -41,6 +41,8 @@ static IndexScan *create_indexscan_node(Query *root, IndexPath *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
static TidScan *create_tidscan_node(TidPath *best_path, List *tlist,
|
||||
List *scan_clauses);
|
||||
static SubqueryScan *create_subqueryscan_node(Path *best_path,
|
||||
List *tlist, List *scan_clauses);
|
||||
static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *outer_node, List *outer_tlist,
|
||||
@ -66,6 +68,8 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
ScanDirection indexscandir);
|
||||
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
List *tideval);
|
||||
static SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
|
||||
Index scanrelid, Plan *subplan);
|
||||
static NestLoop *make_nestloop(List *tlist,
|
||||
List *joinclauses, List *otherclauses,
|
||||
Plan *lefttree, Plan *righttree,
|
||||
@ -110,6 +114,7 @@ create_plan(Query *root, Path *best_path)
|
||||
case T_IndexScan:
|
||||
case T_SeqScan:
|
||||
case T_TidScan:
|
||||
case T_SubqueryScan:
|
||||
plan_node = (Plan *) create_scan_node(root, best_path, tlist);
|
||||
break;
|
||||
case T_HashJoin:
|
||||
@ -164,7 +169,9 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
|
||||
switch (best_path->pathtype)
|
||||
{
|
||||
case T_SeqScan:
|
||||
node = (Scan *) create_seqscan_node(best_path, tlist, scan_clauses);
|
||||
node = (Scan *) create_seqscan_node(best_path,
|
||||
tlist,
|
||||
scan_clauses);
|
||||
break;
|
||||
|
||||
case T_IndexScan:
|
||||
@ -180,6 +187,12 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
|
||||
scan_clauses);
|
||||
break;
|
||||
|
||||
case T_SubqueryScan:
|
||||
node = (Scan *) create_subqueryscan_node(best_path,
|
||||
tlist,
|
||||
scan_clauses);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "create_scan_node: unknown node type: %d",
|
||||
best_path->pathtype);
|
||||
@ -301,6 +314,7 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
|
||||
|
||||
/* there should be exactly one base rel involved... */
|
||||
Assert(length(best_path->parent->relids) == 1);
|
||||
Assert(! best_path->parent->issubquery);
|
||||
|
||||
scan_relid = (Index) lfirsti(best_path->parent->relids);
|
||||
|
||||
@ -342,6 +356,8 @@ create_indexscan_node(Query *root,
|
||||
|
||||
/* there should be exactly one base rel involved... */
|
||||
Assert(length(best_path->path.parent->relids) == 1);
|
||||
Assert(! best_path->path.parent->issubquery);
|
||||
|
||||
baserelid = lfirsti(best_path->path.parent->relids);
|
||||
|
||||
/* check to see if any of the indices are lossy */
|
||||
@ -391,8 +407,7 @@ create_indexscan_node(Query *root,
|
||||
make_ands_explicit(lfirst(orclause)));
|
||||
indxqual_expr = make_orclause(orclauses);
|
||||
|
||||
qpqual = set_difference(scan_clauses,
|
||||
lcons(indxqual_expr, NIL));
|
||||
qpqual = set_difference(scan_clauses, makeList1(indxqual_expr));
|
||||
|
||||
if (lossy)
|
||||
qpqual = lappend(qpqual, copyObject(indxqual_expr));
|
||||
@ -449,6 +464,7 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
|
||||
|
||||
/* there should be exactly one base rel involved... */
|
||||
Assert(length(best_path->path.parent->relids) == 1);
|
||||
Assert(! best_path->path.parent->issubquery);
|
||||
|
||||
scan_relid = (Index) lfirsti(best_path->path.parent->relids);
|
||||
|
||||
@ -465,6 +481,34 @@ create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses)
|
||||
return scan_node;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_subqueryscan_node
|
||||
* Returns a subqueryscan node for the base relation scanned by 'best_path'
|
||||
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
||||
*/
|
||||
static SubqueryScan *
|
||||
create_subqueryscan_node(Path *best_path, List *tlist, List *scan_clauses)
|
||||
{
|
||||
SubqueryScan *scan_node;
|
||||
Index scan_relid;
|
||||
|
||||
/* there should be exactly one base rel involved... */
|
||||
Assert(length(best_path->parent->relids) == 1);
|
||||
/* and it must be a subquery */
|
||||
Assert(best_path->parent->issubquery);
|
||||
|
||||
scan_relid = (Index) lfirsti(best_path->parent->relids);
|
||||
|
||||
scan_node = make_subqueryscan(tlist,
|
||||
scan_clauses,
|
||||
scan_relid,
|
||||
best_path->parent->subplan);
|
||||
|
||||
copy_path_costsize(&scan_node->scan.plan, best_path);
|
||||
|
||||
return scan_node;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* JOIN METHODS
|
||||
@ -1162,6 +1206,28 @@ make_tidscan(List *qptlist,
|
||||
return node;
|
||||
}
|
||||
|
||||
static SubqueryScan *
|
||||
make_subqueryscan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
Plan *subplan)
|
||||
{
|
||||
SubqueryScan *node = makeNode(SubqueryScan);
|
||||
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->subplan = subplan;
|
||||
node->scan.scanstate = (CommonScanState *) NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
static NestLoop *
|
||||
make_nestloop(List *tlist,
|
||||
@ -1405,7 +1471,11 @@ make_agg(List *tlist, List *qual, Plan *lefttree)
|
||||
* mode, so it didn't reduce its row count already.)
|
||||
*/
|
||||
if (IsA(lefttree, Group))
|
||||
{
|
||||
plan->plan_rows *= 0.1;
|
||||
if (plan->plan_rows < 1)
|
||||
plan->plan_rows = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
plan->plan_rows = 1;
|
||||
@ -1447,7 +1517,11 @@ make_group(List *tlist,
|
||||
* --- bogus, but how to do better?
|
||||
*/
|
||||
if (!tuplePerGroup)
|
||||
{
|
||||
plan->plan_rows *= 0.1;
|
||||
if (plan->plan_rows < 1)
|
||||
plan->plan_rows = 1;
|
||||
}
|
||||
|
||||
plan->state = (EState *) NULL;
|
||||
plan->qual = NULL;
|
||||
@ -1489,6 +1563,8 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList)
|
||||
* 10% as many tuples out as in.
|
||||
*/
|
||||
plan->plan_rows *= 0.1;
|
||||
if (plan->plan_rows < 1)
|
||||
plan->plan_rows = 1;
|
||||
|
||||
plan->state = (EState *) NULL;
|
||||
plan->targetlist = tlist;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.50 2000/09/12 21:06:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.51 2000/09/29 18:21:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -35,9 +35,10 @@
|
||||
|
||||
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 distribute_qual_to_rels(Query *root, Node *clause,
|
||||
bool ispusheddown,
|
||||
bool isouterjoin,
|
||||
Relids qualscope);
|
||||
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||
Relids join_relids);
|
||||
static void add_vars_to_targetlist(Query *root, List *vars);
|
||||
@ -93,15 +94,13 @@ add_vars_to_targetlist(Query *root, List *vars)
|
||||
* 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 can be processed. For instance,
|
||||
* select count(*) from foo;
|
||||
* would fail to scan foo if this routine were not called. More subtly,
|
||||
* 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
|
||||
@ -115,35 +114,25 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
|
||||
|
||||
if (jtnode == NULL)
|
||||
return NIL;
|
||||
if (IsA(jtnode, List))
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
/* This call to get_base_rel does the primary work... */
|
||||
RelOptInfo *rel = get_base_rel(root, varno);
|
||||
|
||||
result = makeList1(rel);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) jtnode)
|
||||
foreach(l, f->fromlist)
|
||||
{
|
||||
result = nconc(result,
|
||||
add_missing_rels_to_query(root, lfirst(l)));
|
||||
}
|
||||
}
|
||||
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;
|
||||
@ -167,58 +156,74 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
|
||||
|
||||
|
||||
/*
|
||||
* 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().
|
||||
* distribute_quals_to_rels
|
||||
* Recursively scan the query's join tree for WHERE and JOIN/ON qual
|
||||
* clauses, and add these to the appropriate RestrictInfo and JoinInfo
|
||||
* lists belonging to base RelOptInfos. New base rel entries are created
|
||||
* as needed. Also, base RelOptInfos are marked with outerjoinset
|
||||
* information, to aid in proper positioning of qual clauses that appear
|
||||
* above outer joins.
|
||||
*
|
||||
* 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.
|
||||
* available. However, we cannot push a qual down into the nullable side(s)
|
||||
* of an outer join since the qual might eliminate matching rows and cause a
|
||||
* NULL row to be incorrectly emitted by the join. Therefore, rels appearing
|
||||
* within the nullable side(s) 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
|
||||
* To ease the calculation of these values, distribute_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)
|
||||
distribute_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))
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
|
||||
/* No quals to deal with, just return correct result */
|
||||
result = lconsi(varno, NIL);
|
||||
result = makeListi1(varno);
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
List *qual;
|
||||
|
||||
/*
|
||||
* First, recurse to handle child joins.
|
||||
*
|
||||
* Note: we assume it's impossible to see same RT index from more
|
||||
* than one subtree, so nconc() is OK rather than set_unioni().
|
||||
*/
|
||||
foreach(l, f->fromlist)
|
||||
{
|
||||
result = nconc(result,
|
||||
distribute_quals_to_rels(root, lfirst(l)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Now process the top-level quals. These are always marked as
|
||||
* "pushed down", since they clearly didn't come from a JOIN expr.
|
||||
*/
|
||||
foreach(qual, (List *) f->quals)
|
||||
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
||||
true, false, result);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
Relids leftids,
|
||||
rightids,
|
||||
outerjoinids;
|
||||
rightids;
|
||||
bool isouterjoin;
|
||||
List *qual;
|
||||
|
||||
/*
|
||||
@ -228,15 +233,15 @@ add_join_quals_to_rels(Query *root, Node *jtnode)
|
||||
* 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.
|
||||
* quals and quals above us in the join tree.
|
||||
* 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);
|
||||
leftids = distribute_quals_to_rels(root, j->larg);
|
||||
rightids = distribute_quals_to_rels(root, j->rarg);
|
||||
|
||||
result = nconc(listCopy(leftids), rightids);
|
||||
|
||||
outerjoinids = NIL;
|
||||
isouterjoin = false;
|
||||
switch (j->jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
@ -244,15 +249,15 @@ add_join_quals_to_rels(Query *root, Node *jtnode)
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
mark_baserels_for_outer_join(root, rightids, result);
|
||||
outerjoinids = result;
|
||||
isouterjoin = true;
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
mark_baserels_for_outer_join(root, result, result);
|
||||
outerjoinids = result;
|
||||
isouterjoin = true;
|
||||
break;
|
||||
case JOIN_RIGHT:
|
||||
mark_baserels_for_outer_join(root, leftids, result);
|
||||
outerjoinids = result;
|
||||
isouterjoin = true;
|
||||
break;
|
||||
case JOIN_UNION:
|
||||
/*
|
||||
@ -262,17 +267,18 @@ add_join_quals_to_rels(Query *root, Node *jtnode)
|
||||
elog(ERROR, "UNION JOIN is not implemented yet");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "add_join_quals_to_rels: unsupported join type %d",
|
||||
elog(ERROR,
|
||||
"distribute_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);
|
||||
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
||||
false, isouterjoin, result);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "add_join_quals_to_rels: unexpected node type %d",
|
||||
elog(ERROR, "distribute_quals_to_rels: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
@ -301,25 +307,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids 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 root->base_rel_list.
|
||||
*
|
||||
* 'clauses': the list of clauses in the cnfify'd query qualification.
|
||||
*/
|
||||
void
|
||||
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),
|
||||
false, NIL);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_restrict_and_join_to_rel
|
||||
* distribute_qual_to_rels
|
||||
* Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
|
||||
* (depending on whether the clause is a join) of each base relation
|
||||
* mentioned in the clause. A RestrictInfo node is created and added to
|
||||
@ -327,20 +315,21 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
|
||||
* 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.)
|
||||
* 'clause': the qual clause to be distributed
|
||||
* 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
|
||||
* (this indicates the clause came from a FromExpr, not a JoinExpr)
|
||||
* 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
|
||||
* 'qualscope': list of baserels the qual's syntactic scope covers
|
||||
*
|
||||
* 'qualscope' identifies what level of JOIN the qual came from. For a top
|
||||
* level qual (WHERE qual), qualscope lists all baserel ids and in addition
|
||||
* 'ispusheddown' will be TRUE.
|
||||
*/
|
||||
static void
|
||||
add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
bool isjoinqual,
|
||||
Relids outerjoinrelids)
|
||||
distribute_qual_to_rels(Query *root, Node *clause,
|
||||
bool ispusheddown,
|
||||
bool isouterjoin,
|
||||
Relids qualscope)
|
||||
{
|
||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||
Relids relids;
|
||||
@ -348,7 +337,6 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
bool can_be_equijoin;
|
||||
|
||||
restrictinfo->clause = (Expr *) clause;
|
||||
restrictinfo->isjoinqual = isjoinqual;
|
||||
restrictinfo->subclauseindices = NIL;
|
||||
restrictinfo->mergejoinoperator = InvalidOid;
|
||||
restrictinfo->left_sortop = InvalidOid;
|
||||
@ -361,17 +349,40 @@ 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.
|
||||
* Cross-check: clause should contain no relids not within its scope.
|
||||
* Otherwise the parser messed up.
|
||||
*/
|
||||
if (outerjoinrelids)
|
||||
if (! is_subseti(relids, qualscope))
|
||||
elog(ERROR, "JOIN qualification may not refer to other relations");
|
||||
|
||||
/*
|
||||
* If the clause is variable-free, we force it to be evaluated at its
|
||||
* original syntactic level. Note that this should not happen for
|
||||
* top-level clauses, because query_planner() special-cases them. But
|
||||
* it will happen for variable-free JOIN/ON clauses. We don't have to
|
||||
* be real smart about such a case, we just have to be correct.
|
||||
*/
|
||||
if (relids == NIL)
|
||||
relids = qualscope;
|
||||
|
||||
/*
|
||||
* For an outer-join qual, pretend that the clause references all rels
|
||||
* appearing within its syntactic scope, even if it really doesn't.
|
||||
* This ensures that the clause will be evaluated exactly at the level
|
||||
* of joining corresponding to the outer join.
|
||||
*
|
||||
* For a non-outer-join qual, we can evaluate the qual as soon as
|
||||
* (1) we have all the rels it mentions, and (2) we are at or above any
|
||||
* outer joins that can null any of these rels and are below the syntactic
|
||||
* location of the given qual. To enforce the latter, scan the base rels
|
||||
* listed in relids, and merge their outer-join lists into the clause's
|
||||
* own reference list. At the time we are called, the outerjoinset list
|
||||
* of each baserel will show exactly those outer joins that are below the
|
||||
* qual in the join tree.
|
||||
*/
|
||||
if (isouterjoin)
|
||||
{
|
||||
/* 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;
|
||||
relids = qualscope;
|
||||
can_be_equijoin = false;
|
||||
}
|
||||
else
|
||||
@ -379,15 +390,16 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
Relids newrelids = relids;
|
||||
List *relid;
|
||||
|
||||
/* We rely on LispUnioni to be nondestructive of its input lists... */
|
||||
/* We rely on set_unioni 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)
|
||||
if (rel->outerjoinset &&
|
||||
! is_subseti(rel->outerjoinset, relids))
|
||||
{
|
||||
newrelids = LispUnioni(newrelids, rel->outerjoinset);
|
||||
newrelids = set_unioni(newrelids, rel->outerjoinset);
|
||||
/*
|
||||
* Because application of the qual will be delayed by outer
|
||||
* join, we mustn't assume its vars are equal everywhere.
|
||||
@ -396,8 +408,19 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
}
|
||||
}
|
||||
relids = newrelids;
|
||||
/* Should still be a subset of current scope ... */
|
||||
Assert(is_subseti(relids, qualscope));
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the qual as "pushed down" if it can be applied at a level below
|
||||
* its original syntactic level. This allows us to distinguish original
|
||||
* JOIN/ON quals from higher-level quals pushed down to the same joinrel.
|
||||
* A qual originating from WHERE is always considered "pushed down".
|
||||
*/
|
||||
restrictinfo->ispusheddown = ispusheddown || !sameseti(relids,
|
||||
qualscope);
|
||||
|
||||
if (length(relids) == 1)
|
||||
{
|
||||
|
||||
@ -454,10 +477,9 @@ add_restrict_and_join_to_rel(Query *root, Node *clause,
|
||||
{
|
||||
/*
|
||||
* 'clause' references no rels, and therefore we have no place to
|
||||
* attach it. This means query_planner() screwed up --- it should
|
||||
* treat variable-less clauses separately.
|
||||
* attach it. Shouldn't get here if callers are working properly.
|
||||
*/
|
||||
elog(ERROR, "add_restrict_and_join_to_rel: can't cope with variable-free clause");
|
||||
elog(ERROR, "distribute_qual_to_rels: can't cope with variable-free clause");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -557,7 +579,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
else
|
||||
{
|
||||
JoinInfo *joininfo = find_joininfo_node(rel1,
|
||||
lconsi(irel2, NIL));
|
||||
makeListi1(irel2));
|
||||
|
||||
restrictlist = joininfo->jinfo_restrictinfo;
|
||||
}
|
||||
@ -612,10 +634,20 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
|
||||
InvalidOid, /* opid */
|
||||
BOOLOID); /* operator result type */
|
||||
clause->args = lcons(item1, lcons(item2, NIL));
|
||||
clause->args = makeList2(item1, item2);
|
||||
|
||||
add_restrict_and_join_to_rel(root, (Node *) clause,
|
||||
false, NIL);
|
||||
/*
|
||||
* Note: we mark the qual "pushed down" to ensure that it can never be
|
||||
* taken for an original JOIN/ON clause. We also claim it is an outer-
|
||||
* join clause, which it isn't, but that keeps distribute_qual_to_rels
|
||||
* from examining the outerjoinsets of the relevant rels (which are no
|
||||
* longer of interest, but could keep the qual from being pushed down
|
||||
* to where it should be). It'll also save a useless call to
|
||||
* add_equijoined keys...
|
||||
*/
|
||||
distribute_qual_to_rels(root, (Node *) clause,
|
||||
true, true,
|
||||
pull_varnos((Node *) clause));
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.59 2000/09/12 21:06:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.60 2000/09/29 18:21:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,8 +32,8 @@
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
|
||||
double tuple_fraction);
|
||||
static Plan *subplanner(Query *root, List *flat_tlist,
|
||||
double tuple_fraction);
|
||||
|
||||
|
||||
/*--------------------
|
||||
@ -75,46 +75,36 @@ query_planner(Query *root,
|
||||
List *tlist,
|
||||
double tuple_fraction)
|
||||
{
|
||||
List *normal_qual;
|
||||
List *noncachable_qual;
|
||||
List *constant_qual;
|
||||
List *constant_quals;
|
||||
List *var_only_tlist;
|
||||
Plan *subplan;
|
||||
|
||||
/*
|
||||
* If the query contains no relation references at all, it must be
|
||||
* something like "SELECT 2+2;". Build a trivial "Result" plan.
|
||||
* If the query has an empty join tree, then it's something easy like
|
||||
* "SELECT 2+2;" or "INSERT ... VALUES()". Fall through quickly.
|
||||
*/
|
||||
if (root->rtable == NIL)
|
||||
if (root->jointree->fromlist == NIL)
|
||||
{
|
||||
/* If it's not a select, it should have had a target relation... */
|
||||
if (root->commandType != CMD_SELECT)
|
||||
elog(ERROR, "Empty range table for non-SELECT query");
|
||||
|
||||
root->query_pathkeys = NIL; /* signal unordered result */
|
||||
|
||||
/* Make childless Result node to evaluate given tlist. */
|
||||
return (Plan *) make_result(tlist, root->qual, (Plan *) NULL);
|
||||
return (Plan *) make_result(tlist, root->jointree->quals,
|
||||
(Plan *) NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull out any non-variable qual clauses so these can be put in a
|
||||
* Pull out any non-variable WHERE clauses so these can be put in a
|
||||
* toplevel "Result" node, where they will gate execution of the whole
|
||||
* plan (the Result will not invoke its descendant plan unless the
|
||||
* quals are true). Note that any *really* non-variable quals will
|
||||
* have been optimized away by eval_const_expressions(). What we're
|
||||
* mostly interested in here is quals that depend only on outer-level
|
||||
* vars, although if the qual reduces to "WHERE FALSE" this path will
|
||||
* also be taken. We also need a special case for quals that contain
|
||||
* 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 plan, so that they get evaluated once per potential
|
||||
* output tuple.
|
||||
* also be taken.
|
||||
*/
|
||||
normal_qual = pull_constant_clauses((List *) root->qual,
|
||||
&noncachable_qual,
|
||||
&constant_qual);
|
||||
root->jointree->quals = (Node *)
|
||||
pull_constant_clauses((List *) root->jointree->quals,
|
||||
&constant_quals);
|
||||
|
||||
/*
|
||||
* Create a target list that consists solely of (resdom var) target
|
||||
@ -132,18 +122,12 @@ query_planner(Query *root,
|
||||
/*
|
||||
* Choose the best access path and build a plan for it.
|
||||
*/
|
||||
subplan = subplanner(root, var_only_tlist, normal_qual, tuple_fraction);
|
||||
|
||||
/*
|
||||
* Handle the noncachable quals.
|
||||
*/
|
||||
if (noncachable_qual)
|
||||
subplan->qual = nconc(subplan->qual, noncachable_qual);
|
||||
subplan = subplanner(root, var_only_tlist, tuple_fraction);
|
||||
|
||||
/*
|
||||
* Build a result node to control the plan if we have constant quals.
|
||||
*/
|
||||
if (constant_qual)
|
||||
if (constant_quals)
|
||||
{
|
||||
|
||||
/*
|
||||
@ -151,7 +135,7 @@ query_planner(Query *root,
|
||||
* originally requested tlist.
|
||||
*/
|
||||
subplan = (Plan *) make_result(tlist,
|
||||
(Node *) constant_qual,
|
||||
(Node *) constant_quals,
|
||||
subplan);
|
||||
}
|
||||
else
|
||||
@ -175,7 +159,6 @@ query_planner(Query *root,
|
||||
* for processing a single level of attributes.
|
||||
*
|
||||
* flat_tlist is the flattened target list
|
||||
* qual is the qualification to be satisfied (restrict and join quals only)
|
||||
* tuple_fraction is the fraction of tuples we expect will be retrieved
|
||||
*
|
||||
* See query_planner() comments about the interpretation of tuple_fraction.
|
||||
@ -185,7 +168,6 @@ query_planner(Query *root,
|
||||
static Plan *
|
||||
subplanner(Query *root,
|
||||
List *flat_tlist,
|
||||
List *qual,
|
||||
double tuple_fraction)
|
||||
{
|
||||
List *joined_rels;
|
||||
@ -210,9 +192,8 @@ subplanner(Query *root,
|
||||
root->equi_key_list = NIL;
|
||||
|
||||
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);
|
||||
|
||||
(void) distribute_quals_to_rels(root, (Node *) root->jointree);
|
||||
|
||||
/*
|
||||
* Make sure we have RelOptInfo nodes for all relations to be joined.
|
||||
@ -270,26 +251,7 @@ subplanner(Query *root,
|
||||
final_rel = make_one_rel(root);
|
||||
|
||||
if (!final_rel)
|
||||
{
|
||||
|
||||
/*
|
||||
* 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 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 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 */
|
||||
|
||||
/* Make childless Result node to evaluate given tlist. */
|
||||
resultplan = (Plan *) make_result(flat_tlist, (Node *) qual,
|
||||
(Plan *) NULL);
|
||||
goto plan_built;
|
||||
}
|
||||
elog(ERROR, "subplanner: failed to construct a relation");
|
||||
|
||||
#ifdef NOT_USED /* fix xfunc */
|
||||
|
||||
@ -395,7 +357,10 @@ plan_built:
|
||||
|
||||
/*
|
||||
* Must copy the completed plan tree and its pathkeys out of temporary
|
||||
* context.
|
||||
* context. We also have to copy the rtable in case it contains any
|
||||
* subqueries. (If it does, they'll have been modified during the
|
||||
* recursive invocation of planner.c, and hence will contain substructure
|
||||
* allocated in my temporary context...)
|
||||
*/
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
@ -403,6 +368,8 @@ plan_built:
|
||||
|
||||
root->query_pathkeys = copyObject(root->query_pathkeys);
|
||||
|
||||
root->rtable = copyObject(root->rtable);
|
||||
|
||||
/*
|
||||
* Now we can release the Path storage.
|
||||
*/
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.90 2000/09/25 18:09:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.91 2000/09/29 18:21:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -25,11 +25,24 @@
|
||||
#include "optimizer/subselect.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static void preprocess_join_conditions(Query *parse, Node *jtnode);
|
||||
/* Expression kind codes for preprocess_expression */
|
||||
#define EXPRKIND_TARGET 0
|
||||
#define EXPRKIND_WHERE 1
|
||||
#define EXPRKIND_HAVING 2
|
||||
|
||||
|
||||
static Node *pull_up_subqueries(Query *parse, Node *jtnode);
|
||||
static bool is_simple_subquery(Query *subquery);
|
||||
static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
|
||||
static Node *preprocess_jointree(Query *parse, Node *jtnode);
|
||||
static Node *preprocess_expression(Query *parse, Node *expr, int kind);
|
||||
static void preprocess_qual_conditions(Query *parse, Node *jtnode);
|
||||
static List *make_subplanTargetList(Query *parse, List *tlist,
|
||||
AttrNumber **groupColIdx);
|
||||
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
|
||||
@ -52,8 +65,9 @@ planner(Query *parse)
|
||||
int save_PlannerPlanId;
|
||||
|
||||
/*
|
||||
* The planner can be called recursively (an example is when
|
||||
* eval_const_expressions tries to simplify an SQL function).
|
||||
* The outer planner can be called recursively, for example to process
|
||||
* a subquery in the rangetable. (A less obvious example occurs when
|
||||
* eval_const_expressions tries to simplify an SQL function.)
|
||||
* So, global state variables must be saved and restored.
|
||||
*
|
||||
* (Perhaps these should be moved into the Query structure instead?)
|
||||
@ -72,7 +86,7 @@ planner(Query *parse)
|
||||
/* this should go away sometime soon */
|
||||
transformKeySetQuery(parse);
|
||||
|
||||
/* primary planning entry point (may recurse for subplans) */
|
||||
/* primary planning entry point (may recurse for sublinks) */
|
||||
result_plan = subquery_planner(parse, -1.0 /* default case */ );
|
||||
|
||||
Assert(PlannerQueryLevel == 1);
|
||||
@ -126,6 +140,18 @@ planner(Query *parse)
|
||||
Plan *
|
||||
subquery_planner(Query *parse, double tuple_fraction)
|
||||
{
|
||||
/*
|
||||
* Check to see if any subqueries in the rangetable can be merged into
|
||||
* this query.
|
||||
*/
|
||||
parse->jointree = (FromExpr *)
|
||||
pull_up_subqueries(parse, (Node *) parse->jointree);
|
||||
/*
|
||||
* If so, we may have created opportunities to simplify the jointree.
|
||||
*/
|
||||
parse->jointree = (FromExpr *)
|
||||
preprocess_jointree(parse, (Node *) parse->jointree);
|
||||
|
||||
/*
|
||||
* A HAVING clause without aggregates is equivalent to a WHERE clause
|
||||
* (except it can only refer to grouped fields). If there are no aggs
|
||||
@ -135,89 +161,26 @@ subquery_planner(Query *parse, double tuple_fraction)
|
||||
*/
|
||||
if (parse->havingQual != NULL && !parse->hasAggs)
|
||||
{
|
||||
if (parse->qual == NULL)
|
||||
parse->qual = parse->havingQual;
|
||||
else
|
||||
parse->qual = (Node *) make_andclause(lappend(lcons(parse->qual,
|
||||
NIL),
|
||||
parse->havingQual));
|
||||
parse->jointree->quals = make_and_qual(parse->jointree->quals,
|
||||
parse->havingQual);
|
||||
parse->havingQual = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simplify constant expressions in targetlist and quals.
|
||||
*
|
||||
* Note that at this point the qual has not yet been converted to
|
||||
* implicit-AND form, so we can apply eval_const_expressions directly.
|
||||
* Also note that we need to do this before SS_process_sublinks,
|
||||
* because that routine inserts bogus "Const" nodes.
|
||||
* Do preprocessing on targetlist and quals.
|
||||
*/
|
||||
parse->targetList = (List *)
|
||||
eval_const_expressions((Node *) parse->targetList);
|
||||
parse->qual = eval_const_expressions(parse->qual);
|
||||
parse->havingQual = eval_const_expressions(parse->havingQual);
|
||||
preprocess_expression(parse, (Node *) parse->targetList,
|
||||
EXPRKIND_TARGET);
|
||||
|
||||
preprocess_qual_conditions(parse, (Node *) parse->jointree);
|
||||
|
||||
parse->havingQual = preprocess_expression(parse, parse->havingQual,
|
||||
EXPRKIND_HAVING);
|
||||
|
||||
/*
|
||||
* Canonicalize the qual, and convert it to implicit-AND format.
|
||||
*
|
||||
* XXX Is there any value in re-applying eval_const_expressions after
|
||||
* canonicalize_qual?
|
||||
* Do the main planning (potentially recursive)
|
||||
*/
|
||||
parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true);
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
printf("After canonicalize_qual()\n");
|
||||
pprint(parse->qual);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Ditto for the havingQual
|
||||
*/
|
||||
parse->havingQual = (Node *) canonicalize_qual((Expr *) parse->havingQual,
|
||||
true);
|
||||
|
||||
/* Expand SubLinks to SubPlans */
|
||||
if (parse->hasSubLinks)
|
||||
{
|
||||
parse->targetList = (List *)
|
||||
SS_process_sublinks((Node *) parse->targetList);
|
||||
parse->qual = SS_process_sublinks(parse->qual);
|
||||
parse->havingQual = SS_process_sublinks(parse->havingQual);
|
||||
|
||||
if (parse->groupClause != NIL || parse->hasAggs)
|
||||
{
|
||||
|
||||
/*
|
||||
* Check for ungrouped variables passed to subplans. Note we
|
||||
* do NOT do this for subplans in WHERE; it's legal there
|
||||
* because WHERE is evaluated pre-GROUP.
|
||||
*
|
||||
* An interesting fine point: if we reassigned a HAVING qual into
|
||||
* WHERE above, then we will accept references to ungrouped
|
||||
* vars from subplans in the HAVING qual. This is not
|
||||
* entirely consistent, but it doesn't seem particularly
|
||||
* harmful...
|
||||
*/
|
||||
check_subplans_for_ungrouped_vars((Node *) parse->targetList,
|
||||
parse);
|
||||
check_subplans_for_ungrouped_vars(parse->havingQual, parse);
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace uplevel vars with Param nodes */
|
||||
if (PlannerQueryLevel > 1)
|
||||
{
|
||||
parse->targetList = (List *)
|
||||
SS_replace_correlation_vars((Node *) parse->targetList);
|
||||
parse->qual = SS_replace_correlation_vars(parse->qual);
|
||||
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);
|
||||
|
||||
/*
|
||||
@ -230,55 +193,381 @@ 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.
|
||||
* pull_up_subqueries
|
||||
* Look for subqueries in the rangetable that can be pulled up into
|
||||
* the parent query. If the subquery has no special features like
|
||||
* grouping/aggregation then we can merge it into the parent's jointree.
|
||||
*
|
||||
* A tricky aspect of this code is that if we pull up a subquery we have
|
||||
* to replace Vars that reference the subquery's outputs throughout the
|
||||
* parent query, including quals attached to jointree nodes above the one
|
||||
* we are currently processing! We handle this by being careful not to
|
||||
* change the jointree structure while recursing: no nodes other than
|
||||
* subquery RangeTblRef entries will be replaced. Also, we can't turn
|
||||
* ResolveNew loose on the whole jointree, because it'll return a mutated
|
||||
* copy of the tree; we have to invoke it just on the quals, instead.
|
||||
*/
|
||||
static void
|
||||
preprocess_join_conditions(Query *parse, Node *jtnode)
|
||||
static Node *
|
||||
pull_up_subqueries(Query *parse, Node *jtnode)
|
||||
{
|
||||
if (jtnode == NULL)
|
||||
return;
|
||||
if (IsA(jtnode, List))
|
||||
return NULL;
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
|
||||
Query *subquery = rte->subquery;
|
||||
|
||||
/*
|
||||
* Is this a subquery RTE, and if so, is the subquery simple enough
|
||||
* to pull up? (If not, do nothing at this node.)
|
||||
*/
|
||||
if (subquery && is_simple_subquery(subquery))
|
||||
{
|
||||
int rtoffset;
|
||||
Node *subjointree;
|
||||
List *subtlist;
|
||||
|
||||
/*
|
||||
* First, recursively pull up the subquery's subqueries,
|
||||
* so that this routine's processing is complete for its
|
||||
* jointree and rangetable.
|
||||
*/
|
||||
subquery->jointree = (FromExpr *)
|
||||
pull_up_subqueries(subquery, (Node *) subquery->jointree);
|
||||
/*
|
||||
* Append the subquery's rangetable to mine (currently,
|
||||
* no adjustments will be needed in the subquery's rtable).
|
||||
*/
|
||||
rtoffset = length(parse->rtable);
|
||||
parse->rtable = nconc(parse->rtable, subquery->rtable);
|
||||
/*
|
||||
* Make copies of the subquery's jointree and targetlist
|
||||
* with varnos adjusted to match the merged rangetable.
|
||||
*/
|
||||
subjointree = copyObject(subquery->jointree);
|
||||
OffsetVarNodes(subjointree, rtoffset, 0);
|
||||
subtlist = copyObject(subquery->targetList);
|
||||
OffsetVarNodes((Node *) subtlist, rtoffset, 0);
|
||||
/*
|
||||
* Replace all of the top query's references to the subquery's
|
||||
* outputs with copies of the adjusted subtlist items, being
|
||||
* careful not to replace any of the jointree structure.
|
||||
*/
|
||||
parse->targetList = (List *)
|
||||
ResolveNew((Node *) parse->targetList,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
|
||||
parse->havingQual =
|
||||
ResolveNew(parse->havingQual,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
/*
|
||||
* Miscellaneous housekeeping.
|
||||
*/
|
||||
parse->hasSubLinks |= subquery->hasSubLinks;
|
||||
/*
|
||||
* Return the adjusted subquery jointree to replace the
|
||||
* RangeTblRef entry in my jointree.
|
||||
*/
|
||||
return subjointree;
|
||||
}
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) jtnode)
|
||||
preprocess_join_conditions(parse, lfirst(l));
|
||||
}
|
||||
else if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
/* nothing to do here */
|
||||
foreach(l, f->fromlist)
|
||||
{
|
||||
lfirst(l) = pull_up_subqueries(parse, lfirst(l));
|
||||
}
|
||||
}
|
||||
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);
|
||||
j->larg = pull_up_subqueries(parse, j->larg);
|
||||
j->rarg = pull_up_subqueries(parse, j->rarg);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "preprocess_join_conditions: unexpected node type %d",
|
||||
elog(ERROR, "pull_up_subqueries: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return jtnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_simple_subquery
|
||||
* Check a subquery in the range table to see if it's simple enough
|
||||
* to pull up into the parent query.
|
||||
*/
|
||||
static bool
|
||||
is_simple_subquery(Query *subquery)
|
||||
{
|
||||
/*
|
||||
* Let's just make sure it's a valid subselect ...
|
||||
*/
|
||||
if (!IsA(subquery, Query) ||
|
||||
subquery->commandType != CMD_SELECT ||
|
||||
subquery->resultRelation != 0 ||
|
||||
subquery->into != NULL ||
|
||||
subquery->isPortal)
|
||||
elog(ERROR, "is_simple_subquery: subquery is bogus");
|
||||
/*
|
||||
* Also check for currently-unsupported features.
|
||||
*/
|
||||
if (subquery->rowMarks)
|
||||
elog(ERROR, "FOR UPDATE is not supported in subselects");
|
||||
if (subquery->limitOffset || subquery->limitCount)
|
||||
elog(ERROR, "LIMIT is not supported in subselects");
|
||||
/*
|
||||
* Can't currently pull up a union query. Maybe after querytree redesign.
|
||||
*/
|
||||
if (subquery->unionClause)
|
||||
return false;
|
||||
/*
|
||||
* Can't pull up a subquery involving grouping, aggregation, or sorting.
|
||||
*/
|
||||
if (subquery->hasAggs ||
|
||||
subquery->groupClause ||
|
||||
subquery->havingQual ||
|
||||
subquery->sortClause ||
|
||||
subquery->distinctClause)
|
||||
return false;
|
||||
/*
|
||||
* Hack: don't try to pull up a subquery with an empty jointree.
|
||||
* query_planner() will correctly generate a Result plan for a
|
||||
* jointree that's totally empty, but I don't think the right things
|
||||
* happen if an empty FromExpr appears lower down in a jointree.
|
||||
* Not worth working hard on this, just to collapse SubqueryScan/Result
|
||||
* into Result...
|
||||
*/
|
||||
if (subquery->jointree->fromlist == NIL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routine for pull_up_subqueries: do ResolveNew on every expression
|
||||
* in the jointree, without changing the jointree structure itself. Ugly,
|
||||
* but there's no other way...
|
||||
*/
|
||||
static void
|
||||
resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
|
||||
{
|
||||
if (jtnode == NULL)
|
||||
return;
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
resolvenew_in_jointree(lfirst(l), varno, subtlist);
|
||||
f->quals = ResolveNew(f->quals,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
resolvenew_in_jointree(j->larg, varno, subtlist);
|
||||
resolvenew_in_jointree(j->rarg, varno, subtlist);
|
||||
j->quals = ResolveNew(j->quals,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
/* We don't bother to update the colvars list, since it won't be
|
||||
* used again ...
|
||||
*/
|
||||
}
|
||||
else
|
||||
elog(ERROR, "resolvenew_in_jointree: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
}
|
||||
|
||||
/*
|
||||
* preprocess_jointree
|
||||
* Attempt to simplify a query's jointree.
|
||||
*
|
||||
* If we succeed in pulling up a subquery then we might form a jointree
|
||||
* in which a FromExpr is a direct child of another FromExpr. In that
|
||||
* case we can consider collapsing the two FromExprs into one. This is
|
||||
* an optional conversion, since the planner will work correctly either
|
||||
* way. But we may find a better plan (at the cost of more planning time)
|
||||
* if we merge the two nodes.
|
||||
*
|
||||
* NOTE: don't try to do this in the same jointree scan that does subquery
|
||||
* pullup! Since we're changing the jointree structure here, that wouldn't
|
||||
* work reliably --- see comments for pull_up_subqueries().
|
||||
*/
|
||||
static Node *
|
||||
preprocess_jointree(Query *parse, Node *jtnode)
|
||||
{
|
||||
if (jtnode == NULL)
|
||||
return NULL;
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
/* nothing to do here... */
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *newlist = NIL;
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
{
|
||||
Node *child = (Node *) lfirst(l);
|
||||
|
||||
/* Recursively simplify the child... */
|
||||
child = preprocess_jointree(parse, child);
|
||||
/* Now, is it a FromExpr? */
|
||||
if (child && IsA(child, FromExpr))
|
||||
{
|
||||
/*
|
||||
* Yes, so do we want to merge it into parent? Always do so
|
||||
* if child has just one element (since that doesn't make the
|
||||
* parent's list any longer). Otherwise we have to be careful
|
||||
* about the increase in planning time caused by combining the
|
||||
* two join search spaces into one. Our heuristic is to merge
|
||||
* if the merge will produce a join list no longer than
|
||||
* GEQO_RELS/2. (Perhaps need an additional user parameter?)
|
||||
*/
|
||||
FromExpr *subf = (FromExpr *) child;
|
||||
int childlen = length(subf->fromlist);
|
||||
int myothers = length(newlist) + length(lnext(l));
|
||||
|
||||
if (childlen <= 1 || (childlen+myothers) <= geqo_rels/2)
|
||||
{
|
||||
newlist = nconc(newlist, subf->fromlist);
|
||||
f->quals = make_and_qual(f->quals, subf->quals);
|
||||
}
|
||||
else
|
||||
newlist = lappend(newlist, child);
|
||||
}
|
||||
else
|
||||
newlist = lappend(newlist, child);
|
||||
}
|
||||
f->fromlist = newlist;
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
/* Can't usefully change the JoinExpr, but recurse on children */
|
||||
j->larg = preprocess_jointree(parse, j->larg);
|
||||
j->rarg = preprocess_jointree(parse, j->rarg);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "preprocess_jointree: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return jtnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* preprocess_expression
|
||||
* Do subquery_planner's preprocessing work for an expression,
|
||||
* which can be a targetlist, a WHERE clause (including JOIN/ON
|
||||
* conditions), or a HAVING clause.
|
||||
*/
|
||||
static Node *
|
||||
preprocess_expression(Query *parse, Node *expr, int kind)
|
||||
{
|
||||
/*
|
||||
* Simplify constant expressions.
|
||||
*
|
||||
* Note that at this point quals have not yet been converted to
|
||||
* implicit-AND form, so we can apply eval_const_expressions directly.
|
||||
* Also note that we need to do this before SS_process_sublinks,
|
||||
* because that routine inserts bogus "Const" nodes.
|
||||
*/
|
||||
expr = eval_const_expressions(expr);
|
||||
|
||||
/*
|
||||
* If it's a qual or havingQual, canonicalize it, and convert it
|
||||
* to implicit-AND format.
|
||||
*
|
||||
* XXX Is there any value in re-applying eval_const_expressions after
|
||||
* canonicalize_qual?
|
||||
*/
|
||||
if (kind != EXPRKIND_TARGET)
|
||||
{
|
||||
expr = (Node *) canonicalize_qual((Expr *) expr, true);
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
printf("After canonicalize_qual()\n");
|
||||
pprint(expr);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (parse->hasSubLinks)
|
||||
{
|
||||
/* Expand SubLinks to SubPlans */
|
||||
expr = SS_process_sublinks(expr);
|
||||
|
||||
if (kind != EXPRKIND_WHERE &&
|
||||
(parse->groupClause != NIL || parse->hasAggs))
|
||||
{
|
||||
/*
|
||||
* Check for ungrouped variables passed to subplans. Note we
|
||||
* do NOT do this for subplans in WHERE (or JOIN/ON); it's legal
|
||||
* there because WHERE is evaluated pre-GROUP.
|
||||
*
|
||||
* An interesting fine point: if subquery_planner reassigned a
|
||||
* HAVING qual into WHERE, then we will accept references to
|
||||
* ungrouped vars from subplans in the HAVING qual. This is not
|
||||
* entirely consistent, but it doesn't seem particularly
|
||||
* harmful...
|
||||
*/
|
||||
check_subplans_for_ungrouped_vars(expr, parse);
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace uplevel vars with Param nodes */
|
||||
if (PlannerQueryLevel > 1)
|
||||
expr = SS_replace_correlation_vars(expr);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* preprocess_qual_conditions
|
||||
* Recursively scan the query's jointree and do subquery_planner's
|
||||
* preprocessing work on each qual condition found therein.
|
||||
*/
|
||||
static void
|
||||
preprocess_qual_conditions(Query *parse, Node *jtnode)
|
||||
{
|
||||
if (jtnode == NULL)
|
||||
return;
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
/* nothing to do here */
|
||||
}
|
||||
else if (IsA(jtnode, FromExpr))
|
||||
{
|
||||
FromExpr *f = (FromExpr *) jtnode;
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
preprocess_qual_conditions(parse, lfirst(l));
|
||||
|
||||
f->quals = preprocess_expression(parse, f->quals, EXPRKIND_WHERE);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
preprocess_qual_conditions(parse, j->larg);
|
||||
preprocess_qual_conditions(parse, j->rarg);
|
||||
|
||||
j->quals = preprocess_expression(parse, j->quals, EXPRKIND_WHERE);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "preprocess_qual_conditions: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
}
|
||||
|
||||
@ -309,7 +598,6 @@ union_planner(Query *parse,
|
||||
double tuple_fraction)
|
||||
{
|
||||
List *tlist = parse->targetList;
|
||||
List *rangetable = parse->rtable;
|
||||
Plan *result_plan = (Plan *) NULL;
|
||||
AttrNumber *groupColIdx = NULL;
|
||||
List *current_pathkeys = NIL;
|
||||
@ -342,7 +630,7 @@ union_planner(Query *parse,
|
||||
sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
|
||||
tlist);
|
||||
}
|
||||
else if (find_inheritable_rt_entry(rangetable,
|
||||
else if (find_inheritable_rt_entry(parse->rtable,
|
||||
&rt_index, &inheritors))
|
||||
{
|
||||
List *sub_tlist;
|
||||
@ -373,7 +661,7 @@ union_planner(Query *parse,
|
||||
parse->resultRelation,
|
||||
parse->rtable);
|
||||
|
||||
if (parse->rowMark != NULL)
|
||||
if (parse->rowMarks)
|
||||
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
|
||||
|
||||
/*
|
||||
@ -401,33 +689,35 @@ union_planner(Query *parse,
|
||||
parse->rtable);
|
||||
|
||||
/*
|
||||
* Add row-mark targets for UPDATE (should this be done in
|
||||
* preprocess_targetlist?)
|
||||
* Add TID targets for rels selected FOR UPDATE (should this be
|
||||
* done in preprocess_targetlist?). The executor uses the TID
|
||||
* to know which rows to lock, much as for UPDATE or DELETE.
|
||||
*/
|
||||
if (parse->rowMark != NULL)
|
||||
if (parse->rowMarks)
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, parse->rowMark)
|
||||
foreach(l, parse->rowMarks)
|
||||
{
|
||||
RowMark *rowmark = (RowMark *) lfirst(l);
|
||||
TargetEntry *ctid;
|
||||
Index rti = lfirsti(l);
|
||||
char *resname;
|
||||
Resdom *resdom;
|
||||
Var *var;
|
||||
char *resname;
|
||||
|
||||
if (!(rowmark->info & ROW_MARK_FOR_UPDATE))
|
||||
continue;
|
||||
TargetEntry *ctid;
|
||||
|
||||
resname = (char *) palloc(32);
|
||||
sprintf(resname, "ctid%u", rowmark->rti);
|
||||
sprintf(resname, "ctid%u", rti);
|
||||
resdom = makeResdom(length(tlist) + 1,
|
||||
TIDOID,
|
||||
-1,
|
||||
resname,
|
||||
true);
|
||||
|
||||
var = makeVar(rowmark->rti, -1, TIDOID, -1, 0);
|
||||
var = makeVar(rti,
|
||||
SelfItemPointerAttributeNumber,
|
||||
TIDOID,
|
||||
-1,
|
||||
0);
|
||||
|
||||
ctid = makeTargetEntry(resdom, (Node *) var);
|
||||
tlist = lappend(tlist, ctid);
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.65 2000/09/12 21:06:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.66 2000/09/29 18:21:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -102,6 +102,11 @@ set_plan_references(Plan *plan)
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
break;
|
||||
case T_SubqueryScan:
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
fix_expr_references(plan, (Node *) plan->qual);
|
||||
/* No need to recurse into the subplan, it's fixed already */
|
||||
break;
|
||||
case T_NestLoop:
|
||||
set_join_references((Join *) plan);
|
||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||
|
@ -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.41 2000/09/12 21:06:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.42 2000/09/29 18:21:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -453,19 +453,6 @@ make_subplan(SubLink *slink)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* this oughta be merged with LispUnioni */
|
||||
|
||||
static List *
|
||||
set_unioni(List *l1, List *l2)
|
||||
{
|
||||
if (l1 == NULL)
|
||||
return l2;
|
||||
if (l2 == NULL)
|
||||
return l1;
|
||||
|
||||
return nconc(l1, set_differencei(l2, l1));
|
||||
}
|
||||
|
||||
/*
|
||||
* finalize_primnode: build lists of subplans and params appearing
|
||||
* in the given expression tree. NOTE: items are added to lists passed in,
|
||||
@ -680,6 +667,7 @@ SS_finalize_plan(Plan *plan)
|
||||
|
||||
case T_Agg:
|
||||
case T_SeqScan:
|
||||
case T_SubqueryScan:
|
||||
case T_Material:
|
||||
case T_Sort:
|
||||
case T_Unique:
|
||||
|
@ -85,19 +85,14 @@ transformKeySetQuery(Query *origNode)
|
||||
/*************************/
|
||||
/* Qualify where clause */
|
||||
/*************************/
|
||||
if (!inspectOrNode((Expr *) origNode->qual) || TotalExpr < 9)
|
||||
if (!inspectOrNode((Expr *) origNode->jointree->quals) || TotalExpr < 9)
|
||||
return;
|
||||
|
||||
/* Copy essential elements into a union node */
|
||||
while (((Expr *) origNode->qual)->opType == OR_EXPR)
|
||||
while (((Expr *) origNode->jointree->quals)->opType == OR_EXPR)
|
||||
{
|
||||
Query *unionNode = makeNode(Query);
|
||||
|
||||
/* Pull up Expr = */
|
||||
unionNode->qual = lsecond(((Expr *) origNode->qual)->args);
|
||||
|
||||
/* Pull up balance of tree */
|
||||
origNode->qual = lfirst(((Expr *) origNode->qual)->args);
|
||||
List *qualargs = ((Expr *) origNode->jointree->quals)->args;
|
||||
|
||||
unionNode->commandType = origNode->commandType;
|
||||
unionNode->resultRelation = origNode->resultRelation;
|
||||
@ -107,9 +102,16 @@ transformKeySetQuery(Query *origNode)
|
||||
Node_Copy(origNode, unionNode, distinctClause);
|
||||
Node_Copy(origNode, unionNode, sortClause);
|
||||
Node_Copy(origNode, unionNode, rtable);
|
||||
origNode->jointree->quals = NULL; /* avoid unnecessary copying */
|
||||
Node_Copy(origNode, unionNode, jointree);
|
||||
Node_Copy(origNode, unionNode, targetList);
|
||||
|
||||
/* Pull up Expr = */
|
||||
unionNode->jointree->quals = lsecond(qualargs);
|
||||
|
||||
/* Pull up balance of tree */
|
||||
origNode->jointree->quals = lfirst(qualargs);
|
||||
|
||||
origNode->unionClause = lappend(origNode->unionClause, unionNode);
|
||||
}
|
||||
return;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.52 2000/09/12 21:06:57 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.53 2000/09/29 18:21:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -411,7 +411,7 @@ find_all_inheritors(Oid parentrel)
|
||||
* there can't be any cycles in the inheritance graph anyway.)
|
||||
*/
|
||||
currentchildren = set_differencei(currentchildren, examined_relids);
|
||||
unexamined_relids = LispUnioni(unexamined_relids, currentchildren);
|
||||
unexamined_relids = set_unioni(unexamined_relids, currentchildren);
|
||||
}
|
||||
|
||||
return examined_relids;
|
||||
|
@ -4,7 +4,7 @@
|
||||
# Makefile for optimizer/util
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/util/Makefile,v 1.13 2000/08/31 16:10:14 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/util/Makefile,v 1.14 2000/09/29 18:21:23 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -12,7 +12,7 @@ subdir = src/backend/optimizer/util
|
||||
top_builddir = ../../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = restrictinfo.o clauses.o indexnode.o plancat.o \
|
||||
OBJS = restrictinfo.o clauses.o plancat.o \
|
||||
joininfo.o pathnode.o relnode.o tlist.o var.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.75 2000/09/25 18:14:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.76 2000/09/29 18:21:23 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -119,9 +119,9 @@ make_opclause(Oper *op, Var *leftop, Var *rightop)
|
||||
expr->opType = OP_EXPR;
|
||||
expr->oper = (Node *) op;
|
||||
if (rightop)
|
||||
expr->args = lcons(leftop, lcons(rightop, NIL));
|
||||
expr->args = makeList2(leftop, rightop);
|
||||
else
|
||||
expr->args = lcons(leftop, NIL);
|
||||
expr->args = makeList1(leftop);
|
||||
return expr;
|
||||
}
|
||||
|
||||
@ -264,7 +264,7 @@ make_notclause(Expr *notclause)
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = NOT_EXPR;
|
||||
expr->oper = NULL;
|
||||
expr->args = lcons(notclause, NIL);
|
||||
expr->args = makeList1(notclause);
|
||||
return expr;
|
||||
}
|
||||
|
||||
@ -303,7 +303,6 @@ and_clause(Node *clause)
|
||||
* make_andclause
|
||||
*
|
||||
* Create an 'and' clause given its arguments in a list.
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
make_andclause(List *andclauses)
|
||||
@ -317,6 +316,23 @@ make_andclause(List *andclauses)
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* make_and_qual
|
||||
*
|
||||
* Variant of make_andclause for ANDing two qual conditions together.
|
||||
* Qual conditions have the property that a NULL nodetree is interpreted
|
||||
* as 'true'.
|
||||
*/
|
||||
Node *
|
||||
make_and_qual(Node *qual1, Node *qual2)
|
||||
{
|
||||
if (qual1 == NULL)
|
||||
return qual2;
|
||||
if (qual2 == NULL)
|
||||
return qual1;
|
||||
return (Node *) make_andclause(makeList2(qual1, qual2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes (such as in the result of canonicalize_qual or the input of
|
||||
* ExecQual), we use lists of expression nodes with implicit AND semantics.
|
||||
@ -356,7 +372,7 @@ make_ands_implicit(Expr *clause)
|
||||
DatumGetBool(((Const *) clause)->constvalue))
|
||||
return NIL; /* constant TRUE input -> NIL list */
|
||||
else
|
||||
return lcons(clause, NIL);
|
||||
return makeList1(clause);
|
||||
}
|
||||
|
||||
|
||||
@ -676,49 +692,32 @@ is_pseudo_constant_clause(Node *clause)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*----------
|
||||
/*
|
||||
* pull_constant_clauses
|
||||
* Scan through a list of qualifications and separate "constant" quals
|
||||
* from those that are not.
|
||||
*
|
||||
* The input qual list is divided into three parts:
|
||||
* * The function's return value is a list of all those quals that contain
|
||||
* variable(s) of the current query level. (These quals will become
|
||||
* restrict and join quals.)
|
||||
* * *noncachableQual receives a list of quals that have no Vars, yet
|
||||
* cannot be treated as constants because they contain noncachable
|
||||
* function calls. (Example: WHERE random() < 0.5)
|
||||
* * *constantQual receives a list of the remaining quals, which can be
|
||||
* treated as constants for any one scan of the current query level.
|
||||
* (They are really only pseudo-constant, since they may contain
|
||||
* Params or outer-level Vars.)
|
||||
*----------
|
||||
* Returns a list of the pseudo-constant clauses in constantQual and the
|
||||
* remaining quals as the return value.
|
||||
*/
|
||||
List *
|
||||
pull_constant_clauses(List *quals,
|
||||
List **noncachableQual,
|
||||
List **constantQual)
|
||||
pull_constant_clauses(List *quals, List **constantQual)
|
||||
{
|
||||
List *q;
|
||||
List *normqual = NIL;
|
||||
List *noncachequal = NIL;
|
||||
List *constqual = NIL;
|
||||
List *restqual = NIL;
|
||||
List *q;
|
||||
|
||||
foreach(q, quals)
|
||||
{
|
||||
Node *qual = (Node *) lfirst(q);
|
||||
Node *qual = (Node *) lfirst(q);
|
||||
|
||||
if (contain_var_clause(qual))
|
||||
normqual = lappend(normqual, qual);
|
||||
else if (contain_noncachable_functions(qual))
|
||||
noncachequal = lappend(noncachequal, qual);
|
||||
else
|
||||
if (is_pseudo_constant_clause(qual))
|
||||
constqual = lappend(constqual, qual);
|
||||
else
|
||||
restqual = lappend(restqual, qual);
|
||||
}
|
||||
|
||||
*noncachableQual = noncachequal;
|
||||
*constantQual = constqual;
|
||||
return normqual;
|
||||
return restqual;
|
||||
}
|
||||
|
||||
|
||||
@ -1636,9 +1635,9 @@ 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.) Also, RangeTblRef and
|
||||
* JoinExpr nodes are handled, so that qual expressions in a jointree can be
|
||||
* processed without additional code.
|
||||
* chooses to process TargetEntry nodes specially.) Also, RangeTblRef,
|
||||
* FromExpr, and JoinExpr nodes are handled, so that qual expressions in a
|
||||
* jointree can be processed without additional code.
|
||||
*
|
||||
* expression_tree_walker will handle SubLink and SubPlan nodes by recursing
|
||||
* normally into the "lefthand" arguments (which belong to the outer plan).
|
||||
@ -1801,6 +1800,16 @@ expression_tree_walker(Node *node,
|
||||
break;
|
||||
case T_TargetEntry:
|
||||
return walker(((TargetEntry *) node)->expr, context);
|
||||
case T_FromExpr:
|
||||
{
|
||||
FromExpr *from = (FromExpr *) node;
|
||||
|
||||
if (walker(from->fromlist, context))
|
||||
return true;
|
||||
if (walker(from->quals, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
{
|
||||
JoinExpr *join = (JoinExpr *) node;
|
||||
@ -1844,14 +1853,12 @@ query_tree_walker(Query *query,
|
||||
|
||||
if (walker((Node *) query->targetList, context))
|
||||
return true;
|
||||
if (walker(query->qual, context))
|
||||
if (walker((Node *) query->jointree, 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
|
||||
* XXX for subselect-in-FROM, may need to examine rtable as well?
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
@ -2126,6 +2133,17 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_FromExpr:
|
||||
{
|
||||
FromExpr *from = (FromExpr *) node;
|
||||
FromExpr *newnode;
|
||||
|
||||
FLATCOPY(newnode, from, FromExpr);
|
||||
MUTATE(newnode->fromlist, from->fromlist, List *);
|
||||
MUTATE(newnode->quals, from->quals, Node *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
{
|
||||
JoinExpr *join = (JoinExpr *) node;
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indexnode.c
|
||||
* Routines to find all indices on a relation
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/indexnode.c,v 1.22 2000/01/26 05:56:40 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/plancat.h"
|
||||
|
||||
|
||||
/*
|
||||
* find_relation_indices
|
||||
* Returns a list of index nodes containing appropriate information for
|
||||
* each (secondary) index defined on a relation.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
find_relation_indices(Query *root, RelOptInfo *rel)
|
||||
{
|
||||
if (rel->indexed)
|
||||
return find_secondary_indexes(root, lfirsti(rel->relids));
|
||||
else
|
||||
return NIL;
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.65 2000/09/12 21:06:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.66 2000/09/29 18:21:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/plannodes.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/paths.h"
|
||||
@ -272,7 +273,6 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
|
||||
* create_seqscan_path
|
||||
* Creates a path corresponding to a sequential scan, returning the
|
||||
* pathnode.
|
||||
*
|
||||
*/
|
||||
Path *
|
||||
create_seqscan_path(RelOptInfo *rel)
|
||||
@ -343,8 +343,8 @@ create_index_path(Query *root,
|
||||
* We are making a pathnode for a single-scan indexscan; therefore,
|
||||
* both indexid and indexqual should be single-element lists.
|
||||
*/
|
||||
pathnode->indexid = lconsi(index->indexoid, NIL);
|
||||
pathnode->indexqual = lcons(indexquals, NIL);
|
||||
pathnode->indexid = makeListi1(index->indexoid);
|
||||
pathnode->indexqual = makeList1(indexquals);
|
||||
|
||||
pathnode->indexscandir = indexscandir;
|
||||
|
||||
@ -390,6 +390,27 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_subqueryscan_path
|
||||
* Creates a path corresponding to a sequential scan of a subquery,
|
||||
* returning the pathnode.
|
||||
*/
|
||||
Path *
|
||||
create_subqueryscan_path(RelOptInfo *rel)
|
||||
{
|
||||
Path *pathnode = makeNode(Path);
|
||||
|
||||
pathnode->pathtype = T_SubqueryScan;
|
||||
pathnode->parent = rel;
|
||||
pathnode->pathkeys = NIL; /* for now, assume unordered result */
|
||||
|
||||
/* just copy the subplan's cost estimates */
|
||||
pathnode->startup_cost = rel->subplan->startup_cost;
|
||||
pathnode->total_cost = rel->subplan->total_cost;
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* create_nestloop_path
|
||||
* Creates a pathnode corresponding to a nestloop join between two
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.60 2000/07/27 23:16:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.61 2000/09/29 18:21:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -25,7 +25,6 @@
|
||||
#include "catalog/pg_inherits.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/relcache.h"
|
||||
@ -37,16 +36,15 @@
|
||||
/*
|
||||
* relation_info -
|
||||
* Retrieves catalog information for a given relation.
|
||||
* Given the rangetable index of the relation, return the following info:
|
||||
* Given the Oid of the relation, return the following info:
|
||||
* whether the relation has secondary indices
|
||||
* number of pages
|
||||
* number of tuples
|
||||
*/
|
||||
void
|
||||
relation_info(Query *root, Index relid,
|
||||
relation_info(Oid relationObjectId,
|
||||
bool *hasindex, long *pages, double *tuples)
|
||||
{
|
||||
Oid relationObjectId = getrelid(relid, root->rtable);
|
||||
HeapTuple relationTuple;
|
||||
Form_pg_class relation;
|
||||
|
||||
@ -69,19 +67,18 @@ relation_info(Query *root, Index relid,
|
||||
/*
|
||||
* find_secondary_indexes
|
||||
* Creates a list of IndexOptInfo nodes containing information for each
|
||||
* secondary index defined on the given relation.
|
||||
* secondary index defined on the specified relation.
|
||||
*
|
||||
* 'relid' is the RT index of the relation for which indices are being located
|
||||
* 'relationObjectId' is the OID of the relation for which indices are wanted
|
||||
*
|
||||
* Returns a list of new IndexOptInfo nodes.
|
||||
*/
|
||||
List *
|
||||
find_secondary_indexes(Query *root, Index relid)
|
||||
find_secondary_indexes(Oid relationObjectId)
|
||||
{
|
||||
List *indexinfos = NIL;
|
||||
List *indexoidlist,
|
||||
*indexoidscan;
|
||||
Oid indrelid = getrelid(relid, root->rtable);
|
||||
Relation relation;
|
||||
|
||||
/*
|
||||
@ -89,12 +86,12 @@ find_secondary_indexes(Query *root, Index relid)
|
||||
* a cached list of OID indexes for each relation. So, get that list
|
||||
* and then use the syscache to obtain pg_index entries.
|
||||
*/
|
||||
relation = heap_open(indrelid, AccessShareLock);
|
||||
relation = heap_open(relationObjectId, AccessShareLock);
|
||||
indexoidlist = RelationGetIndexList(relation);
|
||||
|
||||
foreach(indexoidscan, indexoidlist)
|
||||
{
|
||||
Oid indexoid = lfirsti(indexoidscan);
|
||||
Oid indexoid = lfirsti(indexoidscan);
|
||||
HeapTuple indexTuple;
|
||||
Form_pg_index index;
|
||||
IndexOptInfo *info;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.28 2000/09/12 21:06:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.29 2000/09/29 18:21:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,6 +19,7 @@
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
|
||||
static List *new_join_tlist(List *tlist, int first_resdomno);
|
||||
@ -44,6 +45,7 @@ get_base_rel(Query *root, int relid)
|
||||
{
|
||||
List *baserels;
|
||||
RelOptInfo *rel;
|
||||
Oid relationObjectId;
|
||||
|
||||
foreach(baserels, root->base_rel_list)
|
||||
{
|
||||
@ -59,7 +61,7 @@ get_base_rel(Query *root, int relid)
|
||||
|
||||
/* No existing RelOptInfo for this base rel, so make a new one */
|
||||
rel = makeNode(RelOptInfo);
|
||||
rel->relids = lconsi(relid, NIL);
|
||||
rel->relids = makeListi1(relid);
|
||||
rel->rows = 0;
|
||||
rel->width = 0;
|
||||
rel->targetlist = NIL;
|
||||
@ -67,18 +69,31 @@ get_base_rel(Query *root, int relid)
|
||||
rel->cheapest_startup_path = NULL;
|
||||
rel->cheapest_total_path = NULL;
|
||||
rel->pruneable = true;
|
||||
rel->issubquery = false;
|
||||
rel->indexed = false;
|
||||
rel->pages = 0;
|
||||
rel->tuples = 0;
|
||||
rel->subplan = NULL;
|
||||
rel->baserestrictinfo = NIL;
|
||||
rel->baserestrictcost = 0;
|
||||
rel->outerjoinset = NIL;
|
||||
rel->joininfo = NIL;
|
||||
rel->innerjoin = NIL;
|
||||
|
||||
/* Retrieve relation statistics from the system catalogs. */
|
||||
relation_info(root, relid,
|
||||
&rel->indexed, &rel->pages, &rel->tuples);
|
||||
/* Check rtable to see if it's a plain relation or a subquery */
|
||||
relationObjectId = getrelid(relid, root->rtable);
|
||||
|
||||
if (relationObjectId != InvalidOid)
|
||||
{
|
||||
/* Plain relation --- retrieve statistics from the system catalogs */
|
||||
relation_info(relationObjectId,
|
||||
&rel->indexed, &rel->pages, &rel->tuples);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* subquery --- mark it as such for later processing */
|
||||
rel->issubquery = true;
|
||||
}
|
||||
|
||||
root->base_rel_list = lcons(rel, root->base_rel_list);
|
||||
|
||||
@ -174,9 +189,11 @@ get_join_rel(Query *root,
|
||||
joinrel->cheapest_startup_path = NULL;
|
||||
joinrel->cheapest_total_path = NULL;
|
||||
joinrel->pruneable = true;
|
||||
joinrel->issubquery = false;
|
||||
joinrel->indexed = false;
|
||||
joinrel->pages = 0;
|
||||
joinrel->tuples = 0;
|
||||
joinrel->subplan = NULL;
|
||||
joinrel->baserestrictinfo = NIL;
|
||||
joinrel->baserestrictcost = 0;
|
||||
joinrel->outerjoinset = NIL;
|
||||
@ -310,7 +327,7 @@ build_joinrel_restrictlist(RelOptInfo *joinrel,
|
||||
* We must eliminate duplicates, since we will see the same clauses
|
||||
* arriving from both input relations...
|
||||
*/
|
||||
return LispUnion(subbuild_joinrel_restrictlist(joinrel,
|
||||
return set_union(subbuild_joinrel_restrictlist(joinrel,
|
||||
outer_rel->joininfo),
|
||||
subbuild_joinrel_restrictlist(joinrel,
|
||||
inner_rel->joininfo));
|
||||
@ -396,7 +413,7 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
||||
|
||||
new_joininfo = find_joininfo_node(joinrel, new_unjoined_relids);
|
||||
new_joininfo->jinfo_restrictinfo =
|
||||
LispUnion(new_joininfo->jinfo_restrictinfo,
|
||||
set_union(new_joininfo->jinfo_restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.11 2000/09/12 21:06:58 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.12 2000/09/29 18:21:23 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -59,7 +59,7 @@ get_actual_clauses(List *restrictinfo_list)
|
||||
* get_actual_join_clauses
|
||||
*
|
||||
* Extract clauses from 'restrictinfo_list', separating those that
|
||||
* came from JOIN/ON conditions from those that didn't.
|
||||
* syntactically match the join level from those that were pushed down.
|
||||
*/
|
||||
void
|
||||
get_actual_join_clauses(List *restrictinfo_list,
|
||||
@ -74,9 +74,9 @@ get_actual_join_clauses(List *restrictinfo_list,
|
||||
{
|
||||
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
|
||||
|
||||
if (clause->isjoinqual)
|
||||
*joinquals = lappend(*joinquals, clause->clause);
|
||||
else
|
||||
if (clause->ispusheddown)
|
||||
*otherquals = lappend(*otherquals, clause->clause);
|
||||
else
|
||||
*joinquals = lappend(*joinquals, clause->clause);
|
||||
}
|
||||
}
|
||||
|
@ -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.158 2000/09/25 12:58:46 momjian Exp $
|
||||
* $Id: analyze.c,v 1.159 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -56,6 +56,7 @@ static void transformColumnType(ParseState *pstate, ColumnDef *column);
|
||||
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint);
|
||||
|
||||
static void release_pstate_resources(ParseState *pstate);
|
||||
static FromExpr *makeFromExpr(List *fromlist, Node *quals);
|
||||
|
||||
/* kluge to return extra info from transformCreateStmt() */
|
||||
static List *extras_before;
|
||||
@ -289,6 +290,7 @@ static Query *
|
||||
transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
Node *qual;
|
||||
|
||||
qry->commandType = CMD_DELETE;
|
||||
|
||||
@ -299,17 +301,17 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
qry->distinctClause = NIL;
|
||||
|
||||
/* fix where clause */
|
||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
/* done building the rtable */
|
||||
/* done building the range table and jointree */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
|
||||
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
parseCheckAggregates(pstate, qry, qual);
|
||||
|
||||
return (Query *) qry;
|
||||
}
|
||||
@ -322,6 +324,7 @@ static Query *
|
||||
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
Node *qual;
|
||||
List *icolumns;
|
||||
List *attrnos;
|
||||
List *attnos;
|
||||
@ -348,7 +351,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
|
||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||
|
||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
/*
|
||||
* Initial processing of HAVING clause is just like WHERE clause.
|
||||
@ -371,7 +374,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
parseCheckAggregates(pstate, qry, qual);
|
||||
|
||||
/*
|
||||
* The INSERT INTO ... SELECT ... could have a UNION in child, so
|
||||
@ -393,13 +396,13 @@ 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.) Note that the INSERT target is NOT added to the
|
||||
* join tree, since we don't want to join over it.
|
||||
* joinlist, since we don't want to join over it.
|
||||
*/
|
||||
setTargetTable(pstate, stmt->relname, false, false);
|
||||
|
||||
/* now the range table will not change */
|
||||
/* now the range table and jointree will not change */
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
|
||||
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
||||
|
||||
/* Prepare to assign non-conflicting resnos to resjunk attributes */
|
||||
@ -715,7 +718,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
||||
snamenode->val.val.str = qstring;
|
||||
funccallnode = makeNode(FuncCall);
|
||||
funccallnode->funcname = "nextval";
|
||||
funccallnode->args = lcons(snamenode, NIL);
|
||||
funccallnode->args = makeList1(snamenode);
|
||||
funccallnode->agg_star = false;
|
||||
funccallnode->agg_distinct = false;
|
||||
|
||||
@ -748,7 +751,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
||||
elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
|
||||
sequence->seqname, stmt->relname, column->colname);
|
||||
|
||||
blist = lcons(sequence, NIL);
|
||||
blist = makeList1(sequence);
|
||||
}
|
||||
|
||||
/* Process column constraints, if any... */
|
||||
@ -776,7 +779,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
||||
id->isRel = false;
|
||||
|
||||
fkconstraint = (FkConstraint *) constraint;
|
||||
fkconstraint->fk_attrs = lappend(NIL, id);
|
||||
fkconstraint->fk_attrs = makeList1(id);
|
||||
|
||||
fkconstraints = lappend(fkconstraints, constraint);
|
||||
continue;
|
||||
@ -815,7 +818,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
||||
{
|
||||
key = makeNode(Ident);
|
||||
key->name = pstrdup(column->colname);
|
||||
constraint->keys = lcons(key, NIL);
|
||||
constraint->keys = makeList1(key);
|
||||
}
|
||||
dlist = lappend(dlist, constraint);
|
||||
break;
|
||||
@ -827,7 +830,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
|
||||
{
|
||||
key = makeNode(Ident);
|
||||
key->name = pstrdup(column->colname);
|
||||
constraint->keys = lcons(key, NIL);
|
||||
constraint->keys = makeList1(key);
|
||||
}
|
||||
dlist = lappend(dlist, constraint);
|
||||
break;
|
||||
@ -1453,8 +1456,11 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
newrte = addRangeTableEntry(pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
false, true);
|
||||
/* Must override addRangeTableEntry's default access-check flags */
|
||||
oldrte->checkForRead = false;
|
||||
newrte->checkForRead = false;
|
||||
/*
|
||||
* They must be in the jointree too for lookup purposes, but only add
|
||||
* They must be in the joinlist 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.
|
||||
@ -1464,17 +1470,17 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
switch (stmt->event)
|
||||
{
|
||||
case CMD_SELECT:
|
||||
addRTEtoJoinTree(pstate, oldrte);
|
||||
addRTEtoJoinList(pstate, oldrte);
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
addRTEtoJoinTree(pstate, oldrte);
|
||||
addRTEtoJoinTree(pstate, newrte);
|
||||
addRTEtoJoinList(pstate, oldrte);
|
||||
addRTEtoJoinList(pstate, newrte);
|
||||
break;
|
||||
case CMD_INSERT:
|
||||
addRTEtoJoinTree(pstate, newrte);
|
||||
addRTEtoJoinList(pstate, newrte);
|
||||
break;
|
||||
case CMD_DELETE:
|
||||
addRTEtoJoinTree(pstate, oldrte);
|
||||
addRTEtoJoinList(pstate, oldrte);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "transformRuleStmt: unexpected event type %d",
|
||||
@ -1504,9 +1510,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
|
||||
nothing_qry->commandType = CMD_NOTHING;
|
||||
nothing_qry->rtable = pstate->p_rtable;
|
||||
nothing_qry->jointree = NIL; /* no join actually wanted */
|
||||
nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
|
||||
|
||||
stmt->actions = lappend(NIL, nothing_qry);
|
||||
stmt->actions = makeList1(nothing_qry);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1526,7 +1532,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
* 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
|
||||
* actions. We don't need to add them to the joinlist for
|
||||
* qualified-name lookup, either (see qualifiedNameToVar()).
|
||||
*/
|
||||
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
|
||||
@ -1535,6 +1541,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
newrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
|
||||
makeAttr("*NEW*", NULL),
|
||||
false, false);
|
||||
oldrte->checkForRead = false;
|
||||
newrte->checkForRead = false;
|
||||
|
||||
/* Transform the rule action statement */
|
||||
sub_qry = transformStmt(sub_pstate, lfirst(actions));
|
||||
@ -1581,8 +1589,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
*/
|
||||
if (has_old)
|
||||
{
|
||||
addRTEtoJoinTree(sub_pstate, oldrte);
|
||||
sub_qry->jointree = sub_pstate->p_jointree;
|
||||
addRTEtoJoinList(sub_pstate, oldrte);
|
||||
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
|
||||
}
|
||||
|
||||
lfirst(actions) = sub_qry;
|
||||
@ -1605,6 +1613,7 @@ static Query *
|
||||
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
Node *qual;
|
||||
|
||||
qry->commandType = CMD_SELECT;
|
||||
|
||||
@ -1617,7 +1626,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
|
||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||
|
||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
/*
|
||||
* Initial processing of HAVING clause is just like WHERE clause.
|
||||
@ -1641,7 +1650,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
parseCheckAggregates(pstate, qry, qual);
|
||||
|
||||
/*
|
||||
* The INSERT INTO ... SELECT ... could have a UNION in child, so
|
||||
@ -1657,7 +1666,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
qry->intersectClause = stmt->intersectClause;
|
||||
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
|
||||
|
||||
if (stmt->forUpdate != NULL)
|
||||
transformForUpdate(qry, stmt->forUpdate);
|
||||
@ -1674,6 +1683,7 @@ static Query *
|
||||
transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
Node *qual;
|
||||
List *origTargetList;
|
||||
List *tl;
|
||||
|
||||
@ -1683,22 +1693,27 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
/*
|
||||
* the FROM clause is non-standard SQL syntax. We used to be able to
|
||||
* do this with REPLACE in POSTQUEL so we keep the feature.
|
||||
*
|
||||
* Note: it's critical here that we process FROM before adding the
|
||||
* target table to the rtable --- otherwise, if the target is also
|
||||
* used in FROM, we'd fail to notice that it should be marked
|
||||
* checkForRead as well as checkForWrite. See setTargetTable().
|
||||
*/
|
||||
makeRangeTable(pstate, stmt->fromClause);
|
||||
setTargetTable(pstate, stmt->relname, stmt->inh, true);
|
||||
|
||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
||||
|
||||
qry->qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
qual = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
qry->rtable = pstate->p_rtable;
|
||||
qry->jointree = pstate->p_jointree;
|
||||
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
|
||||
qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
qry->hasAggs = pstate->p_hasAggs;
|
||||
if (pstate->p_hasAggs)
|
||||
parseCheckAggregates(pstate, qry);
|
||||
parseCheckAggregates(pstate, qry, qual);
|
||||
|
||||
/*
|
||||
* Now we are done with SELECT-like processing, and can get on with
|
||||
@ -2083,7 +2098,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = OR_EXPR;
|
||||
expr->args = makeList(lexpr, rexpr, -1);
|
||||
expr->args = makeList2(lexpr, rexpr);
|
||||
result = (Node *) expr;
|
||||
break;
|
||||
}
|
||||
@ -2095,7 +2110,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = AND_EXPR;
|
||||
expr->args = makeList(lexpr, rexpr, -1);
|
||||
expr->args = makeList2(lexpr, rexpr);
|
||||
result = (Node *) expr;
|
||||
break;
|
||||
}
|
||||
@ -2106,7 +2121,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = NOT_EXPR;
|
||||
expr->args = makeList(rexpr, -1);
|
||||
expr->args = makeList1(rexpr);
|
||||
result = (Node *) expr;
|
||||
break;
|
||||
}
|
||||
@ -2122,7 +2137,7 @@ A_Expr_to_Expr(Node *ptr, bool *intersect_present)
|
||||
void
|
||||
CheckSelectForUpdate(Query *qry)
|
||||
{
|
||||
if (qry->unionClause != NULL)
|
||||
if (qry->unionClause || qry->intersectClause)
|
||||
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT clause");
|
||||
if (qry->distinctClause != NIL)
|
||||
elog(ERROR, "SELECT FOR UPDATE is not allowed with DISTINCT clause");
|
||||
@ -2135,61 +2150,70 @@ CheckSelectForUpdate(Query *qry)
|
||||
static void
|
||||
transformForUpdate(Query *qry, List *forUpdate)
|
||||
{
|
||||
List *rowMark = NULL;
|
||||
RowMark *newrm;
|
||||
List *rowMarks = NIL;
|
||||
List *l;
|
||||
List *rt;
|
||||
Index i;
|
||||
|
||||
CheckSelectForUpdate(qry);
|
||||
|
||||
if (lfirst(forUpdate) == NULL) /* all tables */
|
||||
if (lfirst(forUpdate) == NULL)
|
||||
{
|
||||
i = 1;
|
||||
foreach(l, qry->rtable)
|
||||
/* all tables used in query */
|
||||
i = 0;
|
||||
foreach(rt, qry->rtable)
|
||||
{
|
||||
newrm = makeNode(RowMark);
|
||||
newrm->rti = i++;
|
||||
newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
|
||||
rowMark = lappend(rowMark, newrm);
|
||||
}
|
||||
qry->rowMark = nconc(qry->rowMark, rowMark);
|
||||
return;
|
||||
}
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
||||
|
||||
foreach(l, forUpdate)
|
||||
{
|
||||
char *relname = lfirst(l);
|
||||
List *l2;
|
||||
|
||||
i = 1;
|
||||
foreach(l2, qry->rtable)
|
||||
{
|
||||
if (strcmp(((RangeTblEntry *) lfirst(l2))->eref->relname, relname) == 0)
|
||||
++i;
|
||||
if (rte->subquery)
|
||||
{
|
||||
List *l3;
|
||||
|
||||
foreach(l3, rowMark)
|
||||
{
|
||||
if (((RowMark *) lfirst(l3))->rti == i) /* duplicate */
|
||||
break;
|
||||
}
|
||||
if (l3 == NULL)
|
||||
{
|
||||
newrm = makeNode(RowMark);
|
||||
newrm->rti = i;
|
||||
newrm->info = ROW_MARK_FOR_UPDATE | ROW_ACL_FOR_UPDATE;
|
||||
rowMark = lappend(rowMark, newrm);
|
||||
}
|
||||
break;
|
||||
/* FOR UPDATE of subquery is propagated to subquery's rels */
|
||||
transformForUpdate(rte->subquery, makeList1(NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
rowMarks = lappendi(rowMarks, i);
|
||||
rte->checkForWrite = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (l2 == NULL)
|
||||
elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
|
||||
relname);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* just the named tables */
|
||||
foreach(l, forUpdate)
|
||||
{
|
||||
char *relname = lfirst(l);
|
||||
|
||||
i = 0;
|
||||
foreach(rt, qry->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
||||
|
||||
++i;
|
||||
if (strcmp(rte->eref->relname, relname) == 0)
|
||||
{
|
||||
if (rte->subquery)
|
||||
{
|
||||
/* propagate to subquery */
|
||||
transformForUpdate(rte->subquery, makeList1(NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!intMember(i, rowMarks)) /* avoid duplicates */
|
||||
rowMarks = lappendi(rowMarks, i);
|
||||
rte->checkForWrite = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rt == NIL)
|
||||
elog(ERROR, "FOR UPDATE: relation \"%s\" not found in FROM clause",
|
||||
relname);
|
||||
}
|
||||
}
|
||||
|
||||
qry->rowMark = rowMark;
|
||||
qry->rowMarks = rowMarks;
|
||||
}
|
||||
|
||||
|
||||
@ -2452,6 +2476,17 @@ transformConstraintAttrs(List *constraintList)
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a FromExpr node */
|
||||
static FromExpr *
|
||||
makeFromExpr(List *fromlist, Node *quals)
|
||||
{
|
||||
FromExpr *f = makeNode(FromExpr);
|
||||
|
||||
f->fromlist = fromlist;
|
||||
f->quals = quals;
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special handling of type definition for a column
|
||||
*/
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.192 2000/09/25 18:38:39 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.193 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -403,7 +403,7 @@ stmtmulti: stmtmulti ';' stmt
|
||||
}
|
||||
| stmt
|
||||
{ if ($1 != (Node *)NULL)
|
||||
$$ = lcons($1,NIL);
|
||||
$$ = makeList1($1);
|
||||
else
|
||||
$$ = NIL;
|
||||
}
|
||||
@ -575,11 +575,11 @@ user_createuser_clause: CREATEUSER { $$ = +1; }
|
||||
|
||||
user_list: user_list ',' UserId
|
||||
{
|
||||
$$ = lcons((void*)makeString($3), $1);
|
||||
$$ = lappend($1, makeString($3));
|
||||
}
|
||||
| UserId
|
||||
{
|
||||
$$ = lcons((void*)makeString($1), NIL);
|
||||
$$ = makeList1(makeString($1));
|
||||
}
|
||||
;
|
||||
|
||||
@ -721,7 +721,7 @@ SessionList: SessionList ',' SessionClause
|
||||
}
|
||||
| SessionClause
|
||||
{
|
||||
$$ = lcons($1, NIL);
|
||||
$$ = makeList1($1);
|
||||
}
|
||||
;
|
||||
|
||||
@ -937,7 +937,7 @@ constraints_set_list: ALL
|
||||
|
||||
constraints_set_namelist: IDENT
|
||||
{
|
||||
$$ = lappend(NIL, $1);
|
||||
$$ = makeList1($1);
|
||||
}
|
||||
| constraints_set_namelist ',' IDENT
|
||||
{
|
||||
@ -1193,11 +1193,11 @@ OptTableElementList: OptTableElementList ',' OptTableElement
|
||||
| OptTableElement
|
||||
{
|
||||
if ($1 != NULL)
|
||||
$$ = lcons($1, NIL);
|
||||
$$ = makeList1($1);
|
||||
else
|
||||
$$ = NULL;
|
||||
$$ = NIL;
|
||||
}
|
||||
| /*EMPTY*/ { $$ = NULL; }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
OptTableElement: columnDef { $$ = $1; }
|
||||
@ -1551,7 +1551,7 @@ OptCreateAs: '(' CreateAsList ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
CreateAsList: CreateAsList ',' CreateAsElement { $$ = lappend($1, $3); }
|
||||
| CreateAsElement { $$ = lcons($1, NIL); }
|
||||
| CreateAsElement { $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
CreateAsElement: ColId
|
||||
@ -1783,7 +1783,7 @@ TriggerForType: ROW { $$ = TRUE; }
|
||||
;
|
||||
|
||||
TriggerFuncArgs: TriggerFuncArg
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
| TriggerFuncArgs ',' TriggerFuncArg
|
||||
{ $$ = lappend($1, $3); }
|
||||
| /*EMPTY*/
|
||||
@ -1899,7 +1899,7 @@ def_name: PROCEDURE { $$ = "procedure"; }
|
||||
definition: '(' def_list ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
def_list: def_elem { $$ = lcons($1, NIL); }
|
||||
def_list: def_elem { $$ = makeList1($1); }
|
||||
| def_list ',' def_elem { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
@ -2361,11 +2361,11 @@ access_method_clause: USING access_method { $$ = $2; }
|
||||
;
|
||||
|
||||
index_params: index_list { $$ = $1; }
|
||||
| func_index { $$ = lcons($1,NIL); }
|
||||
| func_index { $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
index_list: index_list ',' index_elem { $$ = lappend($1, $3); }
|
||||
| index_elem { $$ = lcons($1, NIL); }
|
||||
| index_elem { $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
func_index: func_name '(' name_list ')' opt_class
|
||||
@ -2486,9 +2486,9 @@ func_args: '(' func_args_list ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
func_args_list: func_arg
|
||||
{ $$ = lcons(makeString($1->name),NIL); }
|
||||
{ $$ = makeList1(makeString($1->name)); }
|
||||
| func_args_list ',' func_arg
|
||||
{ $$ = lappend($1,makeString($3->name)); }
|
||||
{ $$ = lappend($1, makeString($3->name)); }
|
||||
;
|
||||
|
||||
/* Would be nice to use the full Typename production for these fields,
|
||||
@ -2539,9 +2539,9 @@ opt_arg: IN
|
||||
;
|
||||
|
||||
func_as: Sconst
|
||||
{ $$ = lcons(makeString($1),NIL); }
|
||||
{ $$ = makeList1(makeString($1)); }
|
||||
| Sconst ',' Sconst
|
||||
{ $$ = lappend(lcons(makeString($1),NIL), makeString($3)); }
|
||||
{ $$ = makeList2(makeString($1), makeString($3)); }
|
||||
;
|
||||
|
||||
func_return: SimpleTypename
|
||||
@ -2631,11 +2631,11 @@ oper_argtypes: name
|
||||
elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
|
||||
}
|
||||
| name ',' name
|
||||
{ $$ = makeList(makeString($1), makeString($3), -1); }
|
||||
{ $$ = makeList2(makeString($1), makeString($3)); }
|
||||
| NONE ',' name /* left unary */
|
||||
{ $$ = makeList(NULL, makeString($3), -1); }
|
||||
{ $$ = makeList2(NULL, makeString($3)); }
|
||||
| name ',' NONE /* right unary */
|
||||
{ $$ = makeList(makeString($1), NULL, -1); }
|
||||
{ $$ = makeList2(makeString($1), NULL); }
|
||||
;
|
||||
|
||||
|
||||
@ -2724,22 +2724,22 @@ RuleStmt: CREATE RULE name AS
|
||||
;
|
||||
|
||||
RuleActionList: NOTHING { $$ = NIL; }
|
||||
| SelectStmt { $$ = lcons($1, NIL); }
|
||||
| RuleActionStmt { $$ = lcons($1, NIL); }
|
||||
| SelectStmt { $$ = makeList1($1); }
|
||||
| RuleActionStmt { $$ = makeList1($1); }
|
||||
| '[' RuleActionMulti ']' { $$ = $2; }
|
||||
| '(' RuleActionMulti ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
/* the thrashing around here is to discard "empty" statements... */
|
||||
RuleActionMulti: RuleActionMulti ';' RuleActionStmtOrEmpty
|
||||
{ if ($3 != (Node *)NULL)
|
||||
{ if ($3 != (Node *) NULL)
|
||||
$$ = lappend($1, $3);
|
||||
else
|
||||
$$ = $1;
|
||||
}
|
||||
| RuleActionStmtOrEmpty
|
||||
{ if ($1 != (Node *)NULL)
|
||||
$$ = lcons($1,NIL);
|
||||
{ if ($1 != (Node *) NULL)
|
||||
$$ = makeList1($1);
|
||||
else
|
||||
$$ = NIL;
|
||||
}
|
||||
@ -2761,7 +2761,7 @@ event_object: relation_name '.' attr_name
|
||||
$$ = makeNode(Attr);
|
||||
$$->relname = $1;
|
||||
$$->paramNo = NULL;
|
||||
$$->attrs = lcons(makeString($3), NIL);
|
||||
$$->attrs = makeList1(makeString($3));
|
||||
$$->indirection = NIL;
|
||||
}
|
||||
| relation_name
|
||||
@ -2910,10 +2910,8 @@ ViewStmt: CREATE VIEW name opt_column_list AS SelectStmt
|
||||
n->viewname = $3;
|
||||
n->aliases = $4;
|
||||
n->query = (Query *)$6;
|
||||
if (((SelectStmt *)n->query)->sortClause != NULL)
|
||||
elog(ERROR,"ORDER BY and DISTINCT on views are not implemented");
|
||||
if (((SelectStmt *)n->query)->unionClause != NULL)
|
||||
elog(ERROR,"UNION on views is not implemented");
|
||||
elog(ERROR,"UNION in views is not implemented");
|
||||
if (((SelectStmt *)n->query)->forUpdate != NULL)
|
||||
elog(ERROR, "SELECT FOR UPDATE is not allowed in CREATE VIEW");
|
||||
$$ = (Node *)n;
|
||||
@ -3092,9 +3090,9 @@ opt_va_list: '(' va_list ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
va_list: name
|
||||
{ $$=lcons($1,NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
| va_list ',' name
|
||||
{ $$=lappend($1,$3); }
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
|
||||
@ -3240,7 +3238,7 @@ opt_column_list: '(' columnList ')' { $$ = $2; }
|
||||
columnList: columnList ',' columnElem
|
||||
{ $$ = lappend($1, $3); }
|
||||
| columnElem
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
columnElem: ColId opt_indirection
|
||||
@ -3364,7 +3362,7 @@ opt_cursor: BINARY { $$ = TRUE; }
|
||||
*****************************************************************************/
|
||||
|
||||
/* A complete SELECT statement looks like this. Note sort, for_update,
|
||||
* and limit clauses can only appear once, not in each subselect.
|
||||
* and limit clauses can only appear once, not in each set operation.
|
||||
*
|
||||
* The rule returns a SelectStmt Node having the set operations attached to
|
||||
* unionClause and intersectClause (NIL if no set operations were present)
|
||||
@ -3584,7 +3582,7 @@ opt_all: ALL { $$ = TRUE; }
|
||||
/* We use (NIL) as a placeholder to indicate that all target expressions
|
||||
* should be placed in the DISTINCT list during parsetree analysis.
|
||||
*/
|
||||
opt_distinct: DISTINCT { $$ = lcons(NIL,NIL); }
|
||||
opt_distinct: DISTINCT { $$ = makeList1(NIL); }
|
||||
| DISTINCT ON '(' expr_list ')' { $$ = $4; }
|
||||
| ALL { $$ = NIL; }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
@ -3594,7 +3592,7 @@ sort_clause: ORDER BY sortby_list { $$ = $3; }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
sortby_list: sortby { $$ = lcons($1, NIL); }
|
||||
sortby_list: sortby { $$ = makeList1($1); }
|
||||
| sortby_list ',' sortby { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
@ -3614,17 +3612,17 @@ OptUseOp: USING all_Op { $$ = $2; }
|
||||
|
||||
|
||||
opt_select_limit: LIMIT select_limit_value ',' select_offset_value
|
||||
{ $$ = lappend(lappend(NIL, $4), $2); }
|
||||
{ $$ = makeList2($4, $2); }
|
||||
| LIMIT select_limit_value OFFSET select_offset_value
|
||||
{ $$ = lappend(lappend(NIL, $4), $2); }
|
||||
{ $$ = makeList2($4, $2); }
|
||||
| LIMIT select_limit_value
|
||||
{ $$ = lappend(lappend(NIL, NULL), $2); }
|
||||
{ $$ = makeList2(NULL, $2); }
|
||||
| OFFSET select_offset_value LIMIT select_limit_value
|
||||
{ $$ = lappend(lappend(NIL, $2), $4); }
|
||||
{ $$ = makeList2($2, $4); }
|
||||
| OFFSET select_offset_value
|
||||
{ $$ = lappend(lappend(NIL, $2), NULL); }
|
||||
{ $$ = makeList2($2, NULL); }
|
||||
| /* EMPTY */
|
||||
{ $$ = lappend(lappend(NIL, NULL), NULL); }
|
||||
{ $$ = makeList2(NULL, NULL); }
|
||||
;
|
||||
|
||||
select_limit_value: Iconst
|
||||
@ -3704,9 +3702,9 @@ opt_inh_star: '*' { $$ = TRUE; }
|
||||
relation_name_list: name_list;
|
||||
|
||||
name_list: name
|
||||
{ $$ = lcons(makeString($1),NIL); }
|
||||
{ $$ = makeList1(makeString($1)); }
|
||||
| name_list ',' name
|
||||
{ $$ = lappend($1,makeString($3)); }
|
||||
{ $$ = lappend($1, makeString($3)); }
|
||||
;
|
||||
|
||||
group_clause: GROUP BY expr_list { $$ = $3; }
|
||||
@ -3726,7 +3724,7 @@ for_update_clause: FOR UPDATE update_list { $$ = $3; }
|
||||
;
|
||||
|
||||
update_list: OF va_list { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = lcons(NULL, NULL); }
|
||||
| /* EMPTY */ { $$ = makeList1(NULL); }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
@ -3742,7 +3740,7 @@ from_clause: FROM from_list { $$ = $2; }
|
||||
;
|
||||
|
||||
from_list: from_list ',' table_ref { $$ = lappend($1, $3); }
|
||||
| table_ref { $$ = lcons($1, NIL); }
|
||||
| table_ref { $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
/*
|
||||
@ -4001,10 +3999,10 @@ Typename: SimpleTypename opt_array_bounds
|
||||
}
|
||||
;
|
||||
|
||||
opt_array_bounds: '[' ']' opt_array_bounds
|
||||
{ $$ = lcons(makeInteger(-1), $3); }
|
||||
| '[' Iconst ']' opt_array_bounds
|
||||
{ $$ = lcons(makeInteger($2), $4); }
|
||||
opt_array_bounds: opt_array_bounds '[' ']'
|
||||
{ $$ = lappend($1, makeInteger(-1)); }
|
||||
| opt_array_bounds '[' Iconst ']'
|
||||
{ $$ = lappend($1, makeInteger($3)); }
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
;
|
||||
@ -4296,7 +4294,7 @@ opt_timezone: WITH TIME ZONE { $$ = TRUE; }
|
||||
| /*EMPTY*/ { $$ = FALSE; }
|
||||
;
|
||||
|
||||
opt_interval: datetime { $$ = lcons($1, NIL); }
|
||||
opt_interval: datetime { $$ = makeList1($1); }
|
||||
| YEAR_P TO MONTH_P { $$ = NIL; }
|
||||
| DAY_P TO HOUR_P { $$ = NIL; }
|
||||
| DAY_P TO MINUTE_P { $$ = NIL; }
|
||||
@ -4403,7 +4401,7 @@ row_list: row_list ',' a_expr
|
||||
}
|
||||
| a_expr
|
||||
{
|
||||
$$ = lcons($1, NIL);
|
||||
$$ = makeList1($1);
|
||||
}
|
||||
;
|
||||
|
||||
@ -4524,7 +4522,7 @@ a_expr: c_expr
|
||||
{
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
n->funcname = "like_escape";
|
||||
n->args = makeList($3, $5, -1);
|
||||
n->args = makeList2($3, $5);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
$$ = makeA_Expr(OP, "~~", $1, (Node *) n);
|
||||
@ -4535,7 +4533,7 @@ a_expr: c_expr
|
||||
{
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
n->funcname = "like_escape";
|
||||
n->args = makeList($4, $6, -1);
|
||||
n->args = makeList2($4, $6);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
$$ = makeA_Expr(OP, "!~~", $1, (Node *) n);
|
||||
@ -4546,7 +4544,7 @@ a_expr: c_expr
|
||||
{
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
n->funcname = "like_escape";
|
||||
n->args = makeList($3, $5, -1);
|
||||
n->args = makeList2($3, $5);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
$$ = makeA_Expr(OP, "~~*", $1, (Node *) n);
|
||||
@ -4557,7 +4555,7 @@ a_expr: c_expr
|
||||
{
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
n->funcname = "like_escape";
|
||||
n->args = makeList($4, $6, -1);
|
||||
n->args = makeList2($4, $6);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
$$ = makeA_Expr(OP, "!~~*", $1, (Node *) n);
|
||||
@ -4634,7 +4632,7 @@ a_expr: c_expr
|
||||
if (IsA($4, SubLink))
|
||||
{
|
||||
SubLink *n = (SubLink *)$4;
|
||||
n->lefthand = lcons($1, NIL);
|
||||
n->lefthand = makeList1($1);
|
||||
n->oper = (List *) makeA_Expr(OP, "=", NULL, NULL);
|
||||
n->useor = FALSE;
|
||||
n->subLinkType = ANY_SUBLINK;
|
||||
@ -4661,7 +4659,7 @@ a_expr: c_expr
|
||||
if (IsA($5, SubLink))
|
||||
{
|
||||
SubLink *n = (SubLink *)$5;
|
||||
n->lefthand = lcons($1, NIL);
|
||||
n->lefthand = makeList1($1);
|
||||
n->oper = (List *) makeA_Expr(OP, "<>", NULL, NULL);
|
||||
n->useor = FALSE;
|
||||
n->subLinkType = ALL_SUBLINK;
|
||||
@ -4685,7 +4683,7 @@ a_expr: c_expr
|
||||
| a_expr all_Op sub_type '(' SubSelect ')'
|
||||
{
|
||||
SubLink *n = makeNode(SubLink);
|
||||
n->lefthand = lcons($1, NIL);
|
||||
n->lefthand = makeList1($1);
|
||||
n->oper = (List *) makeA_Expr(OP, $2, NULL, NULL);
|
||||
n->useor = FALSE; /* doesn't matter since only one col */
|
||||
n->subLinkType = $3;
|
||||
@ -4840,7 +4838,7 @@ c_expr: attr
|
||||
star->val.type = T_Integer;
|
||||
star->val.val.ival = 1;
|
||||
n->funcname = $1;
|
||||
n->args = lcons(star, NIL);
|
||||
n->args = makeList1(star);
|
||||
n->agg_star = TRUE;
|
||||
n->agg_distinct = FALSE;
|
||||
$$ = (Node *)n;
|
||||
@ -4873,7 +4871,7 @@ c_expr: attr
|
||||
t->typmod = -1;
|
||||
|
||||
n->funcname = xlateSqlType("date");
|
||||
n->args = lcons(s, NIL);
|
||||
n->args = makeList1(s);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
|
||||
@ -4898,7 +4896,7 @@ c_expr: attr
|
||||
t->typmod = -1;
|
||||
|
||||
n->funcname = xlateSqlType("time");
|
||||
n->args = lcons(s, NIL);
|
||||
n->args = makeList1(s);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
|
||||
@ -4923,7 +4921,7 @@ c_expr: attr
|
||||
t->typmod = -1;
|
||||
|
||||
n->funcname = xlateSqlType("time");
|
||||
n->args = lcons(s, NIL);
|
||||
n->args = makeList1(s);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
|
||||
@ -4952,7 +4950,7 @@ c_expr: attr
|
||||
t->typmod = -1;
|
||||
|
||||
n->funcname = xlateSqlType("timestamp");
|
||||
n->args = lcons(s, NIL);
|
||||
n->args = makeList1(s);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
|
||||
@ -4977,7 +4975,7 @@ c_expr: attr
|
||||
t->typmod = -1;
|
||||
|
||||
n->funcname = xlateSqlType("timestamp");
|
||||
n->args = lcons(s, NIL);
|
||||
n->args = makeList1(s);
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
|
||||
@ -5104,26 +5102,26 @@ c_expr: attr
|
||||
* Supporting nonterminals for expressions.
|
||||
*/
|
||||
|
||||
opt_indirection: '[' a_expr ']' opt_indirection
|
||||
opt_indirection: opt_indirection '[' a_expr ']'
|
||||
{
|
||||
A_Indices *ai = makeNode(A_Indices);
|
||||
ai->lidx = NULL;
|
||||
ai->uidx = $2;
|
||||
$$ = lcons(ai, $4);
|
||||
ai->uidx = $3;
|
||||
$$ = lappend($1, ai);
|
||||
}
|
||||
| '[' a_expr ':' a_expr ']' opt_indirection
|
||||
| opt_indirection '[' a_expr ':' a_expr ']'
|
||||
{
|
||||
A_Indices *ai = makeNode(A_Indices);
|
||||
ai->lidx = $2;
|
||||
ai->uidx = $4;
|
||||
$$ = lcons(ai, $6);
|
||||
ai->lidx = $3;
|
||||
ai->uidx = $5;
|
||||
$$ = lappend($1, ai);
|
||||
}
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
;
|
||||
|
||||
expr_list: a_expr
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
| expr_list ',' a_expr
|
||||
{ $$ = lappend($1, $3); }
|
||||
| expr_list USING a_expr
|
||||
@ -5135,7 +5133,7 @@ extract_list: extract_arg FROM a_expr
|
||||
A_Const *n = makeNode(A_Const);
|
||||
n->val.type = T_String;
|
||||
n->val.val.str = $1;
|
||||
$$ = makeList((Node *)n, $3, -1);
|
||||
$$ = makeList2((Node *) n, $3);
|
||||
}
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
@ -5149,7 +5147,7 @@ extract_arg: datetime { $$ = $1; }
|
||||
/* position_list uses b_expr not a_expr to avoid conflict with general IN */
|
||||
|
||||
position_list: b_expr IN b_expr
|
||||
{ $$ = makeList($3, $1, -1); }
|
||||
{ $$ = makeList2($3, $1); }
|
||||
| /*EMPTY*/
|
||||
{ $$ = NIL; }
|
||||
;
|
||||
@ -5169,7 +5167,7 @@ substr_from: FROM expr_list
|
||||
A_Const *n = makeNode(A_Const);
|
||||
n->val.type = T_Integer;
|
||||
n->val.val.ival = 1;
|
||||
$$ = lcons((Node *)n,NIL);
|
||||
$$ = makeList1((Node *)n);
|
||||
}
|
||||
;
|
||||
|
||||
@ -5198,7 +5196,7 @@ in_expr: SubSelect
|
||||
;
|
||||
|
||||
in_expr_nodes: a_expr
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
| in_expr_nodes ',' a_expr
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
@ -5236,7 +5234,7 @@ case_expr: CASE case_arg when_clause_list case_default END_TRANS
|
||||
w->result = (Node *)n;
|
||||
*/
|
||||
w->expr = makeA_Expr(OP, "=", $3, $5);
|
||||
c->args = lcons(w, NIL);
|
||||
c->args = makeList1(w);
|
||||
c->defresult = $3;
|
||||
$$ = (Node *)c;
|
||||
}
|
||||
@ -5259,7 +5257,7 @@ case_expr: CASE case_arg when_clause_list case_default END_TRANS
|
||||
when_clause_list: when_clause_list when_clause
|
||||
{ $$ = lappend($1, $2); }
|
||||
| when_clause
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
when_clause: WHEN a_expr THEN a_expr
|
||||
@ -5300,7 +5298,7 @@ attr: relation_name '.' attrs opt_indirection
|
||||
;
|
||||
|
||||
attrs: attr_name
|
||||
{ $$ = lcons(makeString($1), NIL); }
|
||||
{ $$ = makeList1(makeString($1)); }
|
||||
| attrs '.' attr_name
|
||||
{ $$ = lappend($1, makeString($3)); }
|
||||
| attrs '.' '*'
|
||||
@ -5319,7 +5317,7 @@ attrs: attr_name
|
||||
target_list: target_list ',' target_el
|
||||
{ $$ = lappend($1, $3); }
|
||||
| target_el
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
/* AS is not optional because shift/red conflict with unary ops */
|
||||
@ -5342,7 +5340,7 @@ target_el: a_expr AS ColLabel
|
||||
Attr *att = makeNode(Attr);
|
||||
att->relname = $1;
|
||||
att->paramNo = NULL;
|
||||
att->attrs = lcons(makeString("*"), NIL);
|
||||
att->attrs = makeList1(makeString("*"));
|
||||
att->indirection = NIL;
|
||||
$$ = makeNode(ResTarget);
|
||||
$$->name = NULL;
|
||||
@ -5368,7 +5366,7 @@ target_el: a_expr AS ColLabel
|
||||
update_target_list: update_target_list ',' update_target_el
|
||||
{ $$ = lappend($1,$3); }
|
||||
| update_target_el
|
||||
{ $$ = lcons($1, NIL); }
|
||||
{ $$ = makeList1($1); }
|
||||
;
|
||||
|
||||
update_target_el: ColId opt_indirection '=' a_expr
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.41 2000/09/25 18:14:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.42 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -129,10 +129,13 @@ check_ungrouped_columns_walker(Node *node,
|
||||
* Ideally this should be done earlier, but it's difficult to distinguish
|
||||
* aggregates from plain functions at the grammar level. So instead we
|
||||
* check here. This function should be called after the target list and
|
||||
* qualifications are finalized.
|
||||
* qualifications are finalized. BUT: in some cases we want to call this
|
||||
* routine before we've assembled the joinlist and qual into a FromExpr.
|
||||
* So, rather than looking at qry->jointree, look at pstate->p_joinlist
|
||||
* and the explicitly-passed qual.
|
||||
*/
|
||||
void
|
||||
parseCheckAggregates(ParseState *pstate, Query *qry)
|
||||
parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual)
|
||||
{
|
||||
List *groupClauses = NIL;
|
||||
List *tl;
|
||||
@ -141,18 +144,16 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
|
||||
Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual);
|
||||
|
||||
/*
|
||||
* Aggregates must never appear in WHERE clauses. (Note this check
|
||||
* should appear first to deliver an appropriate error message;
|
||||
* otherwise we are likely to complain about some innocent variable in
|
||||
* the target list, which is outright misleading if the problem is in
|
||||
* WHERE.)
|
||||
* Aggregates must never appear in WHERE or JOIN/ON clauses.
|
||||
*
|
||||
* (Note this check should appear first to deliver an appropriate error
|
||||
* message; otherwise we are likely to complain about some innocent
|
||||
* variable in the target list, which is outright misleading if the
|
||||
* problem is in WHERE.)
|
||||
*/
|
||||
if (contain_agg_clause(qry->qual))
|
||||
if (contain_agg_clause(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))
|
||||
if (contain_agg_clause((Node *) pstate->p_joinlist))
|
||||
elog(ERROR, "Aggregates not allowed in JOIN conditions");
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.67 2000/09/17 22:21:27 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.68 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -64,7 +64,7 @@ static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
|
||||
* POSTQUEL, we allow references to relations not specified in the
|
||||
* from-clause. PostgreSQL keeps this extension to standard SQL.)
|
||||
*
|
||||
* Note: we assume that pstate's p_rtable and p_jointree lists were
|
||||
* Note: we assume that pstate's p_rtable and p_joinlist lists were
|
||||
* initialized to NIL when the pstate was created. We will add onto
|
||||
* any entries already present --- this is needed for rule processing!
|
||||
*/
|
||||
@ -75,7 +75,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
|
||||
|
||||
/*
|
||||
* The grammar will have produced a list of RangeVars, RangeSubselects,
|
||||
* and/or JoinExprs. Transform each one, and then add it to the join tree.
|
||||
* and/or JoinExprs. Transform each one, and then add it to the joinlist.
|
||||
*/
|
||||
foreach(fl, frmList)
|
||||
{
|
||||
@ -83,7 +83,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
|
||||
List *containedRels;
|
||||
|
||||
n = transformFromClauseItem(pstate, n, &containedRels);
|
||||
pstate->p_jointree = lappend(pstate->p_jointree, n);
|
||||
pstate->p_joinlist = lappend(pstate->p_joinlist, n);
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ makeRangeTable(ParseState *pstate, List *frmList)
|
||||
* Add the target relation of INSERT/UPDATE/DELETE to the range table,
|
||||
* and make the special links to it in the ParseState.
|
||||
*
|
||||
* inJoinSet says whether to add the target to the join tree.
|
||||
* inJoinSet says whether to add the target to the join list.
|
||||
* For INSERT, we don't want the target to be joined to; it's a
|
||||
* destination of tuples, not a source. For UPDATE/DELETE, we do
|
||||
* need to scan or join the target.
|
||||
@ -106,15 +106,32 @@ setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
|
||||
if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
|
||||
{
|
||||
rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
|
||||
/*
|
||||
* Since the rel wasn't in the rangetable already, it's not being
|
||||
* read; override addRangeTableEntry's default checkForRead.
|
||||
*
|
||||
* If we find an explicit reference to the rel later during
|
||||
* parse analysis, scanRTEForColumn will change checkForRead
|
||||
* to 'true' again. That can't happen for INSERT but it is
|
||||
* possible for UPDATE and DELETE.
|
||||
*/
|
||||
rte->checkForRead = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
rte = refnameRangeTableEntry(pstate, relname);
|
||||
/*
|
||||
* Since the rel was in the rangetable already, it's being read
|
||||
* as well as written. Therefore, leave checkForRead true.
|
||||
*/
|
||||
/* XXX what if pre-existing entry has wrong inh setting? */
|
||||
}
|
||||
|
||||
/* Mark target table as requiring write access. */
|
||||
rte->checkForWrite = true;
|
||||
|
||||
if (inJoinSet)
|
||||
addRTEtoJoinTree(pstate, rte);
|
||||
addRTEtoJoinList(pstate, rte);
|
||||
|
||||
/* This could only happen for multi-action rules */
|
||||
if (pstate->p_target_relation != NULL)
|
||||
@ -242,22 +259,22 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
List *containedRels)
|
||||
{
|
||||
Node *result;
|
||||
List *sv_jointree;
|
||||
List *sv_joinlist;
|
||||
List *clause_varnos,
|
||||
*l;
|
||||
|
||||
/*
|
||||
* This is a tad tricky, for two reasons. First, at the point where
|
||||
* we're called, the two subtrees of the JOIN node aren't yet part of
|
||||
* the pstate's jointree, which means that transformExpr() won't resolve
|
||||
* the pstate's joinlist, which means that transformExpr() won't resolve
|
||||
* unqualified references to their columns correctly. We fix this in a
|
||||
* slightly klugy way: temporarily make the pstate's jointree consist of
|
||||
* slightly klugy way: temporarily make the pstate's joinlist consist of
|
||||
* just those two subtrees (which creates exactly the namespace the ON
|
||||
* clause should see). This is OK only because the ON clause can't
|
||||
* legally alter the jointree by causing relation refs to be added.
|
||||
* legally alter the joinlist by causing relation refs to be added.
|
||||
*/
|
||||
sv_jointree = pstate->p_jointree;
|
||||
pstate->p_jointree = lcons(j->larg, lcons(j->rarg, NIL));
|
||||
sv_joinlist = pstate->p_joinlist;
|
||||
pstate->p_joinlist = makeList2(j->larg, j->rarg);
|
||||
|
||||
/* This part is just like transformWhereClause() */
|
||||
result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
|
||||
@ -267,12 +284,12 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
typeidTypeName(exprType(result)));
|
||||
}
|
||||
|
||||
pstate->p_jointree = sv_jointree;
|
||||
pstate->p_joinlist = sv_joinlist;
|
||||
|
||||
/*
|
||||
* Second, we need to check that the ON condition doesn't refer to any
|
||||
* rels outside the input subtrees of the JOIN. It could do that despite
|
||||
* our hack on the jointree if it uses fully-qualified names. So, grovel
|
||||
* our hack on the joinlist if it uses fully-qualified names. So, grovel
|
||||
* through the transformed clause and make sure there are no bogus
|
||||
* references.
|
||||
*/
|
||||
@ -312,7 +329,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
|
||||
rte = addRangeTableEntry(pstate, relname, r->name, r->inh, true);
|
||||
|
||||
/*
|
||||
* We create a RangeTblRef, but we do not add it to the jointree here.
|
||||
* We create a RangeTblRef, but we do not add it to the joinlist here.
|
||||
* makeRangeTable will do so, if we are at top level of the FROM clause.
|
||||
*/
|
||||
rtr = makeNode(RangeTblRef);
|
||||
@ -333,6 +350,16 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
SelectStmt *subquery = (SelectStmt *) r->subquery;
|
||||
List *parsetrees;
|
||||
Query *query;
|
||||
RangeTblEntry *rte;
|
||||
RangeTblRef *rtr;
|
||||
|
||||
/*
|
||||
* We require user to supply an alias for a subselect, per SQL92.
|
||||
* To relax this, we'd have to be prepared to gin up a unique alias
|
||||
* for an unlabeled subselect.
|
||||
*/
|
||||
if (r->name == NULL)
|
||||
elog(ERROR, "sub-select in FROM must have an alias");
|
||||
|
||||
/*
|
||||
* subquery node might not be SelectStmt if user wrote something like
|
||||
@ -347,7 +374,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
* Analyze and transform the subquery as if it were an independent
|
||||
* statement (we do NOT want it to see the outer query as a parent).
|
||||
*/
|
||||
parsetrees = parse_analyze(lcons(subquery, NIL), NULL);
|
||||
parsetrees = parse_analyze(makeList1(subquery), NULL);
|
||||
|
||||
/*
|
||||
* Check that we got something reasonable. Some of these conditions
|
||||
@ -362,13 +389,24 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
|
||||
if (query->commandType != CMD_SELECT)
|
||||
elog(ERROR, "Expected SELECT query from subselect in FROM");
|
||||
if (query->resultRelation != 0 || query->into != NULL)
|
||||
if (query->resultRelation != 0 || query->into != NULL || query->isPortal)
|
||||
elog(ERROR, "Subselect in FROM may not have SELECT INTO");
|
||||
|
||||
/*
|
||||
* OK, build an RTE for the subquery.
|
||||
*/
|
||||
rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);
|
||||
|
||||
elog(ERROR, "Subselect in FROM not done yet");
|
||||
/*
|
||||
* We create a RangeTblRef, but we do not add it to the joinlist here.
|
||||
* makeRangeTable will do so, if we are at top level of the FROM clause.
|
||||
*/
|
||||
rtr = makeNode(RangeTblRef);
|
||||
/* assume new rte is at end */
|
||||
rtr->rtindex = length(pstate->p_rtable);
|
||||
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
||||
|
||||
return NULL;
|
||||
return rtr;
|
||||
}
|
||||
|
||||
|
||||
@ -376,12 +414,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
* transformFromClauseItem -
|
||||
* Transform a FROM-clause item, adding any required entries to the
|
||||
* range table list being built in the ParseState, and return the
|
||||
* transformed item ready to include in the jointree list.
|
||||
* transformed item ready to include in the joinlist.
|
||||
* This routine can recurse to handle SQL92 JOIN expressions.
|
||||
*
|
||||
* Aside from the primary return value (the transformed jointree item)
|
||||
* Aside from the primary return value (the transformed joinlist item)
|
||||
* this routine also returns an integer list of the rangetable indexes
|
||||
* of all the base relations represented in the jointree item. This
|
||||
* of all the base relations represented in the joinlist item. This
|
||||
* list is needed for checking JOIN/ON conditions in higher levels.
|
||||
*/
|
||||
static Node *
|
||||
@ -393,7 +431,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
RangeTblRef *rtr;
|
||||
|
||||
rtr = transformTableEntry(pstate, (RangeVar *) n);
|
||||
*containedRels = lconsi(rtr->rtindex, NIL);
|
||||
*containedRels = makeListi1(rtr->rtindex);
|
||||
return (Node *) rtr;
|
||||
}
|
||||
else if (IsA(n, RangeSubselect))
|
||||
@ -402,7 +440,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
RangeTblRef *rtr;
|
||||
|
||||
rtr = transformRangeSubselect(pstate, (RangeSubselect *) n);
|
||||
*containedRels = lconsi(rtr->rtindex, NIL);
|
||||
*containedRels = makeListi1(rtr->rtindex);
|
||||
return (Node *) rtr;
|
||||
}
|
||||
else if (IsA(n, JoinExpr))
|
||||
@ -599,7 +637,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
a->lexpr = l_colvar;
|
||||
w->expr = (Node *) a;
|
||||
w->result = l_colvar;
|
||||
c->args = lcons(w, NIL);
|
||||
c->args = makeList1(w);
|
||||
c->defresult = r_colvar;
|
||||
colvar = transformExpr(pstate, (Node *) c,
|
||||
EXPR_COLUMN_FIRST);
|
||||
@ -641,17 +679,17 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
* The given table alias must be unique in the current nesting level,
|
||||
* ie it cannot match any RTE refname or jointable alias. This is
|
||||
* a bit painful to check because my own child joins are not yet in
|
||||
* the pstate's jointree, so they have to be scanned separately.
|
||||
* the pstate's joinlist, so they have to be scanned separately.
|
||||
*/
|
||||
if (j->alias)
|
||||
{
|
||||
/* Check against previously created RTEs and jointree entries */
|
||||
/* Check against previously created RTEs and joinlist entries */
|
||||
if (refnameRangeOrJoinEntry(pstate, j->alias->relname, NULL))
|
||||
elog(ERROR, "Table name \"%s\" specified more than once",
|
||||
j->alias->relname);
|
||||
/* Check children */
|
||||
if (scanJoinTreeForRefname(j->larg, j->alias->relname) ||
|
||||
scanJoinTreeForRefname(j->rarg, j->alias->relname))
|
||||
if (scanJoinListForRefname(j->larg, j->alias->relname) ||
|
||||
scanJoinListForRefname(j->rarg, j->alias->relname))
|
||||
elog(ERROR, "Table name \"%s\" specified more than once",
|
||||
j->alias->relname);
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.83 2000/09/12 21:07:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.84 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -175,7 +175,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
"nullvalue",
|
||||
lcons(lexpr, NIL),
|
||||
makeList1(lexpr),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
@ -188,7 +188,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
|
||||
result = ParseFuncOrColumn(pstate,
|
||||
"nonnullvalue",
|
||||
lcons(lexpr, NIL),
|
||||
makeList1(lexpr),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
@ -213,7 +213,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = AND_EXPR;
|
||||
expr->args = makeList(lexpr, rexpr, -1);
|
||||
expr->args = makeList2(lexpr, rexpr);
|
||||
result = (Node *) expr;
|
||||
}
|
||||
break;
|
||||
@ -235,7 +235,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = OR_EXPR;
|
||||
expr->args = makeList(lexpr, rexpr, -1);
|
||||
expr->args = makeList2(lexpr, rexpr);
|
||||
result = (Node *) expr;
|
||||
}
|
||||
break;
|
||||
@ -251,7 +251,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
typeidTypeName(exprType(rexpr)), typeidTypeName(BOOLOID));
|
||||
expr->typeOid = BOOLOID;
|
||||
expr->opType = NOT_EXPR;
|
||||
expr->args = makeList(rexpr, -1);
|
||||
expr->args = makeList1(rexpr);
|
||||
result = (Node *) expr;
|
||||
}
|
||||
break;
|
||||
@ -294,7 +294,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
break;
|
||||
}
|
||||
pstate->p_hasSubLinks = true;
|
||||
qtrees = parse_analyze(lcons(sublink->subselect, NIL), pstate);
|
||||
qtrees = parse_analyze(makeList1(sublink->subselect),
|
||||
pstate);
|
||||
if (length(qtrees) != 1)
|
||||
elog(ERROR, "Bad query in subselect");
|
||||
qtree = (Query *) lfirst(qtrees);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.90 2000/09/12 21:07:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.91 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -76,7 +76,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
|
||||
EXPR_RELATION_FIRST);
|
||||
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
|
||||
lcons(param, NIL),
|
||||
makeList1(param),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
@ -87,7 +87,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
|
||||
ident->name = attr->relname;
|
||||
ident->isRel = TRUE;
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(attr->attrs)),
|
||||
lcons(ident, NIL),
|
||||
makeList1(ident),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
@ -96,7 +96,7 @@ ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr, int precedence)
|
||||
foreach(mutator_iter, lnext(attr->attrs))
|
||||
{
|
||||
retval = ParseFuncOrColumn(pstate, strVal(lfirst(mutator_iter)),
|
||||
lcons(retval, NIL),
|
||||
makeList1(retval),
|
||||
false, false,
|
||||
precedence);
|
||||
}
|
||||
@ -447,6 +447,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
* but has varattno == 0 to signal that the whole tuple is the
|
||||
* argument.
|
||||
*/
|
||||
if (rte->relname == NULL)
|
||||
elog(ERROR,
|
||||
"function applied to tuple is not supported for subSELECTs");
|
||||
toid = typeTypeId(typenameType(rte->relname));
|
||||
|
||||
/* replace it in the arg list */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.47 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -152,11 +152,11 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
result->oper = (Node *) newop;
|
||||
|
||||
if (!left)
|
||||
result->args = lcons(right, NIL);
|
||||
result->args = makeList1(right);
|
||||
else if (!right)
|
||||
result->args = lcons(left, NIL);
|
||||
result->args = makeList1(left);
|
||||
else
|
||||
result->args = lcons(left, lcons(right, NIL));
|
||||
result->args = makeList2(left, right);
|
||||
|
||||
return result;
|
||||
} /* make_op() */
|
||||
@ -171,8 +171,8 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
||||
{
|
||||
int vnum,
|
||||
sublevels_up;
|
||||
Oid vartypeid;
|
||||
int32 type_mod;
|
||||
Oid vartypeid = 0;
|
||||
int32 type_mod = 0;
|
||||
|
||||
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
@ -197,8 +197,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
||||
else
|
||||
{
|
||||
/* Subselect RTE --- get type info from subselect's tlist */
|
||||
elog(ERROR, "make_var: subselect in FROM not implemented yet");
|
||||
vartypeid = type_mod = 0;
|
||||
List *tlistitem;
|
||||
|
||||
foreach(tlistitem, rte->subquery->targetList)
|
||||
{
|
||||
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
|
||||
|
||||
if (te->resdom->resjunk || te->resdom->resno != attrno)
|
||||
continue;
|
||||
vartypeid = te->resdom->restype;
|
||||
type_mod = te->resdom->restypmod;
|
||||
break;
|
||||
}
|
||||
/* falling off end of list shouldn't happen... */
|
||||
if (tlistitem == NIL)
|
||||
elog(ERROR, "Subquery %s does not have attribute %d",
|
||||
rte->eref->relname, attrno);
|
||||
}
|
||||
|
||||
return makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.48 2000/09/25 18:14:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.49 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -26,7 +26,6 @@
|
||||
#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"
|
||||
|
||||
@ -98,7 +97,7 @@ refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
|
||||
/*
|
||||
* Check the rangetable for RTEs; if no match, recursively scan
|
||||
* the jointree for join tables. We assume that no duplicate
|
||||
* the joinlist for join tables. We assume that no duplicate
|
||||
* entries have been made in any one nesting level.
|
||||
*/
|
||||
foreach(temp, pstate->p_rtable)
|
||||
@ -109,7 +108,7 @@ refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
return (Node *) rte;
|
||||
}
|
||||
|
||||
join = scanJoinTreeForRefname((Node *) pstate->p_jointree, refname);
|
||||
join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname);
|
||||
if (join)
|
||||
return (Node *) join;
|
||||
|
||||
@ -122,9 +121,14 @@ refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Recursively search a jointree for a joinexpr with given refname */
|
||||
/*
|
||||
* Recursively search a joinlist for a joinexpr with given refname
|
||||
*
|
||||
* Note that during parse analysis, we don't expect to find a FromExpr node
|
||||
* in p_joinlist; its top level is just a bare List.
|
||||
*/
|
||||
JoinExpr *
|
||||
scanJoinTreeForRefname(Node *jtnode, char *refname)
|
||||
scanJoinListForRefname(Node *jtnode, char *refname)
|
||||
{
|
||||
JoinExpr *result = NULL;
|
||||
|
||||
@ -136,7 +140,7 @@ scanJoinTreeForRefname(Node *jtnode, char *refname)
|
||||
|
||||
foreach(l, (List *) jtnode)
|
||||
{
|
||||
result = scanJoinTreeForRefname(lfirst(l), refname);
|
||||
result = scanJoinListForRefname(lfirst(l), refname);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
@ -151,12 +155,12 @@ scanJoinTreeForRefname(Node *jtnode, char *refname)
|
||||
|
||||
if (j->alias && strcmp(j->alias->relname, refname) == 0)
|
||||
return j;
|
||||
result = scanJoinTreeForRefname(j->larg, refname);
|
||||
result = scanJoinListForRefname(j->larg, refname);
|
||||
if (! result)
|
||||
result = scanJoinTreeForRefname(j->rarg, refname);
|
||||
result = scanJoinListForRefname(j->rarg, refname);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "scanJoinTreeForRefname: unexpected node type %d",
|
||||
elog(ERROR, "scanJoinListForRefname: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return result;
|
||||
}
|
||||
@ -261,6 +265,9 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
|
||||
* 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.
|
||||
*
|
||||
* Side effect: if we find a match, mark the RTE as requiring read access.
|
||||
* See comments in setTargetTable().
|
||||
*/
|
||||
static Node *
|
||||
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
@ -281,6 +288,7 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
if (result)
|
||||
elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
|
||||
result = (Node *) make_var(pstate, rte, attnum);
|
||||
rte->checkForRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +307,10 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
{
|
||||
attnum = specialAttNum(colname);
|
||||
if (attnum != InvalidAttrNumber)
|
||||
{
|
||||
result = (Node *) make_var(pstate, rte, attnum);
|
||||
rte->checkForRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -310,6 +321,11 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
* 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.
|
||||
*
|
||||
* NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
|
||||
* checkForRead true for the referenced tables. This is so because a join
|
||||
* expression can only appear in a FROM clause, and any table named in
|
||||
* FROM will be marked checkForRead from the beginning.
|
||||
*/
|
||||
static Node *
|
||||
scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
|
||||
@ -359,7 +375,7 @@ colnameToVar(ParseState *pstate, char *colname)
|
||||
* those, ignore RTEs that are marked as not inFromCl and not
|
||||
* the query's target relation.
|
||||
*/
|
||||
foreach(jt, pstate->p_jointree)
|
||||
foreach(jt, pstate->p_joinlist)
|
||||
{
|
||||
Node *jtnode = (Node *) lfirst(jt);
|
||||
Node *newresult = NULL;
|
||||
@ -446,8 +462,9 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an entry to the pstate's range table (p_rtable), unless the
|
||||
* specified refname is already present, in which case raise error.
|
||||
* Add an entry for a relation to the pstate's range table (p_rtable).
|
||||
*
|
||||
* If the specified refname is already present, raise error.
|
||||
*
|
||||
* If pstate is NULL, we just build an RTE and return it without worrying
|
||||
* about membership in an rtable list.
|
||||
@ -481,6 +498,7 @@ addRangeTableEntry(ParseState *pstate,
|
||||
|
||||
rte->relname = relname;
|
||||
rte->alias = alias;
|
||||
rte->subquery = NULL;
|
||||
|
||||
/*
|
||||
* Get the rel's OID. This access also ensures that we have an
|
||||
@ -492,7 +510,7 @@ addRangeTableEntry(ParseState *pstate,
|
||||
rte->relid = RelationGetRelid(rel);
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
|
||||
eref = alias ? copyObject(alias) : makeAttr(refname, NULL);
|
||||
eref = alias ? (Attr *) copyObject(alias) : makeAttr(refname, NULL);
|
||||
numaliases = length(eref->attrs);
|
||||
|
||||
if (maxattrs < numaliases)
|
||||
@ -515,12 +533,18 @@ addRangeTableEntry(ParseState *pstate,
|
||||
* 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.
|
||||
* - this RTE should be checked for read/write access rights.
|
||||
*
|
||||
* The initial default on access checks is always check-for-READ-access,
|
||||
* which is the right thing for all except target tables.
|
||||
*----------
|
||||
*/
|
||||
rte->inh = inh;
|
||||
rte->inFromCl = inFromCl;
|
||||
rte->skipAcl = false; /* always starts out false */
|
||||
rte->checkForRead = true;
|
||||
rte->checkForWrite = false;
|
||||
|
||||
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
|
||||
|
||||
/*
|
||||
* Add completed RTE to range table list.
|
||||
@ -532,17 +556,105 @@ addRangeTableEntry(ParseState *pstate,
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the given RTE as a top-level entry in the pstate's join tree,
|
||||
* Add an entry for a subquery to the pstate's range table (p_rtable).
|
||||
*
|
||||
* This is just like addRangeTableEntry() except that it makes a subquery RTE.
|
||||
* Note that an alias clause *must* be supplied.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
Query *subquery,
|
||||
Attr *alias,
|
||||
bool inFromCl)
|
||||
{
|
||||
char *refname = alias->relname;
|
||||
RangeTblEntry *rte;
|
||||
Attr *eref;
|
||||
int numaliases;
|
||||
int varattno;
|
||||
List *tlistitem;
|
||||
|
||||
/* Check for conflicting RTE or jointable alias (at level 0 only) */
|
||||
if (pstate != NULL)
|
||||
{
|
||||
Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
|
||||
|
||||
if (rteorjoin)
|
||||
elog(ERROR, "Table name \"%s\" specified more than once",
|
||||
refname);
|
||||
}
|
||||
|
||||
rte = makeNode(RangeTblEntry);
|
||||
|
||||
rte->relname = NULL;
|
||||
rte->relid = InvalidOid;
|
||||
rte->subquery = subquery;
|
||||
rte->alias = alias;
|
||||
|
||||
eref = copyObject(alias);
|
||||
numaliases = length(eref->attrs);
|
||||
|
||||
/* fill in any unspecified alias columns */
|
||||
varattno = 0;
|
||||
foreach(tlistitem, subquery->targetList)
|
||||
{
|
||||
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
|
||||
|
||||
if (te->resdom->resjunk)
|
||||
continue;
|
||||
varattno++;
|
||||
Assert(varattno == te->resdom->resno);
|
||||
if (varattno > numaliases)
|
||||
{
|
||||
char *attrname;
|
||||
|
||||
attrname = pstrdup(te->resdom->resname);
|
||||
eref->attrs = lappend(eref->attrs, makeString(attrname));
|
||||
}
|
||||
}
|
||||
if (varattno < numaliases)
|
||||
elog(ERROR, "Table \"%s\" has %d columns available but %d columns specified",
|
||||
refname, varattno, numaliases);
|
||||
|
||||
rte->eref = eref;
|
||||
|
||||
/*----------
|
||||
* Flags:
|
||||
* - this RTE should be expanded to include descendant tables,
|
||||
* - this RTE is in the FROM clause,
|
||||
* - this RTE should be checked for read/write access rights.
|
||||
*
|
||||
* Subqueries are never checked for access rights.
|
||||
*----------
|
||||
*/
|
||||
rte->inh = false; /* never true for subqueries */
|
||||
rte->inFromCl = inFromCl;
|
||||
rte->checkForRead = false;
|
||||
rte->checkForWrite = false;
|
||||
|
||||
rte->checkAsUser = InvalidOid;
|
||||
|
||||
/*
|
||||
* Add completed RTE to range table list.
|
||||
*/
|
||||
if (pstate != NULL)
|
||||
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
||||
|
||||
return rte;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the given RTE as a top-level entry in the pstate's join list,
|
||||
* unless there already is an entry for it.
|
||||
*/
|
||||
void
|
||||
addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
|
||||
addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte)
|
||||
{
|
||||
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
|
||||
List *jt;
|
||||
RangeTblRef *rtr;
|
||||
|
||||
foreach(jt, pstate->p_jointree)
|
||||
foreach(jt, pstate->p_joinlist)
|
||||
{
|
||||
Node *n = (Node *) lfirst(jt);
|
||||
|
||||
@ -556,7 +668,7 @@ addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte)
|
||||
/* Not present, so add it */
|
||||
rtr = makeNode(RangeTblRef);
|
||||
rtr->rtindex = rtindex;
|
||||
pstate->p_jointree = lappend(pstate->p_jointree, rtr);
|
||||
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -570,7 +682,7 @@ addImplicitRTE(ParseState *pstate, char *relname)
|
||||
RangeTblEntry *rte;
|
||||
|
||||
rte = addRangeTableEntry(pstate, relname, NULL, false, false);
|
||||
addRTEtoJoinTree(pstate, rte);
|
||||
addRTEtoJoinList(pstate, rte);
|
||||
warnAutoRange(pstate, relname);
|
||||
|
||||
return rte;
|
||||
@ -590,11 +702,9 @@ void
|
||||
expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
List **colnames, List **colvars)
|
||||
{
|
||||
Relation rel;
|
||||
int varattno,
|
||||
maxattrs,
|
||||
rtindex,
|
||||
sublevels_up;
|
||||
int rtindex,
|
||||
sublevels_up,
|
||||
varattno;
|
||||
|
||||
if (colnames)
|
||||
*colnames = NIL;
|
||||
@ -604,43 +714,88 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
/* Need the RT index of the entry for creating Vars */
|
||||
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
rel = heap_open(rte->relid, AccessShareLock);
|
||||
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
|
||||
for (varattno = 0; varattno < maxattrs; varattno++)
|
||||
if (rte->relname)
|
||||
{
|
||||
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
|
||||
/* Ordinary relation RTE */
|
||||
Relation rel;
|
||||
int maxattrs;
|
||||
|
||||
rel = heap_openr(rte->relname, AccessShareLock);
|
||||
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
|
||||
for (varattno = 0; varattno < maxattrs; varattno++)
|
||||
{
|
||||
Form_pg_attribute attr = rel->rd_att->attrs[varattno];
|
||||
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
if (COLUMN_IS_DROPPED(attr))
|
||||
continue;
|
||||
if (COLUMN_IS_DROPPED(attr))
|
||||
continue;
|
||||
#endif /* _DROP_COLUMN_HACK__ */
|
||||
|
||||
if (colnames)
|
||||
{
|
||||
char *label;
|
||||
if (colnames)
|
||||
{
|
||||
char *label;
|
||||
|
||||
if (varattno < length(rte->eref->attrs))
|
||||
label = strVal(nth(varattno, rte->eref->attrs));
|
||||
else
|
||||
label = NameStr(attr->attname);
|
||||
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
||||
if (varattno < length(rte->eref->attrs))
|
||||
label = strVal(nth(varattno, rte->eref->attrs));
|
||||
else
|
||||
label = NameStr(attr->attname);
|
||||
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
||||
}
|
||||
|
||||
if (colvars)
|
||||
{
|
||||
Var *varnode;
|
||||
|
||||
varnode = makeVar(rtindex, attr->attnum,
|
||||
attr->atttypid, attr->atttypmod,
|
||||
sublevels_up);
|
||||
|
||||
*colvars = lappend(*colvars, varnode);
|
||||
}
|
||||
}
|
||||
|
||||
if (colvars)
|
||||
heap_close(rel, AccessShareLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Subquery RTE */
|
||||
List *aliasp = rte->eref->attrs;
|
||||
List *tlistitem;
|
||||
|
||||
varattno = 0;
|
||||
foreach(tlistitem, rte->subquery->targetList)
|
||||
{
|
||||
Var *varnode;
|
||||
TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
|
||||
|
||||
varnode = makeVar(rtindex, attr->attnum,
|
||||
attr->atttypid, attr->atttypmod,
|
||||
sublevels_up);
|
||||
if (te->resdom->resjunk)
|
||||
continue;
|
||||
varattno++;
|
||||
Assert(varattno == te->resdom->resno);
|
||||
|
||||
*colvars = lappend(*colvars, varnode);
|
||||
if (colnames)
|
||||
{
|
||||
/* Assume there is one alias per target item */
|
||||
char *label = strVal(lfirst(aliasp));
|
||||
|
||||
*colnames = lappend(*colnames, makeString(pstrdup(label)));
|
||||
aliasp = lnext(aliasp);
|
||||
}
|
||||
|
||||
if (colvars)
|
||||
{
|
||||
Var *varnode;
|
||||
|
||||
varnode = makeVar(rtindex, varattno,
|
||||
te->resdom->restype,
|
||||
te->resdom->restypmod,
|
||||
sublevels_up);
|
||||
|
||||
*colvars = lappend(*colvars, varnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.62 2000/09/12 21:07:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.63 2000/09/29 18:21:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -386,7 +386,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
* 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.
|
||||
* which by now has been transformed into a joinlist.
|
||||
*/
|
||||
static List *
|
||||
ExpandAllTables(ParseState *pstate)
|
||||
@ -395,10 +395,10 @@ ExpandAllTables(ParseState *pstate)
|
||||
List *jt;
|
||||
|
||||
/* SELECT *; */
|
||||
if (pstate->p_jointree == NIL)
|
||||
if (pstate->p_joinlist == NIL)
|
||||
elog(ERROR, "Wildcard with no tables specified not allowed");
|
||||
|
||||
foreach(jt, pstate->p_jointree)
|
||||
foreach(jt, pstate->p_joinlist)
|
||||
{
|
||||
Node *n = (Node *) lfirst(jt);
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
# Makefile for rewrite
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/rewrite/Makefile,v 1.13 2000/08/31 16:10:27 petere Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/rewrite/Makefile,v 1.14 2000/09/29 18:21:24 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -13,7 +13,7 @@ top_builddir = ../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = rewriteRemove.o rewriteDefine.o \
|
||||
rewriteHandler.o rewriteManip.o rewriteSupport.o locks.o
|
||||
rewriteHandler.o rewriteManip.o rewriteSupport.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
@ -1,268 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* locks.c
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.32 2000/09/12 21:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "rewrite/locks.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/*
|
||||
* thisLockWasTriggered
|
||||
*
|
||||
* walk the tree, if there we find a varnode,
|
||||
* we check the varattno against the attnum
|
||||
* if we find at least one such match, we return true
|
||||
* otherwise, we return false
|
||||
*
|
||||
* XXX this should be unified with attribute_used()
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int varno;
|
||||
int attnum;
|
||||
int sublevels_up;
|
||||
} thisLockWasTriggered_context;
|
||||
|
||||
static bool
|
||||
thisLockWasTriggered_walker(Node *node,
|
||||
thisLockWasTriggered_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Var))
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
|
||||
if (var->varlevelsup == context->sublevels_up &&
|
||||
var->varno == context->varno &&
|
||||
(var->varattno == context->attnum || context->attnum == -1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
/* Recurse into subselects */
|
||||
bool result;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static bool
|
||||
thisLockWasTriggered(int varno,
|
||||
int attnum,
|
||||
Query *parsetree)
|
||||
{
|
||||
thisLockWasTriggered_context context;
|
||||
|
||||
context.varno = varno;
|
||||
context.attnum = attnum;
|
||||
context.sublevels_up = 0;
|
||||
|
||||
return thisLockWasTriggered_walker((Node *) parsetree, &context);
|
||||
}
|
||||
|
||||
/*
|
||||
* matchLocks -
|
||||
* match the list of locks and returns the matching rules
|
||||
*/
|
||||
List *
|
||||
matchLocks(CmdType event,
|
||||
RuleLock *rulelocks,
|
||||
int varno,
|
||||
Query *parsetree)
|
||||
{
|
||||
List *real_locks = NIL;
|
||||
int nlocks;
|
||||
int i;
|
||||
|
||||
Assert(rulelocks != NULL); /* we get called iff there is some lock */
|
||||
Assert(parsetree != NULL);
|
||||
|
||||
if (parsetree->commandType != CMD_SELECT)
|
||||
{
|
||||
if (parsetree->resultRelation != varno)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nlocks = rulelocks->numLocks;
|
||||
|
||||
for (i = 0; i < nlocks; i++)
|
||||
{
|
||||
RewriteRule *oneLock = rulelocks->rules[i];
|
||||
|
||||
if (oneLock->event == event)
|
||||
{
|
||||
if (parsetree->commandType != CMD_SELECT ||
|
||||
thisLockWasTriggered(varno,
|
||||
oneLock->attrno,
|
||||
parsetree))
|
||||
real_locks = lappend(real_locks, oneLock);
|
||||
}
|
||||
}
|
||||
|
||||
checkLockPerms(real_locks, parsetree, varno);
|
||||
|
||||
return real_locks;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check the access permissions of tables that are referred to by a rule.
|
||||
* We want to check the access permissions using the userid of the rule's
|
||||
* owner, *not* of the current user (the one accessing the rule). So, we
|
||||
* do the permission check here and set skipAcl = TRUE in each of the rule's
|
||||
* RTEs, to prevent the executor from running another check with the current
|
||||
* user's ID.
|
||||
*
|
||||
* XXX This routine is called before the rule's query tree has been copied
|
||||
* out of the relcache entry where it is kept. Therefore, when we set
|
||||
* skipAcl = TRUE, we are destructively modifying the relcache entry for
|
||||
* the event relation! This seems fairly harmless because the relcache
|
||||
* querytree is only used as a source for the rewriter, but it's a tad
|
||||
* unclean anyway.
|
||||
*
|
||||
* Note that we must check permissions every time, even if skipAcl was
|
||||
* already set TRUE by a prior call. This ensures that we enforce the
|
||||
* current permission settings for each referenced table, even if they
|
||||
* have changed since the relcache entry was loaded.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Oid evowner;
|
||||
} checkLockPerms_context;
|
||||
|
||||
static bool
|
||||
checkLockPerms_walker(Node *node,
|
||||
checkLockPerms_context *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
Query *qry = (Query *) node;
|
||||
int rtablength = length(qry->rtable);
|
||||
int i;
|
||||
|
||||
/* Check all the RTEs in this query node, except OLD and NEW */
|
||||
for (i = 1; i <= rtablength; i++)
|
||||
{
|
||||
RangeTblEntry *rte = rt_fetch(i, qry->rtable);
|
||||
int32 reqperm;
|
||||
int32 aclcheck_res;
|
||||
|
||||
if (strcmp(rte->eref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->eref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
|
||||
if (i == qry->resultRelation)
|
||||
switch (qry->commandType)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
reqperm = ACL_AP;
|
||||
break;
|
||||
default:
|
||||
reqperm = ACL_WR;
|
||||
break;
|
||||
}
|
||||
else
|
||||
reqperm = ACL_RD;
|
||||
|
||||
aclcheck_res = pg_aclcheck(rte->relname,
|
||||
context->evowner,
|
||||
reqperm);
|
||||
if (aclcheck_res != ACLCHECK_OK)
|
||||
elog(ERROR, "%s: %s",
|
||||
rte->relname,
|
||||
aclcheck_error_strings[aclcheck_res]);
|
||||
|
||||
/*
|
||||
* Mark RTE to prevent executor from checking again with the
|
||||
* current user's ID...
|
||||
*/
|
||||
rte->skipAcl = true;
|
||||
}
|
||||
|
||||
/* If there are sublinks, search for them and check their RTEs */
|
||||
if (qry->hasSubLinks)
|
||||
return query_tree_walker(qry, checkLockPerms_walker,
|
||||
(void *) context);
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, checkLockPerms_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
void
|
||||
checkLockPerms(List *locks, Query *parsetree, int rt_index)
|
||||
{
|
||||
RangeTblEntry *rte;
|
||||
Relation ev_rel;
|
||||
HeapTuple usertup;
|
||||
Form_pg_shadow userform;
|
||||
checkLockPerms_context context;
|
||||
List *l;
|
||||
|
||||
if (locks == NIL)
|
||||
return; /* nothing to check */
|
||||
|
||||
/*
|
||||
* Get the userid of the rule's event relation owner
|
||||
*/
|
||||
rte = rt_fetch(rt_index, parsetree->rtable);
|
||||
ev_rel = heap_openr(rte->relname, AccessShareLock);
|
||||
usertup = SearchSysCacheTuple(SHADOWSYSID,
|
||||
ObjectIdGetDatum(ev_rel->rd_rel->relowner),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(usertup))
|
||||
elog(ERROR, "cache lookup for userid %d failed",
|
||||
ev_rel->rd_rel->relowner);
|
||||
userform = (Form_pg_shadow) GETSTRUCT(usertup);
|
||||
context.evowner = userform->usesysid;
|
||||
heap_close(ev_rel, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Check all the locks that should get fired on this query
|
||||
*/
|
||||
foreach(l, locks)
|
||||
{
|
||||
RewriteRule *onelock = (RewriteRule *) lfirst(l);
|
||||
List *action;
|
||||
|
||||
/*
|
||||
* In each lock check every action. We must scan the action
|
||||
* recursively in case there are any sub-queries within it.
|
||||
*/
|
||||
foreach(action, onelock->actions)
|
||||
{
|
||||
Query *query = (Query *) lfirst(action);
|
||||
|
||||
checkLockPerms_walker((Node *) query, &context);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.52 2000/09/12 20:38:09 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.53 2000/09/29 18:21:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,16 +16,21 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/pg_rewrite.h"
|
||||
#include "commands/view.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "rewrite/rewriteDefine.h"
|
||||
#include "rewrite/rewriteSupport.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "commands/view.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
||||
static void setRuleCheckAsUser(Query *qry, Oid userid);
|
||||
static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
|
||||
|
||||
|
||||
/*
|
||||
@ -52,7 +57,7 @@ InsertRule(char *rulname,
|
||||
Oid rewriteObjectId;
|
||||
|
||||
if (IsDefinedRewriteRule(rulname))
|
||||
elog(ERROR, "Attempt to insert rule '%s' failed: already exists",
|
||||
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
|
||||
rulname);
|
||||
|
||||
/* ----------------
|
||||
@ -217,10 +222,7 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
*/
|
||||
if (event_type == CMD_SELECT)
|
||||
{
|
||||
TargetEntry *tle;
|
||||
Resdom *resdom;
|
||||
Form_pg_attribute attr;
|
||||
char *attname;
|
||||
List *tllist;
|
||||
int i;
|
||||
char *expected_name;
|
||||
|
||||
@ -245,6 +247,10 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
query = (Query *) lfirst(action);
|
||||
if (!is_instead || query->commandType != CMD_SELECT)
|
||||
elog(ERROR, "only instead-select rules currently supported on select");
|
||||
|
||||
/*
|
||||
* ... there can be no rule qual, ...
|
||||
*/
|
||||
if (event_qual != NULL)
|
||||
elog(ERROR, "event qualifications not supported for rules on select");
|
||||
|
||||
@ -252,26 +258,36 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
* ... the targetlist of the SELECT action must exactly match the
|
||||
* event relation, ...
|
||||
*/
|
||||
if (event_relation->rd_att->natts != length(query->targetList))
|
||||
elog(ERROR, "select rules target list must match event relations structure");
|
||||
|
||||
for (i = 1; i <= event_relation->rd_att->natts; i++)
|
||||
i = 0;
|
||||
foreach(tllist, query->targetList)
|
||||
{
|
||||
tle = (TargetEntry *) nth(i - 1, query->targetList);
|
||||
resdom = tle->resdom;
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(tllist);
|
||||
Resdom *resdom = tle->resdom;
|
||||
Form_pg_attribute attr;
|
||||
char *attname;
|
||||
|
||||
if (resdom->resjunk)
|
||||
continue;
|
||||
i++;
|
||||
if (i > event_relation->rd_att->natts)
|
||||
elog(ERROR, "select rule's target list has too many entries");
|
||||
|
||||
attr = event_relation->rd_att->attrs[i - 1];
|
||||
attname = NameStr(attr->attname);
|
||||
|
||||
if (strcmp(resdom->resname, attname) != 0)
|
||||
elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
|
||||
elog(ERROR, "select rule's target entry %d has different column name from %s", i, attname);
|
||||
|
||||
if (attr->atttypid != resdom->restype)
|
||||
elog(ERROR, "select rules target entry %d has different type from attribute %s", i, attname);
|
||||
elog(ERROR, "select rule's target entry %d has different type from attribute %s", i, attname);
|
||||
|
||||
if (attr->atttypmod != resdom->restypmod)
|
||||
elog(ERROR, "select rules target entry %d has different size from attribute %s", i, attname);
|
||||
elog(ERROR, "select rule's target entry %d has different size from attribute %s", i, attname);
|
||||
}
|
||||
|
||||
if (i != event_relation->rd_att->natts)
|
||||
elog(ERROR, "select rule's target list has too few entries");
|
||||
|
||||
/*
|
||||
* ... there must not be another ON SELECT rule already ...
|
||||
*/
|
||||
@ -283,7 +299,7 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
|
||||
rule = event_relation->rd_rules->rules[i];
|
||||
if (rule->event == CMD_SELECT)
|
||||
elog(ERROR, "%s is already a view",
|
||||
elog(ERROR, "\"%s\" is already a view",
|
||||
RelationGetRelationName(event_relation));
|
||||
}
|
||||
}
|
||||
@ -294,25 +310,13 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
if (query->limitOffset != NULL || query->limitCount != NULL)
|
||||
elog(ERROR, "LIMIT clause not supported in views");
|
||||
|
||||
/*
|
||||
* DISTINCT on view is not supported
|
||||
*/
|
||||
if (query->distinctClause != NIL)
|
||||
elog(ERROR, "DISTINCT not supported in views");
|
||||
|
||||
/*
|
||||
* ORDER BY in view is not supported
|
||||
*/
|
||||
if (query->sortClause != NIL)
|
||||
elog(ERROR, "ORDER BY not supported in views");
|
||||
|
||||
/*
|
||||
* ... and finally the rule must be named _RETviewname.
|
||||
*/
|
||||
expected_name = MakeRetrieveViewRuleName(event_obj->relname);
|
||||
if (strcmp(expected_name, stmt->rulename) != 0)
|
||||
{
|
||||
elog(ERROR, "view rule for %s must be named %s",
|
||||
elog(ERROR, "view rule for \"%s\" must be named \"%s\"",
|
||||
event_obj->relname, expected_name);
|
||||
}
|
||||
pfree(expected_name);
|
||||
@ -342,7 +346,7 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* This rule is allowed - install it.
|
||||
* This rule is allowed - prepare to install it.
|
||||
*/
|
||||
if (eslot_string == NULL)
|
||||
{
|
||||
@ -360,13 +364,21 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
eslot_string, event_qual, &action,
|
||||
is_instead, event_attype);
|
||||
|
||||
/*
|
||||
* We want the rule's table references to be checked as though by
|
||||
* the rule owner, not the user referencing the rule. Therefore,
|
||||
* scan through the rule's rtables and set the checkAsUser field
|
||||
* on all rtable entries (except *OLD* and *NEW*).
|
||||
*/
|
||||
foreach(l, action)
|
||||
{
|
||||
query = (Query *) lfirst(l);
|
||||
setRuleCheckAsUser(query, GetUserId());
|
||||
}
|
||||
|
||||
/* discard rule if it's null action and not INSTEAD; it's a no-op */
|
||||
if (action != NIL || is_instead)
|
||||
{
|
||||
Relation relationRelation;
|
||||
HeapTuple tuple;
|
||||
Relation idescs[Num_pg_class_indices];
|
||||
|
||||
event_qualP = nodeToString(event_qual);
|
||||
actionP = nodeToString(action);
|
||||
|
||||
@ -386,34 +398,8 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
* Important side effect: an SI notice is broadcast to force all
|
||||
* backends (including me!) to update relcache entries with the new
|
||||
* rule.
|
||||
*
|
||||
* NOTE : Used to call setRelhasrulesInRelation. The code
|
||||
* was inlined so that two updates were not needed. mhh 31-aug-2000
|
||||
*/
|
||||
|
||||
/*
|
||||
* Find the tuple to update in pg_class, using syscache for the lookup.
|
||||
*/
|
||||
relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
|
||||
tuple = SearchSysCacheTupleCopy(RELOID,
|
||||
ObjectIdGetDatum(ev_relid),
|
||||
0, 0, 0);
|
||||
Assert(HeapTupleIsValid(tuple));
|
||||
|
||||
/* Do the update */
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = true;
|
||||
if (RelisBecomingView)
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
|
||||
|
||||
heap_update(relationRelation, &tuple->t_self, tuple, NULL);
|
||||
|
||||
/* Keep the catalog indices up to date */
|
||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
|
||||
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
|
||||
CatalogCloseIndices(Num_pg_class_indices, idescs);
|
||||
|
||||
heap_freetuple(tuple);
|
||||
heap_close(relationRelation, RowExclusiveLock);
|
||||
SetRelationRuleStatus(ev_relid, true, RelisBecomingView);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -427,3 +413,60 @@ DefineQueryRewrite(RuleStmt *stmt)
|
||||
/* Close rel, but keep lock till commit... */
|
||||
heap_close(event_relation, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* setRuleCheckAsUser
|
||||
* Recursively scan a query and set the checkAsUser field to the
|
||||
* given userid in all rtable entries except *OLD* and *NEW*.
|
||||
*/
|
||||
static void
|
||||
setRuleCheckAsUser(Query *qry, Oid userid)
|
||||
{
|
||||
List *l;
|
||||
|
||||
/* Set all the RTEs in this query node, except OLD and NEW */
|
||||
foreach(l, qry->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
||||
|
||||
if (strcmp(rte->eref->relname, "*NEW*") == 0)
|
||||
continue;
|
||||
if (strcmp(rte->eref->relname, "*OLD*") == 0)
|
||||
continue;
|
||||
|
||||
if (rte->subquery)
|
||||
{
|
||||
/*
|
||||
* Recurse into subquery in FROM
|
||||
*/
|
||||
setRuleCheckAsUser(rte->subquery, userid);
|
||||
}
|
||||
else
|
||||
{
|
||||
rte->checkAsUser = userid;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are sublinks, search for them and process their RTEs */
|
||||
if (qry->hasSubLinks)
|
||||
query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expression-tree walker to find sublink queries
|
||||
*/
|
||||
static bool
|
||||
setRuleCheckAsUser_walker(Node *node, Oid *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
Query *qry = (Query *) node;
|
||||
|
||||
setRuleCheckAsUser(qry, *context);
|
||||
return false;
|
||||
}
|
||||
return expression_tree_walker(node, setRuleCheckAsUser_walker,
|
||||
(void *) context);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.48 2000/09/12 21:07:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.49 2000/09/29 18:21:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -473,8 +473,7 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
|
||||
void
|
||||
AddQual(Query *parsetree, Node *qual)
|
||||
{
|
||||
Node *copy,
|
||||
*old;
|
||||
Node *copy;
|
||||
|
||||
if (qual == NULL)
|
||||
return;
|
||||
@ -482,11 +481,8 @@ AddQual(Query *parsetree, Node *qual)
|
||||
/* INTERSECT want's the original, but we need to copy - Jan */
|
||||
copy = copyObject(qual);
|
||||
|
||||
old = parsetree->qual;
|
||||
if (old == NULL)
|
||||
parsetree->qual = copy;
|
||||
else
|
||||
parsetree->qual = (Node *) make_andclause(makeList(old, copy, -1));
|
||||
parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
|
||||
copy);
|
||||
|
||||
/*
|
||||
* Make sure query is marked correctly if added qual has sublinks or
|
||||
@ -504,8 +500,7 @@ AddQual(Query *parsetree, Node *qual)
|
||||
void
|
||||
AddHavingQual(Query *parsetree, Node *havingQual)
|
||||
{
|
||||
Node *copy,
|
||||
*old;
|
||||
Node *copy;
|
||||
|
||||
if (havingQual == NULL)
|
||||
return;
|
||||
@ -513,11 +508,8 @@ AddHavingQual(Query *parsetree, Node *havingQual)
|
||||
/* INTERSECT want's the original, but we need to copy - Jan */
|
||||
copy = copyObject(havingQual);
|
||||
|
||||
old = parsetree->havingQual;
|
||||
if (old == NULL)
|
||||
parsetree->havingQual = copy;
|
||||
else
|
||||
parsetree->havingQual = (Node *) make_andclause(makeList(old, copy, -1));
|
||||
parsetree->havingQual = make_and_qual(parsetree->havingQual,
|
||||
copy);
|
||||
|
||||
/*
|
||||
* Make sure query is marked correctly if added qual has sublinks or
|
||||
@ -560,45 +552,7 @@ AddNotQual(Query *parsetree, Node *qual)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add all expressions used by the given GroupClause list to the
|
||||
* parsetree's targetlist and groupclause list.
|
||||
*
|
||||
* tlist is the old targetlist associated with the input groupclauses.
|
||||
*
|
||||
* XXX shouldn't we be checking to see if there are already matching
|
||||
* entries in parsetree->targetlist?
|
||||
*/
|
||||
void
|
||||
AddGroupClause(Query *parsetree, List *group_by, List *tlist)
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, group_by)
|
||||
{
|
||||
GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
|
||||
TargetEntry *tle = get_sortgroupclause_tle(groupclause, tlist);
|
||||
|
||||
/* copy the groupclause's TLE from the old tlist */
|
||||
tle = (TargetEntry *) copyObject(tle);
|
||||
|
||||
/*
|
||||
* The ressortgroupref number in the old tlist might be already
|
||||
* taken in the new tlist, so force assignment of a new number.
|
||||
*/
|
||||
tle->resdom->ressortgroupref = 0;
|
||||
groupclause->tleSortGroupRef =
|
||||
assignSortGroupRef(tle, parsetree->targetList);
|
||||
|
||||
/* Also need to set the resno and mark it resjunk. */
|
||||
tle->resdom->resno = length(parsetree->targetList) + 1;
|
||||
tle->resdom->resjunk = true;
|
||||
|
||||
parsetree->targetList = lappend(parsetree->targetList, tle);
|
||||
parsetree->groupClause = lappend(parsetree->groupClause, groupclause);
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a NULL constant expression of the given type */
|
||||
static Node *
|
||||
make_null(Oid type)
|
||||
{
|
||||
@ -612,28 +566,6 @@ make_null(Oid type)
|
||||
return (Node *) c;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
FixResdomTypes(List *tlist)
|
||||
{
|
||||
List *i;
|
||||
|
||||
foreach(i, tlist)
|
||||
{
|
||||
TargetEntry *tle = lfirst(i);
|
||||
|
||||
if (nodeTag(tle->expr) == T_Var)
|
||||
{
|
||||
Var *var = (Var *) tle->expr;
|
||||
|
||||
tle->resdom->restype = var->vartype;
|
||||
tle->resdom->restypmod = var->vartypmod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Find a targetlist entry by resno */
|
||||
static Node *
|
||||
FindMatchingNew(List *tlist, int attno)
|
||||
@ -650,6 +582,8 @@ FindMatchingNew(List *tlist, int attno)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
/* Find a targetlist entry by resname */
|
||||
static Node *
|
||||
FindMatchingTLEntry(List *tlist, char *e_attname)
|
||||
@ -662,25 +596,31 @@ FindMatchingTLEntry(List *tlist, char *e_attname)
|
||||
char *resname;
|
||||
|
||||
resname = tle->resdom->resname;
|
||||
if (!strcmp(e_attname, resname))
|
||||
if (strcmp(e_attname, resname) == 0)
|
||||
return tle->expr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* ResolveNew - replace Vars with corresponding items from a targetlist
|
||||
*
|
||||
* Vars matching info->new_varno and sublevels_up are replaced by the
|
||||
* Vars matching target_varno and sublevels_up are replaced by the
|
||||
* entry with matching resno from targetlist, if there is one.
|
||||
* If not, we either change the unmatched Var's varno to update_varno
|
||||
* (when event == CMD_UPDATE) or replace it with a constant NULL.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
RewriteInfo *info;
|
||||
List *targetlist;
|
||||
int target_varno;
|
||||
int sublevels_up;
|
||||
List *targetlist;
|
||||
int event;
|
||||
int update_varno;
|
||||
} ResolveNew_context;
|
||||
|
||||
static Node *
|
||||
@ -694,7 +634,7 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
int this_varno = (int) var->varno;
|
||||
int this_varlevelsup = (int) var->varlevelsup;
|
||||
|
||||
if (this_varno == context->info->new_varno &&
|
||||
if (this_varno == context->target_varno &&
|
||||
this_varlevelsup == context->sublevels_up)
|
||||
{
|
||||
Node *n = FindMatchingNew(context->targetlist,
|
||||
@ -702,13 +642,13 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
|
||||
if (n == NULL)
|
||||
{
|
||||
if (context->info->event == CMD_UPDATE)
|
||||
if (context->event == CMD_UPDATE)
|
||||
{
|
||||
/* For update, just change unmatched var's varno */
|
||||
n = copyObject(node);
|
||||
((Var *) n)->varno = context->info->current_varno;
|
||||
((Var *) n)->varnoold = context->info->current_varno;
|
||||
return n;
|
||||
var = (Var *) copyObject(node);
|
||||
var->varno = context->update_varno;
|
||||
var->varnoold = context->update_varno;
|
||||
return (Node *) var;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -755,54 +695,68 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
|
||||
FLATCOPY(newnode, query, Query);
|
||||
MUTATE(newnode->targetList, query->targetList, List *,
|
||||
ResolveNew_mutator, context);
|
||||
MUTATE(newnode->qual, query->qual, Node *,
|
||||
MUTATE(newnode->jointree, query->jointree, FromExpr *,
|
||||
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,
|
||||
(void *) context);
|
||||
}
|
||||
|
||||
static Node *
|
||||
ResolveNew(Node *node, RewriteInfo *info, List *targetlist,
|
||||
int sublevels_up)
|
||||
Node *
|
||||
ResolveNew(Node *node, int target_varno, int sublevels_up,
|
||||
List *targetlist, int event, int update_varno)
|
||||
{
|
||||
ResolveNew_context context;
|
||||
|
||||
context.info = info;
|
||||
context.targetlist = targetlist;
|
||||
context.target_varno = target_varno;
|
||||
context.sublevels_up = sublevels_up;
|
||||
context.targetlist = targetlist;
|
||||
context.event = event;
|
||||
context.update_varno = update_varno;
|
||||
|
||||
/*
|
||||
* Note: if an entire Query is passed, the right things will happen,
|
||||
* because ResolveNew_mutator increments sublevels_up when it sees
|
||||
* a SubLink, not a Query.
|
||||
*/
|
||||
return ResolveNew_mutator(node, &context);
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternate interface to ResolveNew: substitute Vars in info->rule_action
|
||||
* with targetlist items from the parsetree's targetlist.
|
||||
*/
|
||||
void
|
||||
FixNew(RewriteInfo *info, Query *parsetree)
|
||||
{
|
||||
ResolveNew_context context;
|
||||
|
||||
context.target_varno = info->new_varno;
|
||||
context.sublevels_up = 0;
|
||||
context.targetlist = parsetree->targetList;
|
||||
context.event = info->event;
|
||||
context.update_varno = info->current_varno;
|
||||
|
||||
info->rule_action->targetList = (List *)
|
||||
ResolveNew((Node *) info->rule_action->targetList,
|
||||
info, parsetree->targetList, 0);
|
||||
info->rule_action->qual = ResolveNew(info->rule_action->qual,
|
||||
info, parsetree->targetList, 0);
|
||||
info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual,
|
||||
info, parsetree->targetList, 0);
|
||||
info->rule_action->jointree = (List *)
|
||||
ResolveNew((Node *) info->rule_action->jointree,
|
||||
info, parsetree->targetList, 0);
|
||||
ResolveNew_mutator((Node *) info->rule_action->targetList, &context);
|
||||
info->rule_action->jointree = (FromExpr *)
|
||||
ResolveNew_mutator((Node *) info->rule_action->jointree, &context);
|
||||
info->rule_action->havingQual =
|
||||
ResolveNew_mutator(info->rule_action->havingQual, &context);
|
||||
}
|
||||
|
||||
|
||||
#ifdef NOT_USED
|
||||
|
||||
/*
|
||||
* HandleRIRAttributeRule
|
||||
* Replace Vars matching a given RT index with copies of TL expressions.
|
||||
*
|
||||
* Handles 'on retrieve to relation.attribute
|
||||
* do instead retrieve (attribute = expression) w/qual'
|
||||
*
|
||||
* XXX Why is this not unified with apply_RIR_view()?
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
@ -897,12 +851,12 @@ HandleRIRAttributeRule_mutator(Node *node,
|
||||
FLATCOPY(newnode, query, Query);
|
||||
MUTATE(newnode->targetList, query->targetList, List *,
|
||||
HandleRIRAttributeRule_mutator, context);
|
||||
MUTATE(newnode->qual, query->qual, Node *,
|
||||
MUTATE(newnode->jointree, query->jointree, FromExpr *,
|
||||
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,
|
||||
@ -931,13 +885,12 @@ HandleRIRAttributeRule(Query *parsetree,
|
||||
parsetree->targetList = (List *)
|
||||
HandleRIRAttributeRule_mutator((Node *) parsetree->targetList,
|
||||
&context);
|
||||
parsetree->qual =
|
||||
HandleRIRAttributeRule_mutator(parsetree->qual,
|
||||
parsetree->jointree = (FromExpr *)
|
||||
HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
|
||||
&context);
|
||||
parsetree->havingQual =
|
||||
HandleRIRAttributeRule_mutator(parsetree->havingQual,
|
||||
&context);
|
||||
parsetree->jointree = (List *)
|
||||
HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
|
||||
&context);
|
||||
}
|
||||
|
||||
#endif /* NOT_USED */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.39 2000/09/12 04:49:09 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.40 2000/09/29 18:21:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -40,14 +40,14 @@ RewriteGetRuleEventRel(char *rulename)
|
||||
PointerGetDatum(rulename),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(htup))
|
||||
elog(ERROR, "Rule or view '%s' not found",
|
||||
((!strncmp(rulename, "_RET", 4)) ? (rulename + 4) : rulename));
|
||||
elog(ERROR, "Rule or view \"%s\" not found",
|
||||
((strncmp(rulename, "_RET", 4) == 0) ? (rulename + 4) : rulename));
|
||||
eventrel = ((Form_pg_rewrite) GETSTRUCT(htup))->ev_class;
|
||||
htup = SearchSysCacheTuple(RELOID,
|
||||
PointerGetDatum(eventrel),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(htup))
|
||||
elog(ERROR, "Class '%u' not found", eventrel);
|
||||
elog(ERROR, "Relation %u not found", eventrel);
|
||||
|
||||
return NameStr(((Form_pg_class) GETSTRUCT(htup))->relname);
|
||||
}
|
||||
@ -85,7 +85,7 @@ RemoveRewriteRule(char *ruleName)
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
heap_close(RewriteRelation, RowExclusiveLock);
|
||||
elog(ERROR, "Rule '%s' not found\n", ruleName);
|
||||
elog(ERROR, "Rule \"%s\" not found", ruleName);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -105,7 +105,7 @@ RemoveRewriteRule(char *ruleName)
|
||||
|
||||
/* do not allow the removal of a view's SELECT rule */
|
||||
if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
|
||||
((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' )
|
||||
((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' )
|
||||
elog(ERROR, "Cannot remove a view's SELECT rule");
|
||||
|
||||
hasMoreRules = event_relation->rd_rules != NULL &&
|
||||
@ -133,7 +133,7 @@ RemoveRewriteRule(char *ruleName)
|
||||
* new rule set. Therefore, must do this even if relhasrules is
|
||||
* still true!
|
||||
*/
|
||||
setRelhasrulesInRelation(eventRelationOid, hasMoreRules);
|
||||
SetRelationRuleStatus(eventRelationOid, hasMoreRules, false);
|
||||
|
||||
/* Close rel, but keep lock till commit... */
|
||||
heap_close(event_relation, NoLock);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.43 2000/06/30 07:04:23 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.44 2000/09/29 18:21:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -34,10 +34,11 @@ IsDefinedRewriteRule(char *ruleName)
|
||||
}
|
||||
|
||||
/*
|
||||
* setRelhasrulesInRelation
|
||||
* Set the value of the relation's relhasrules field in pg_class.
|
||||
* SetRelationRuleStatus
|
||||
* Set the value of the relation's relhasrules field in pg_class;
|
||||
* if the relation is becoming a view, also adjust its relkind.
|
||||
*
|
||||
* NOTE: caller should be holding an appropriate lock on the relation.
|
||||
* NOTE: caller must be holding an appropriate lock on the relation.
|
||||
*
|
||||
* NOTE: an important side-effect of this operation is that an SI invalidation
|
||||
* message is sent out to all backends --- including me --- causing relcache
|
||||
@ -47,7 +48,8 @@ IsDefinedRewriteRule(char *ruleName)
|
||||
* an SI message in that case.
|
||||
*/
|
||||
void
|
||||
setRelhasrulesInRelation(Oid relationId, bool relhasrules)
|
||||
SetRelationRuleStatus(Oid relationId, bool relHasRules,
|
||||
bool relIsBecomingView)
|
||||
{
|
||||
Relation relationRelation;
|
||||
HeapTuple tuple;
|
||||
@ -63,7 +65,10 @@ setRelhasrulesInRelation(Oid relationId, bool relhasrules)
|
||||
Assert(HeapTupleIsValid(tuple));
|
||||
|
||||
/* Do the update */
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relhasrules;
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
|
||||
if (relIsBecomingView)
|
||||
((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
|
||||
|
||||
heap_update(relationRelation, &tuple->t_self, tuple, NULL);
|
||||
|
||||
/* Keep the catalog indices up to date */
|
||||
|
@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.63 2000/09/25 18:14:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.64 2000/09/29 18:21:37 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -714,7 +714,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
|
||||
query = (Query *) lfirst(actions);
|
||||
|
||||
context.buf = buf;
|
||||
context.rangetables = lcons(query->rtable, NIL);
|
||||
context.rangetables = makeList1(query->rtable);
|
||||
context.varprefix = (length(query->rtable) != 1);
|
||||
|
||||
get_rule_expr(qual, &context);
|
||||
@ -892,6 +892,9 @@ get_select_query_def(Query *query, deparse_context *context)
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
bool tell_as = false;
|
||||
|
||||
if (tle->resdom->resjunk)
|
||||
continue; /* ignore junk entries */
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
|
||||
@ -922,10 +925,10 @@ get_select_query_def(Query *query, deparse_context *context)
|
||||
get_from_clause(query, context);
|
||||
|
||||
/* Add the WHERE clause if given */
|
||||
if (query->qual != NULL)
|
||||
if (query->jointree->quals != NULL)
|
||||
{
|
||||
appendStringInfo(buf, " WHERE ");
|
||||
get_rule_expr(query->qual, context);
|
||||
get_rule_expr(query->jointree->quals, context);
|
||||
}
|
||||
|
||||
/* Add the GROUP BY CLAUSE */
|
||||
@ -999,6 +1002,9 @@ get_insert_query_def(Query *query, deparse_context *context)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
|
||||
if (tle->resdom->resjunk)
|
||||
continue; /* ignore junk entries */
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname));
|
||||
@ -1006,7 +1012,7 @@ get_insert_query_def(Query *query, deparse_context *context)
|
||||
appendStringInfo(buf, ") ");
|
||||
|
||||
/* Add the VALUES or the SELECT */
|
||||
if (rt_constonly && query->qual == NULL)
|
||||
if (rt_constonly && query->jointree->quals == NULL)
|
||||
{
|
||||
appendStringInfo(buf, "VALUES (");
|
||||
sep = "";
|
||||
@ -1014,6 +1020,9 @@ get_insert_query_def(Query *query, deparse_context *context)
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
|
||||
if (tle->resdom->resjunk)
|
||||
continue; /* ignore junk entries */
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
get_tle_expr(tle, context);
|
||||
@ -1034,7 +1043,6 @@ get_update_query_def(Query *query, deparse_context *context)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
char *sep;
|
||||
TargetEntry *tle;
|
||||
RangeTblEntry *rte;
|
||||
List *l;
|
||||
|
||||
@ -1051,7 +1059,10 @@ get_update_query_def(Query *query, deparse_context *context)
|
||||
sep = "";
|
||||
foreach(l, query->targetList)
|
||||
{
|
||||
tle = (TargetEntry *) lfirst(l);
|
||||
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
||||
|
||||
if (tle->resdom->resjunk)
|
||||
continue; /* ignore junk entries */
|
||||
|
||||
appendStringInfo(buf, sep);
|
||||
sep = ", ";
|
||||
@ -1070,10 +1081,10 @@ get_update_query_def(Query *query, deparse_context *context)
|
||||
get_from_clause(query, context);
|
||||
|
||||
/* Finally add a WHERE clause if given */
|
||||
if (query->qual != NULL)
|
||||
if (query->jointree->quals != NULL)
|
||||
{
|
||||
appendStringInfo(buf, " WHERE ");
|
||||
get_rule_expr(query->qual, context);
|
||||
get_rule_expr(query->jointree->quals, context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1098,10 +1109,10 @@ get_delete_query_def(Query *query, deparse_context *context)
|
||||
quote_identifier(rte->relname));
|
||||
|
||||
/* Add a WHERE clause if given */
|
||||
if (query->qual != NULL)
|
||||
if (query->jointree->quals != NULL)
|
||||
{
|
||||
appendStringInfo(buf, " WHERE ");
|
||||
get_rule_expr(query->qual, context);
|
||||
get_rule_expr(query->jointree->quals, context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1747,11 +1758,13 @@ get_from_clause(Query *query, deparse_context *context)
|
||||
/*
|
||||
* 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.
|
||||
* (These can only appear at the top level of the jointree, so it's
|
||||
* sufficient to check here.)
|
||||
* Also ignore the rule pseudo-RTEs for NEW and OLD.
|
||||
*/
|
||||
sep = " FROM ";
|
||||
|
||||
foreach(l, query->jointree)
|
||||
foreach(l, query->jointree->fromlist)
|
||||
{
|
||||
Node *jtnode = (Node *) lfirst(l);
|
||||
|
||||
@ -1784,9 +1797,21 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
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->relname)
|
||||
{
|
||||
/* Normal relation RTE */
|
||||
appendStringInfo(buf, "%s%s",
|
||||
only_marker(rte),
|
||||
quote_identifier(rte->relname));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Subquery RTE */
|
||||
Assert(rte->subquery != NULL);
|
||||
appendStringInfoChar(buf, '(');
|
||||
get_query_def(rte->subquery, buf, NIL);
|
||||
appendStringInfoChar(buf, ')');
|
||||
}
|
||||
if (rte->alias != NULL)
|
||||
{
|
||||
appendStringInfo(buf, " %s",
|
||||
|
@ -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.47 2000/09/19 18:18:01 petere Exp $
|
||||
* $Id: catversion.h,v 1.48 2000/09/29 18:21:37 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200009191
|
||||
#define CATALOG_VERSION_NO 200009281
|
||||
|
||||
#endif
|
||||
|
25
src/include/executor/nodeSubqueryscan.h
Normal file
25
src/include/executor/nodeSubqueryscan.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSubqueryscan.h
|
||||
*
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeSubqueryscan.h,v 1.1 2000/09/29 18:21:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODESUBQUERYSCAN_H
|
||||
#define NODESUBQUERYSCAN_H
|
||||
|
||||
#include "nodes/plannodes.h"
|
||||
|
||||
extern TupleTableSlot *ExecSubqueryScan(SubqueryScan *node);
|
||||
extern void ExecEndSubqueryScan(SubqueryScan *node);
|
||||
extern bool ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsSubqueryScan(SubqueryScan *node);
|
||||
extern void ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent);
|
||||
|
||||
#endif /* NODESUBQUERYSCAN_H */
|
@ -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.49 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: execnodes.h,v 1.50 2000/09/29 18:21:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -453,6 +453,24 @@ typedef struct TidScanState
|
||||
HeapTupleData tss_htup;
|
||||
} TidScanState;
|
||||
|
||||
/* ----------------
|
||||
* SubqueryScanState information
|
||||
*
|
||||
* SubqueryScanState is used for scanning a sub-query in the range table.
|
||||
* The sub-query will have its own EState, which we save here.
|
||||
* ScanTupleSlot references the current output tuple of the sub-query.
|
||||
*
|
||||
* SubQueryDesc queryDesc for sub-query
|
||||
* SubEState exec state for sub-query
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct SubqueryScanState
|
||||
{
|
||||
CommonScanState csstate; /* its first field is NodeTag */
|
||||
struct QueryDesc *sss_SubQueryDesc;
|
||||
EState *sss_SubEState;
|
||||
} SubqueryScanState;
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Join State Information
|
||||
* ----------------------------------------------------------------
|
||||
|
@ -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.76 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: nodes.h,v 1.77 2000/09/29 18:21:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -49,6 +49,7 @@ typedef enum NodeTag
|
||||
T_Group,
|
||||
T_SubPlan,
|
||||
T_TidScan,
|
||||
T_SubqueryScan,
|
||||
|
||||
/*---------------------
|
||||
* TAGS FOR PRIMITIVE NODES (primnodes.h)
|
||||
@ -69,6 +70,7 @@ typedef enum NodeTag
|
||||
T_Iter,
|
||||
T_RelabelType,
|
||||
T_RangeTblRef,
|
||||
T_FromExpr,
|
||||
T_JoinExpr,
|
||||
|
||||
/*---------------------
|
||||
@ -118,6 +120,7 @@ typedef enum NodeTag
|
||||
T_UniqueState,
|
||||
T_HashState,
|
||||
T_TidScanState,
|
||||
T_SubqueryScanState,
|
||||
|
||||
/*---------------------
|
||||
* TAGS FOR MEMORY NODES (memnodes.h)
|
||||
@ -222,7 +225,7 @@ typedef enum NodeTag
|
||||
T_oldJoinExprXXX, /* not used anymore; this tag# is available */
|
||||
T_CaseExpr,
|
||||
T_CaseWhen,
|
||||
T_RowMark,
|
||||
T_RowMarkXXX, /* not used anymore; this tag# is available */
|
||||
T_FkConstraint,
|
||||
|
||||
/*---------------------
|
||||
|
@ -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.113 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: parsenodes.h,v 1.114 2000/09/29 18:21:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -50,11 +50,12 @@ typedef struct Query
|
||||
bool hasSubLinks; /* has subquery SubLink */
|
||||
|
||||
List *rtable; /* list of range table entries */
|
||||
List *jointree; /* table join tree (from the FROM clause) */
|
||||
FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */
|
||||
|
||||
List *targetList; /* target list (of TargetEntry) */
|
||||
Node *qual; /* qualifications applied to tuples */
|
||||
List *rowMark; /* list of RowMark entries */
|
||||
|
||||
List *rowMarks; /* integer list of RT indexes of relations
|
||||
* that are selected FOR UPDATE */
|
||||
|
||||
List *distinctClause; /* a list of SortClause's */
|
||||
|
||||
@ -1087,7 +1088,7 @@ typedef struct RangeSubselect
|
||||
{
|
||||
NodeTag type;
|
||||
Node *subquery; /* the untransformed sub-select clause */
|
||||
Attr *name; /* optional table alias & column aliases */
|
||||
Attr *name; /* table alias & optional column aliases */
|
||||
} RangeSubselect;
|
||||
|
||||
/*
|
||||
@ -1141,15 +1142,22 @@ typedef struct TargetEntry
|
||||
* RangeTblEntry -
|
||||
* A range table is a List of RangeTblEntry nodes.
|
||||
*
|
||||
* Some of the following are only used in one of
|
||||
* the parsing, optimizing, execution stages.
|
||||
* Currently we use the same node type for both plain relation references
|
||||
* and sub-selects in the FROM clause. It might be cleaner to abstract
|
||||
* the common fields into a "superclass" nodetype.
|
||||
*
|
||||
* 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.
|
||||
* real or aliases). Note that system columns (OID etc) are not included
|
||||
* in the column list.
|
||||
* eref->relname is required to be present, and should generally be used
|
||||
* to identify the RTE for error messages etc.
|
||||
*
|
||||
* inh is TRUE for relation references that should be expanded to include
|
||||
* inheritance children, if the rel has any. This *must* be FALSE for
|
||||
* subquery RTEs.
|
||||
*
|
||||
* 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
|
||||
@ -1160,18 +1168,37 @@ 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 '*'.
|
||||
*
|
||||
* checkForRead, checkForWrite, and checkAsUser control run-time access
|
||||
* permissions checks. A rel will be checked for read or write access
|
||||
* (or both, or neither) per checkForRead and checkForWrite. If
|
||||
* checkAsUser is not InvalidOid, then do the permissions checks using
|
||||
* the access rights of that user, not the current effective user ID.
|
||||
* (This allows rules to act as setuid gateways.)
|
||||
*--------------------
|
||||
*/
|
||||
typedef struct RangeTblEntry
|
||||
{
|
||||
NodeTag type;
|
||||
/*
|
||||
* Fields valid for a plain relation RTE (else NULL/zero):
|
||||
*/
|
||||
char *relname; /* real name of the relation */
|
||||
Oid relid; /* OID of the relation */
|
||||
/*
|
||||
* Fields valid for a subquery RTE (else NULL):
|
||||
*/
|
||||
Query *subquery; /* the sub-query */
|
||||
/*
|
||||
* Fields valid in all RTEs:
|
||||
*/
|
||||
Attr *alias; /* user-written alias clause, if any */
|
||||
Attr *eref; /* expanded reference names */
|
||||
bool inh; /* inheritance requested? */
|
||||
bool inFromCl; /* present in FROM clause */
|
||||
bool skipAcl; /* skip ACL check in executor */
|
||||
bool checkForRead; /* check rel for read access */
|
||||
bool checkForWrite; /* check rel for write access */
|
||||
Oid checkAsUser; /* if not zero, check access as this user */
|
||||
} RangeTblEntry;
|
||||
|
||||
/*
|
||||
@ -1206,14 +1233,4 @@ typedef struct SortClause
|
||||
*/
|
||||
typedef SortClause GroupClause;
|
||||
|
||||
#define ROW_MARK_FOR_UPDATE (1 << 0)
|
||||
#define ROW_ACL_FOR_UPDATE (1 << 1)
|
||||
|
||||
typedef struct RowMark
|
||||
{
|
||||
NodeTag type;
|
||||
Index rti; /* index in Query->rtable */
|
||||
bits8 info; /* as above */
|
||||
} RowMark;
|
||||
|
||||
#endif /* PARSENODES_H */
|
||||
|
@ -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.19 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: pg_list.h,v 1.20 2000/09/29 18:21:39 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -92,6 +92,18 @@ typedef struct List
|
||||
#define foreach(_elt_,_list_) \
|
||||
for(_elt_=(_list_); _elt_!=NIL; _elt_=lnext(_elt_))
|
||||
|
||||
/*
|
||||
* Convenience macros for building fixed-length lists
|
||||
*/
|
||||
#define makeList1(x1) lcons(x1, NIL)
|
||||
#define makeList2(x1,x2) lcons(x1, makeList1(x2))
|
||||
#define makeList3(x1,x2,x3) lcons(x1, makeList2(x2,x3))
|
||||
#define makeList4(x1,x2,x3,x4) lcons(x1, makeList3(x2,x3,x4))
|
||||
|
||||
#define makeListi1(x1) lconsi(x1, NIL)
|
||||
#define makeListi2(x1,x2) lconsi(x1, makeListi1(x2))
|
||||
#define makeListi3(x1,x2,x3) lconsi(x1, makeListi2(x2,x3))
|
||||
#define makeListi4(x1,x2,x3,x4) lconsi(x1, makeListi3(x2,x3,x4))
|
||||
|
||||
/*
|
||||
* function prototypes in nodes/list.c
|
||||
@ -106,11 +118,11 @@ extern bool intMember(int datum, List *list);
|
||||
extern Value *makeInteger(long i);
|
||||
extern Value *makeFloat(char *numericStr);
|
||||
extern Value *makeString(char *str);
|
||||
extern List *makeList(void *elem,...);
|
||||
extern List *lappend(List *list, void *datum);
|
||||
extern List *lappendi(List *list, int datum);
|
||||
extern List *lremove(void *elem, List *list);
|
||||
extern List *LispRemove(void *elem, List *list);
|
||||
extern List *lremovei(int elem, List *list);
|
||||
extern List *ltruncate(int n, List *list);
|
||||
|
||||
extern void *nth(int n, List *l);
|
||||
@ -120,8 +132,8 @@ extern void set_nth(List *l, int n, void *elem);
|
||||
extern List *set_difference(List *list1, List *list2);
|
||||
extern List *set_differencei(List *list1, List *list2);
|
||||
extern List *lreverse(List *l);
|
||||
extern List *LispUnion(List *list1, List *list2);
|
||||
extern List *LispUnioni(List *list1, List *list2);
|
||||
extern List *set_union(List *list1, List *list2);
|
||||
extern List *set_unioni(List *list1, List *list2);
|
||||
|
||||
extern bool sameseti(List *list1, List *list2);
|
||||
extern bool nonoverlap_setsi(List *list1, List *list2);
|
||||
|
@ -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.42 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: plannodes.h,v 1.43 2000/09/29 18:21:39 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -31,6 +31,7 @@
|
||||
*
|
||||
* Scan *** CommonScanState scanstate;
|
||||
* IndexScan IndexScanState indxstate;
|
||||
* SubqueryScan SubqueryScanState subquerystate;
|
||||
*
|
||||
* (*** nodes which inherit Scan also inherit scanstate)
|
||||
*
|
||||
@ -202,6 +203,26 @@ typedef struct TidScan
|
||||
TidScanState *tidstate;
|
||||
} TidScan;
|
||||
|
||||
/* ----------------
|
||||
* subquery scan node
|
||||
*
|
||||
* SubqueryScan is for scanning the output of a sub-query in the range table.
|
||||
* We need a special plan node above the sub-query's plan as a place to switch
|
||||
* execution contexts. Although we are not scanning a physical relation,
|
||||
* we make this a descendant of Scan anyway for code-sharing purposes.
|
||||
*
|
||||
* Note: we store the sub-plan in the type-specific subplan field, not in
|
||||
* the generic lefttree field as you might expect. This is because we do
|
||||
* not want plan-tree-traversal routines to recurse into the subplan without
|
||||
* knowing that they are changing Query contexts.
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct SubqueryScan
|
||||
{
|
||||
Scan scan;
|
||||
Plan *subplan;
|
||||
} SubqueryScan;
|
||||
|
||||
/*
|
||||
* ==========
|
||||
* Join nodes
|
||||
|
@ -10,7 +10,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: primnodes.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: primnodes.h,v 1.49 2000/09/29 18:21:39 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -495,24 +495,32 @@ typedef struct RelabelType
|
||||
*
|
||||
* 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).
|
||||
* or qualified join. Also, FromExpr nodes can appear to denote an
|
||||
* ordinary cross-product join ("FROM foo, bar, baz WHERE ...").
|
||||
* FromExpr is like a JoinExpr of jointype JOIN_INNER, except that it
|
||||
* may have any number of child nodes, not just two. Also, there is an
|
||||
* implementation-defined difference: the planner is allowed to join the
|
||||
* children of a FromExpr using whatever join order seems good to it.
|
||||
* At present, JoinExpr nodes are always joined in exactly the order
|
||||
* implied by the jointree 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 top level of a Query's jointree is always a FromExpr.
|
||||
* Even if the jointree contains no rels, there will be a FromExpr.
|
||||
*
|
||||
* 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.
|
||||
* *in addition to* the query's main WHERE clause, which appears as the
|
||||
* qual of the top-level FromExpr. The reason for associating quals with
|
||||
* specific nodes in the jointree is that the position of a qual is critical
|
||||
* when outer joins are present. (If we enforce a qual too soon or too late,
|
||||
* that may cause the outer join to produce the wrong set of NULL-extended
|
||||
* rows.) If all joins are inner joins then all the qual positions are
|
||||
* semantically 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.
|
||||
* during the parse analysis phase. Also, the top-level FromExpr is added
|
||||
* during parse analysis; the grammar regards FROM and WHERE as separate.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@ -561,4 +569,20 @@ typedef struct JoinExpr
|
||||
List *colvars; /* output column nodes (list of expressions) */
|
||||
} JoinExpr;
|
||||
|
||||
/*----------
|
||||
* FromExpr - represents a FROM ... WHERE ... construct
|
||||
*
|
||||
* This is both more flexible than a JoinExpr (it can have any number of
|
||||
* children, including zero) and less so --- we don't need to deal with
|
||||
* aliases and so on. The output column set is implicitly just the union
|
||||
* of the outputs of the children.
|
||||
*----------
|
||||
*/
|
||||
typedef struct FromExpr
|
||||
{
|
||||
NodeTag type;
|
||||
List *fromlist; /* List of join subtrees */
|
||||
Node *quals; /* qualifiers on join, if any */
|
||||
} FromExpr;
|
||||
|
||||
#endif /* PRIMNODES_H */
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: relation.h,v 1.48 2000/09/12 21:07:10 tgl Exp $
|
||||
* $Id: relation.h,v 1.49 2000/09/29 18:21:39 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -35,10 +35,20 @@ typedef enum CostSelector
|
||||
STARTUP_COST, TOTAL_COST
|
||||
} CostSelector;
|
||||
|
||||
/*
|
||||
/*----------
|
||||
* RelOptInfo
|
||||
* Per-relation information for planning/optimization
|
||||
*
|
||||
* For planning purposes, a "base rel" is either a plain relation (a
|
||||
* table) or the output of a sub-SELECT that appears in the range table.
|
||||
* In either case it is uniquely identified by an RT index. A "joinrel"
|
||||
* is the joining of two or more base rels. A joinrel is identified by
|
||||
* the set of RT indexes for its component baserels.
|
||||
*
|
||||
* Note that there is only one joinrel for any given set of component
|
||||
* baserels, no matter what order we assemble them in; so an unordered
|
||||
* set is the right datatype to identify it with.
|
||||
*
|
||||
* Parts of this data structure are specific to various scan and join
|
||||
* mechanisms. It didn't seem worth creating new node types for them.
|
||||
*
|
||||
@ -61,9 +71,16 @@ typedef enum CostSelector
|
||||
*
|
||||
* * If the relation is a base relation it will have these fields set:
|
||||
*
|
||||
* indexed - true if the relation has secondary indices
|
||||
* pages - number of disk pages in relation
|
||||
* issubquery - true if baserel is a subquery RTE rather than a table
|
||||
* indexed - true if the relation has secondary indices (always false
|
||||
* if it's a subquery)
|
||||
* pages - number of disk pages in relation (zero if a subquery)
|
||||
* tuples - number of tuples in relation (not considering restrictions)
|
||||
* subplan - plan for subquery (NULL if it's a plain table)
|
||||
*
|
||||
* Note: for a subquery, tuples and subplan are not set immediately
|
||||
* upon creation of the RelOptInfo object; they are filled in when
|
||||
* set_base_rel_pathlist processes the object.
|
||||
*
|
||||
* * The presence of the remaining fields depends on the restrictions
|
||||
* and joins that the relation participates in:
|
||||
@ -101,6 +118,7 @@ typedef enum CostSelector
|
||||
* 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
|
||||
@ -122,10 +140,12 @@ typedef struct RelOptInfo
|
||||
struct Path *cheapest_total_path;
|
||||
bool pruneable;
|
||||
|
||||
/* statistics from pg_class (only valid if it's a base rel!) */
|
||||
/* information about a base rel (not set for join rels!) */
|
||||
bool issubquery;
|
||||
bool indexed;
|
||||
long pages;
|
||||
double tuples;
|
||||
struct Plan *subplan;
|
||||
|
||||
/* used by various scans and joins: */
|
||||
List *baserestrictinfo; /* RestrictInfo structures (if
|
||||
@ -272,7 +292,8 @@ typedef struct Path
|
||||
* 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.
|
||||
* This flag is TRUE iff all the indexquals came from non-pushed-down
|
||||
* JOIN/ON conditions, which means the path is safe to use for an outer join.
|
||||
*
|
||||
* '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
|
||||
@ -375,10 +396,10 @@ typedef struct HashPath
|
||||
* Restriction clause info.
|
||||
*
|
||||
* We create one of these for each AND sub-clause of a restriction condition
|
||||
* (WHERE clause). Since the restriction clauses are logically ANDed, we
|
||||
* can use any one of them or any subset of them to filter out tuples,
|
||||
* without having to evaluate the rest. The RestrictInfo node itself stores
|
||||
* data used by the optimizer while choosing the best query plan.
|
||||
* (WHERE or JOIN/ON clause). Since the restriction clauses are logically
|
||||
* ANDed, we can use any one of them or any subset of them to filter out
|
||||
* tuples, without having to evaluate the rest. The RestrictInfo node itself
|
||||
* stores data used by the optimizer while choosing the best query plan.
|
||||
*
|
||||
* If a restriction clause references a single base relation, it will appear
|
||||
* in the baserestrictinfo list of the RelOptInfo for that base rel.
|
||||
@ -405,6 +426,31 @@ typedef struct HashPath
|
||||
* sequence we use. So, these clauses cannot be associated directly with
|
||||
* the join RelOptInfo, but must be kept track of on a per-join-path basis.
|
||||
*
|
||||
* When dealing with outer joins we have to be very careful about pushing qual
|
||||
* clauses up and down the tree. An outer join's own JOIN/ON conditions must
|
||||
* be evaluated exactly at that join node, and any quals appearing in WHERE or
|
||||
* in a JOIN above the outer join cannot be pushed down below the outer join.
|
||||
* Otherwise the outer join will produce wrong results because it will see the
|
||||
* wrong sets of input rows. All quals are stored as RestrictInfo nodes
|
||||
* during planning, but there's a flag to indicate whether a qual has been
|
||||
* pushed down to a lower level than its original syntactic placement in the
|
||||
* join tree would suggest. If an outer join prevents us from pushing a qual
|
||||
* down to its "natural" semantic level (the level associated with just the
|
||||
* base rels used in the qual) then the qual will appear in JoinInfo lists
|
||||
* that reference more than just the base rels it actually uses. By
|
||||
* pretending that the qual references all the rels appearing in the outer
|
||||
* join, we prevent it from being evaluated below the outer join's joinrel.
|
||||
* When we do form the outer join's joinrel, we still need to distinguish
|
||||
* those quals that are actually in that join's JOIN/ON condition from those
|
||||
* that appeared higher in the tree and were pushed down to the join rel
|
||||
* because they used no other rels. That's what the ispusheddown flag is for;
|
||||
* it tells us that a qual came from a point above the join of the specific
|
||||
* set of base rels that it uses (or that the JoinInfo structures claim it
|
||||
* uses). A clause that originally came from WHERE will *always* have its
|
||||
* ispusheddown flag set; a clause that came from an INNER JOIN condition,
|
||||
* but doesn't use all the rels being joined, will also have ispusheddown set
|
||||
* because it will get attached to some lower joinrel.
|
||||
*
|
||||
* In general, the referenced clause might be arbitrarily complex. The
|
||||
* kinds of clauses we can handle as indexscan quals, mergejoin clauses,
|
||||
* or hashjoin clauses are fairly limited --- the code for each kind of
|
||||
@ -415,16 +461,6 @@ typedef struct HashPath
|
||||
* 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
|
||||
@ -433,7 +469,7 @@ typedef struct RestrictInfo
|
||||
|
||||
Expr *clause; /* the represented clause of WHERE or JOIN */
|
||||
|
||||
bool isjoinqual; /* TRUE if clause came from JOIN/ON */
|
||||
bool ispusheddown; /* TRUE if clause was pushed down in level */
|
||||
|
||||
/* only used if clause is an OR clause: */
|
||||
List *subclauseindices; /* indexes matching subclauses */
|
||||
|
@ -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.39 2000/09/12 21:07:11 tgl Exp $
|
||||
* $Id: clauses.h,v 1.40 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -43,6 +43,7 @@ extern Expr *get_notclausearg(Expr *notclause);
|
||||
|
||||
extern bool and_clause(Node *clause);
|
||||
extern Expr *make_andclause(List *andclauses);
|
||||
extern Node *make_and_qual(Node *qual1, Node *qual2);
|
||||
extern Expr *make_ands_explicit(List *andclauses);
|
||||
extern List *make_ands_implicit(Expr *clause);
|
||||
|
||||
@ -56,10 +57,7 @@ extern void check_subplans_for_ungrouped_vars(Node *clause, Query *query);
|
||||
extern bool contain_noncachable_functions(Node *clause);
|
||||
|
||||
extern bool is_pseudo_constant_clause(Node *clause);
|
||||
|
||||
extern List *pull_constant_clauses(List *quals,
|
||||
List **noncachableQual,
|
||||
List **constantQual);
|
||||
extern List *pull_constant_clauses(List *quals, List **constantQual);
|
||||
|
||||
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
|
||||
extern int NumRelids(Node *clause);
|
||||
|
@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pathnode.h
|
||||
* prototypes for pathnode.c, indexnode.c, relnode.c.
|
||||
* prototypes for pathnode.c, relnode.c.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pathnode.h,v 1.28 2000/09/12 21:07:11 tgl Exp $
|
||||
* $Id: pathnode.h,v 1.29 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,6 +32,7 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
|
||||
List *restriction_clauses,
|
||||
ScanDirection indexscandir);
|
||||
extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
|
||||
extern Path *create_subqueryscan_path(RelOptInfo *rel);
|
||||
|
||||
extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
@ -66,9 +67,4 @@ extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel,
|
||||
List **restrictlist_ptr);
|
||||
|
||||
/*
|
||||
* prototypes for indexnode.h
|
||||
*/
|
||||
extern List *find_relation_indices(Query *root, RelOptInfo *rel);
|
||||
|
||||
#endif /* PATHNODE_H */
|
||||
|
@ -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.47 2000/09/12 21:07:11 tgl Exp $
|
||||
* $Id: paths.h,v 1.48 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -28,6 +28,7 @@ extern bool enable_geqo;
|
||||
extern int geqo_rels;
|
||||
|
||||
extern RelOptInfo *make_one_rel(Query *root);
|
||||
extern RelOptInfo *make_fromexpr_rel(Query *root, FromExpr *from);
|
||||
|
||||
/*
|
||||
* indxpath.c
|
||||
@ -77,7 +78,7 @@ extern List *make_rels_by_clause_joins(Query *root,
|
||||
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);
|
||||
extern RelOptInfo *make_jointree_rel(Query *root, Node *jtnode);
|
||||
|
||||
/*
|
||||
* pathkeys.c
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: plancat.h,v 1.19 2000/06/09 03:17:11 tgl Exp $
|
||||
* $Id: plancat.h,v 1.20 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,10 +17,10 @@
|
||||
#include "nodes/relation.h"
|
||||
|
||||
|
||||
extern void relation_info(Query *root, Index relid,
|
||||
bool *hasindex, long *pages, double *tuples);
|
||||
extern void relation_info(Oid relationObjectId,
|
||||
bool *hasindex, long *pages, double *tuples);
|
||||
|
||||
extern List *find_secondary_indexes(Query *root, Index relid);
|
||||
extern List *find_secondary_indexes(Oid relationObjectId);
|
||||
|
||||
extern List *find_inheritance_children(Oid inhparent);
|
||||
|
||||
|
@ -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.44 2000/09/12 21:07:11 tgl Exp $
|
||||
* $Id: planmain.h,v 1.45 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -40,8 +40,7 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||
* prototypes for plan/initsplan.c
|
||||
*/
|
||||
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 Relids distribute_quals_to_rels(Query *root, Node *jtnode);
|
||||
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);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_agg.h,v 1.15 2000/04/12 17:16:45 momjian Exp $
|
||||
* $Id: parse_agg.h,v 1.16 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,7 +17,7 @@
|
||||
#include "parser/parse_node.h"
|
||||
|
||||
extern void AddAggToParseState(ParseState *pstate, Aggref *aggref);
|
||||
extern void parseCheckAggregates(ParseState *pstate, Query *qry);
|
||||
extern void parseCheckAggregates(ParseState *pstate, Query *qry, Node *qual);
|
||||
extern Aggref *ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
|
||||
List *args, bool agg_star, bool agg_distinct,
|
||||
int precedence);
|
||||
|
@ -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.21 2000/09/12 21:07:12 tgl Exp $
|
||||
* $Id: parse_node.h,v 1.22 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -23,7 +23,8 @@ typedef struct ParseState
|
||||
{
|
||||
struct ParseState *parentParseState; /* stack link */
|
||||
List *p_rtable; /* range table so far */
|
||||
List *p_jointree; /* join tree so far */
|
||||
List *p_joinlist; /* join items so far (will become
|
||||
* FromExpr node's fromlist) */
|
||||
int p_last_resno; /* last targetlist resno assigned */
|
||||
bool p_hasAggs;
|
||||
bool p_hasSubLinks;
|
||||
|
@ -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.19 2000/09/12 21:07:12 tgl Exp $
|
||||
* $Id: parse_relation.h,v 1.20 2000/09/29 18:21:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -27,7 +27,7 @@ extern int refnameRangeTablePosn(ParseState *pstate,
|
||||
extern int RTERangeTablePosn(ParseState *pstate,
|
||||
RangeTblEntry *rte,
|
||||
int *sublevels_up);
|
||||
extern JoinExpr *scanJoinTreeForRefname(Node *jtnode, char *refname);
|
||||
extern JoinExpr *scanJoinListForRefname(Node *jtnode, char *refname);
|
||||
extern Node *colnameToVar(ParseState *pstate, char *colname);
|
||||
extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
|
||||
char *colname, bool implicitRTEOK);
|
||||
@ -36,7 +36,11 @@ extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
|
||||
Attr *alias,
|
||||
bool inh,
|
||||
bool inFromCl);
|
||||
extern void addRTEtoJoinTree(ParseState *pstate, RangeTblEntry *rte);
|
||||
extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
Query *subquery,
|
||||
Attr *alias,
|
||||
bool inFromCl);
|
||||
extern void addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte);
|
||||
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
|
||||
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
|
||||
List **colnames, List **colvars);
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* locks.h
|
||||
*
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: locks.h,v 1.13 2000/01/26 05:58:30 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef LOCKS_H
|
||||
#define LOCKS_H
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "rewrite/prs2lock.h"
|
||||
|
||||
extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno,
|
||||
Query *parsetree);
|
||||
extern void checkLockPerms(List *locks, Query *parsetree, int rt_index);
|
||||
|
||||
#endif /* LOCKS_H */
|
@ -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.22 2000/09/12 21:07:15 tgl Exp $
|
||||
* $Id: rewriteManip.h,v 1.23 2000/09/29 18:21:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -31,15 +31,12 @@ extern bool attribute_used(Node *node, int rt_index, int attno,
|
||||
extern void AddQual(Query *parsetree, Node *qual);
|
||||
extern void AddHavingQual(Query *parsetree, Node *havingQual);
|
||||
extern void AddNotQual(Query *parsetree, Node *qual);
|
||||
extern void AddGroupClause(Query *parsetree, List *group_by, List *tlist);
|
||||
|
||||
extern bool checkExprHasAggs(Node *node);
|
||||
extern bool checkExprHasSubLink(Node *node);
|
||||
|
||||
extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up,
|
||||
List *targetlist, int event, int update_varno);
|
||||
extern void FixNew(RewriteInfo *info, Query *parsetree);
|
||||
|
||||
extern void HandleRIRAttributeRule(Query *parsetree, List *rtable,
|
||||
List *targetlist, int rt_index,
|
||||
int attr_num, int *modified, int *badsql);
|
||||
|
||||
#endif /* REWRITEMANIP_H */
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: rewriteSupport.h,v 1.12 2000/06/30 07:04:04 tgl Exp $
|
||||
* $Id: rewriteSupport.h,v 1.13 2000/09/29 18:21:24 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
extern int IsDefinedRewriteRule(char *ruleName);
|
||||
|
||||
extern void setRelhasrulesInRelation(Oid relationId, bool relhasrules);
|
||||
extern void SetRelationRuleStatus(Oid relationId, bool relHasRules,
|
||||
bool relIsBecomingView);
|
||||
|
||||
#endif /* REWRITESUPPORT_H */
|
||||
|
@ -221,7 +221,7 @@ drop rule 314159;
|
||||
ERROR: parser: parse error at or near "314159"
|
||||
-- no such rule
|
||||
drop rule nonesuch;
|
||||
ERROR: Rule or view 'nonesuch' not found
|
||||
ERROR: Rule or view "nonesuch" not found
|
||||
-- bad keyword
|
||||
drop tuple rule nonesuch;
|
||||
ERROR: parser: parse error at or near "tuple"
|
||||
|
Loading…
x
Reference in New Issue
Block a user