mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03: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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user