diff options
Diffstat (limited to 'src/backend/optimizer/prep')
-rw-r--r-- | src/backend/optimizer/prep/Makefile.inc | 14 | ||||
-rw-r--r-- | src/backend/optimizer/prep/archive.c | 66 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepqual.c | 582 | ||||
-rw-r--r-- | src/backend/optimizer/prep/preptlist.c | 322 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 400 |
5 files changed, 1384 insertions, 0 deletions
diff --git a/src/backend/optimizer/prep/Makefile.inc b/src/backend/optimizer/prep/Makefile.inc new file mode 100644 index 00000000000..40026716c9e --- /dev/null +++ b/src/backend/optimizer/prep/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for optimizer/prep +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= archive.c prepqual.c preptlist.c prepunion.c diff --git a/src/backend/optimizer/prep/archive.c b/src/backend/optimizer/prep/archive.c new file mode 100644 index 00000000000..0303eca70f1 --- /dev/null +++ b/src/backend/optimizer/prep/archive.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * archive.c-- + * Support for planning scans on archived relations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Attic/archive.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <sys/types.h> /* for u_int in relcache.h */ +#include "postgres.h" + +#include "utils/rel.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/relcache.h" +#include "catalog/pg_class.h" +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" +#include "optimizer/prep.h" +#include "commands/creatinh.h" + +void +plan_archive(List *rt) +{ + List *rtitem; + RangeTblEntry *rte; + TimeRange *trange; + Relation r; + Oid reloid; + + foreach(rtitem, rt) { + rte = lfirst(rtitem); + trange = rte->timeRange; + if (trange) { + reloid = rte->relid; + r = RelationIdGetRelation(reloid); + if (r->rd_rel->relarch != 'n') { + rte->archive = true; + } + } + } +} + + +/* + * find_archive_rels -- Given a particular relid, find the archive + * relation's relid. + */ +List * +find_archive_rels(Oid relid) +{ + Relation arel; + char *arelName; + + arelName = MakeArchiveName(relid); + arel = RelationNameGetRelation(arelName); + pfree(arelName); + + return lconsi(arel->rd_id, lconsi(relid, NIL)); +} diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c new file mode 100644 index 00000000000..e1aafa7db1e --- /dev/null +++ b/src/backend/optimizer/prep/prepqual.c @@ -0,0 +1,582 @@ +/*------------------------------------------------------------------------- + * + * prepqual.c-- + * Routines for preprocessing the parse tree qualification + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "nodes/makefuncs.h" + +#include "optimizer/internal.h" +#include "optimizer/clauses.h" +#include "optimizer/prep.h" + +#include "utils/lsyscache.h" + +static Expr *pull_args(Expr *qual); +static List *pull_ors(List *orlist); +static List *pull_ands(List *andlist); +static Expr *find_nots(Expr *qual); +static Expr *push_nots(Expr *qual); +static Expr *normalize(Expr *qual); +static List *or_normalize(List *orlist); +static List *distribute_args(List *item, List *args); +static List *qualcleanup(Expr *qual); +static List *remove_ands(Expr *qual); +static List *remove_duplicates(List *list); + +/* + * preprocess-qualification-- + * Driver routine for modifying the parse tree qualification. + * + * Returns the new base qualification and the existential qualification + * in existentialQualPtr. + * + * XXX right now, update_clauses() does nothing so + * preprocess-qualification simply converts the qual in conjunctive + * normal form (see cnfify() below ) + */ +List * +preprocess_qualification(Expr *qual, List *tlist, List **existentialQualPtr) +{ + List *cnf_qual = cnfify(qual, true); +/* + List *existential_qual = + update_clauses(intCons(_query_result_relation_, + update_relations(tlist)), + cnf_qual, + _query_command_type_); + if (existential_qual) { + *existentialQualPtr = existential_qual; + return set_difference(cnf_qual, existential_qual); + } else { + *existentialQualPtr = NIL; + return cnf_qual; + } +*/ + /* update_clauses() is not working right now */ + *existentialQualPtr = NIL; + return cnf_qual; + +} + +/***************************************************************************** + * + * CNF CONVERSION ROUTINES + * + * NOTES: + * The basic algorithms for normalizing the qualification are taken + * from ingres/source/qrymod/norml.c + * + * Remember that the initial qualification may consist of ARBITRARY + * combinations of clauses. In addition, before this routine is called, + * the qualification will contain explicit "AND"s. + * + *****************************************************************************/ + + +/* + * cnfify-- + * Convert a qualification to conjunctive normal form by applying + * successive normalizations. + * + * Returns the modified qualification with an extra level of nesting. + * + * If 'removeAndFlag' is true then it removes the explicit ANDs. + * + * NOTE: this routine is called by the planner (removeAndFlag = true) + * and from the rule manager (removeAndFlag = false). + * + */ +List * +cnfify(Expr *qual, bool removeAndFlag) +{ + Expr *newqual = NULL; + + if (qual != NULL) { + newqual = find_nots(pull_args(qual)); + newqual = normalize(pull_args(newqual)); + newqual = (Expr*)qualcleanup(pull_args(newqual)); + newqual = pull_args(newqual);; + + if (removeAndFlag) { + if(and_clause((Node*)newqual)) + newqual=(Expr*)remove_ands(newqual); + else + newqual=(Expr*)remove_ands(make_andclause(lcons(newqual,NIL))); + } + } + else if (qual!=NULL) + newqual = (Expr*)lcons(qual, NIL); + + return (List*)(newqual); +} + +/* + * pull-args-- + * Given a qualification, eliminate nested 'and' and 'or' clauses. + * + * Returns the modified qualification. + * + */ +static Expr * +pull_args(Expr *qual) +{ + if (qual==NULL) + return (NULL); + + if (is_opclause((Node*)qual)) { + return(make_clause(qual->opType, qual->oper, + lcons(pull_args((Expr*)get_leftop(qual)), + lcons(pull_args((Expr*)get_rightop(qual)), + NIL)))); + } else if (and_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + + foreach (temp, qual->args) + t_list = lappend (t_list, pull_args(lfirst(temp))); + return (make_andclause (pull_ands (t_list))); + }else if (or_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + + foreach (temp, qual->args) + t_list = lappend (t_list, pull_args(lfirst(temp))); + return (make_orclause (pull_ors (t_list))); + } else if (not_clause((Node*)qual)) { + return (make_notclause (pull_args (get_notclausearg (qual)))); + } else { + return (qual); + } +} + +/* + * pull-ors-- + * Pull the arguments of an 'or' clause nested within another 'or' + * clause up into the argument list of the parent. + * + * Returns the modified list. + */ +static List * +pull_ors(List *orlist) +{ + if (orlist==NIL) + return (NIL); + + if (or_clause(lfirst(orlist))) { + List *args = ((Expr*)lfirst(orlist))->args; + return (pull_ors(nconc(copyObject((Node*)args), + copyObject((Node*)lnext(orlist))))); + } else { + return (lcons(lfirst(orlist), pull_ors(lnext(orlist)))); + } +} + +/* + * pull-ands-- + * Pull the arguments of an 'and' clause nested within another 'and' + * clause up into the argument list of the parent. + * + * Returns the modified list. + */ +static List * +pull_ands(List *andlist) +{ + if (andlist==NIL) + return (NIL); + + if (and_clause (lfirst(andlist))) { + List *args = ((Expr*)lfirst(andlist))->args; + return (pull_ands(nconc(copyObject((Node*)args), + copyObject((Node*)lnext(andlist))))); + } else { + return (lcons(lfirst(andlist), pull_ands(lnext(andlist)))); + } +} + +/* + * find-nots-- + * Traverse the qualification, looking for 'not's to take care of. + * For 'not' clauses, remove the 'not' and push it down to the clauses' + * descendants. + * For all other clause types, simply recurse. + * + * Returns the modified qualification. + * + */ +static Expr * +find_nots(Expr *qual) +{ + if (qual==NULL) + return (NULL); + + if (is_opclause((Node*)qual)) { + return (make_clause(qual->opType, qual->oper, + lcons(find_nots((Expr*)get_leftop(qual)), + lcons(find_nots((Expr*)get_rightop(qual)), + NIL)))); + } else if (and_clause ((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + + foreach (temp, qual->args) { + t_list = lappend(t_list,find_nots(lfirst(temp))); + } + + return (make_andclause(t_list)); + } else if (or_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + + foreach (temp, qual->args) { + t_list = lappend(t_list,find_nots(lfirst(temp))); + } + return (make_orclause (t_list)); + } else if (not_clause((Node*)qual)) + return (push_nots(get_notclausearg (qual))); + else + return (qual); +} + +/* + * push-nots-- + * Negate the descendants of a 'not' clause. + * + * Returns the modified qualification. + * + */ +static Expr * +push_nots(Expr *qual) +{ + if (qual==NULL) + return (NULL); + + /* + * Negate an operator clause if possible: + * ("NOT" (< A B)) => (> A B) + * Otherwise, retain the clause as it is (the 'not' can't be pushed + * down any farther). + */ + if (is_opclause((Node*)qual)) { + Oper *oper = (Oper*)((Expr*)qual)->oper; + Oid negator = get_negator(oper->opno); + + if(negator) { + Oper *op = (Oper*) makeOper(negator, + InvalidOid, + oper->opresulttype, + 0, NULL); + op->op_fcache = (FunctionCache *) NULL; + return + (make_opclause(op, get_leftop(qual), get_rightop(qual))); + } else { + return (make_notclause(qual)); + } + } else if (and_clause((Node*)qual)) { + /* Apply DeMorgan's Laws: + * ("NOT" ("AND" A B)) => ("OR" ("NOT" A) ("NOT" B)) + * ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B)) + * i.e., continue negating down through the clause's descendants. + */ + List *temp = NIL; + List *t_list = NIL; + + foreach(temp, qual->args) { + t_list = lappend(t_list,push_nots(lfirst(temp))); + } + return (make_orclause (t_list)); + } else if (or_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + + foreach(temp, qual->args) { + t_list = lappend(t_list,push_nots(lfirst(temp))); + } + return (make_andclause (t_list)); + } else if (not_clause((Node*)qual)) + /* Another 'not' cancels this 'not', so eliminate the 'not' and + * stop negating this branch. + */ + return (find_nots (get_notclausearg (qual))); + else + /* We don't know how to negate anything else, place a 'not' at this + * level. + */ + return (make_notclause (qual)); +} + +/* + * normalize-- + * Given a qualification tree with the 'not's pushed down, convert it + * to a tree in CNF by repeatedly applying the rule: + * ("OR" A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C)) + * bottom-up. + * Note that 'or' clauses will always be turned into 'and' clauses. + * + * Returns the modified qualification. + * + */ +static Expr * +normalize(Expr *qual) +{ + if (qual==NULL) + return (NULL); + + if (is_opclause((Node*)qual)) { + Expr *expr = (Expr*)qual; + return (make_clause(expr->opType, expr->oper, + lcons(normalize((Expr*)get_leftop(qual)), + lcons(normalize((Expr*)get_rightop(qual)), + NIL)))); + } else if (and_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + + foreach (temp, qual->args) { + t_list = lappend(t_list,normalize(lfirst(temp))); + } + return (make_andclause (t_list)); + } else if (or_clause((Node*)qual)) { + /* XXX - let form, maybe incorrect */ + List *orlist = NIL; + List *temp = NIL; + bool has_andclause = FALSE; + + foreach(temp, qual->args) { + orlist = lappend(orlist,normalize(lfirst(temp))); + } + foreach (temp, orlist) { + if (and_clause (lfirst(temp))) { + has_andclause = TRUE; + break; + } + } + if (has_andclause == TRUE) + return (make_andclause(or_normalize(orlist))); + else + return (make_orclause(orlist)); + + } else if (not_clause((Node*)qual)) + return (make_notclause (normalize (get_notclausearg (qual)))); + else + return (qual); +} + +/* + * or-normalize-- + * Given a list of exprs which are 'or'ed together, distribute any + * 'and' clauses. + * + * Returns the modified list. + * + */ +static List * +or_normalize(List *orlist) +{ + List *distributable = NIL; + List *new_orlist = NIL; + List *temp = NIL; + + if (orlist==NIL) + return NIL; + + foreach(temp, orlist) { + if (and_clause(lfirst(temp))) + distributable = lfirst(temp); + } + if (distributable) + new_orlist = LispRemove(distributable,orlist); + + if(new_orlist) { + return + (or_normalize(lcons(distribute_args(lfirst(new_orlist), + ((Expr*)distributable)->args), + lnext(new_orlist)))); + }else { + return (orlist); + } +} + +/* + * distribute-args-- + * Create new 'or' clauses by or'ing 'item' with each element of 'args'. + * E.g.: (distribute-args A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C)) + * + * Returns an 'and' clause. + * + */ +static List * +distribute_args(List *item, List *args) +{ + List *or_list = NIL; + List *n_list = NIL; + List *temp = NIL; + List *t_list = NIL; + + if (args==NULL) + return (item); + + foreach (temp,args) { + n_list = or_normalize(pull_ors(lcons(item, + lcons(lfirst(temp),NIL)))); + or_list = (List*)make_orclause(n_list); + t_list = lappend(t_list,or_list); + } + return ((List*)make_andclause(t_list)); +} + +/* + * qualcleanup-- + * Fix up a qualification by removing duplicate entries (left over from + * normalization), and by removing 'and' and 'or' clauses which have only + * one valid expr (e.g., ("AND" A) => A). + * + * Returns the modified qualfication. + * + */ +static List * +qualcleanup(Expr *qual) +{ + if (qual==NULL) + return (NIL); + + if (is_opclause((Node*)qual)) { + return ((List*)make_clause(qual->opType, qual->oper, + lcons(qualcleanup((Expr*)get_leftop(qual)), + lcons(qualcleanup((Expr*)get_rightop(qual)), + NIL)))); + } else if (and_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + List *new_and_args = NIL; + + foreach(temp, qual->args) + t_list = lappend(t_list,qualcleanup(lfirst(temp))); + + new_and_args = remove_duplicates(t_list); + + if(length (new_and_args) > 1) + return ((List*)make_andclause(new_and_args)); + else + return (lfirst(new_and_args)); + } + else if (or_clause((Node*)qual)) { + List *temp = NIL; + List *t_list = NIL; + List *new_or_args = NIL; + + foreach (temp, qual->args) + t_list = lappend(t_list,qualcleanup(lfirst(temp))); + + new_or_args = remove_duplicates(t_list); + + + if(length (new_or_args) > 1) + return ((List*)make_orclause (new_or_args)); + else + return (lfirst (new_or_args)); + } else if (not_clause((Node*)qual)) + return ((List*)make_notclause((Expr*)qualcleanup((Expr*)get_notclausearg(qual)))); + + else + return ((List*)qual); +} + +/* + * remove-ands-- + * Remove the explicit "AND"s from the qualification: + * ("AND" A B) => (A B) + * + * RETURNS : qual + * MODIFIES: qual + */ +static List * +remove_ands(Expr *qual) +{ + List *t_list = NIL; + + if (qual==NULL) + return (NIL); + if (is_opclause((Node*)qual)) { + return ((List*)make_clause(qual->opType, qual->oper, + lcons(remove_ands((Expr*)get_leftop(qual)), + lcons(remove_ands((Expr*)get_rightop(qual)), + NIL)))); + } else if (and_clause((Node*)qual)) { + List *temp = NIL; + foreach (temp, qual->args) + t_list = lappend(t_list,remove_ands(lfirst(temp))); + return(t_list); + } else if (or_clause((Node*)qual)) { + List *temp = NIL; + foreach (temp, qual->args) + t_list = lappend(t_list,remove_ands(lfirst(temp))); + return ((List*)make_orclause((List*)t_list)); + } else if (not_clause((Node*)qual)) { + return ((List*)make_notclause((Expr*)remove_ands((Expr*)get_notclausearg (qual)))); + } else { + return ((List*)qual); + } +} + +/***************************************************************************** + * + * EXISTENTIAL QUALIFICATIONS + * + *****************************************************************************/ + +/* + * update-relations-- + * Returns the range table indices (i.e., varnos) for all relations which + * are referenced in the target list. + * + */ +#if 0 +static List * +update_relations(List *tlist) +{ + return(NIL); +} +#endif + +/***************************************************************************** + * + * + * + *****************************************************************************/ + +static List * +remove_duplicates(List *list) +{ + List *i; + List *j; + List *result = NIL; + bool there_exists_duplicate = false; + + if (length(list) == 1) + return(list); + + foreach (i, list) { + if (i != NIL) { + foreach (j, lnext(i)) { + if (equal(lfirst(i), lfirst(j))) + there_exists_duplicate = true; + } + if (!there_exists_duplicate) + result = lappend(result, lfirst(i)); + + there_exists_duplicate = false; + } + } + return(result); +} diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c new file mode 100644 index 00000000000..fe1c2c92ba2 --- /dev/null +++ b/src/backend/optimizer/prep/preptlist.c @@ -0,0 +1,322 @@ +/*------------------------------------------------------------------------- + * + * preptlist.c-- + * Routines to preprocess the parse tree target list + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "nodes/relation.h" +#include "nodes/primnodes.h" +#include "nodes/parsenodes.h" + +#include "nodes/makefuncs.h" + +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/palloc.h" + +#include "parser/parsetree.h" /* for getrelid() */ +#include "parser/catalog_utils.h" + +#include "optimizer/internal.h" +#include "optimizer/prep.h" +#include "optimizer/clauses.h" +#include "optimizer/tlist.h" + +static List *expand_targetlist(List *tlist, Oid relid, int command_type, + Index result_relation); +static List *replace_matching_resname(List *new_tlist, + List *old_tlist); +static List *new_relation_targetlist(Oid relid, Index rt_index, + NodeTag node_type); + + +/* + * preprocess-targetlist-- + * Driver for preprocessing the parse tree targetlist. + * + * 1. Deal with appends and replaces by filling missing attributes + * in the target list. + * 2. Reset operator OIDs to the appropriate regproc ids. + * + * Returns the new targetlist. + */ +List * +preprocess_targetlist(List *tlist, + int command_type, + Index result_relation, + List *range_table) +{ + List *expanded_tlist = NIL; + Oid relid = InvalidOid; + List *t_list = NIL; + List *temp = NIL; + + if (result_relation>=1 && command_type != CMD_SELECT) { + relid = getrelid(result_relation, range_table); + } + + /* + * for heap_formtuple to work, the targetlist must match the exact + * order of the attributes. We also need to fill in the missing + * attributes here. -ay 10/94 + */ + expanded_tlist = + expand_targetlist(tlist, relid, command_type, result_relation); + + /* XXX should the fix-opids be this early?? */ + /* was mapCAR */ + foreach (temp,expanded_tlist) { + TargetEntry *tle = lfirst(temp); + if (tle->expr) + fix_opid(tle->expr); + } + t_list = copyObject(expanded_tlist); + + /* ------------------ + * for "replace" or "delete" queries, add ctid of the result + * relation into the target list so that the ctid can get + * propogate through the execution and in the end ExecReplace() + * will find the right tuple to replace or delete. This + * extra field will be removed in ExecReplace(). + * For convinient, we append this extra field to the end of + * the target list. + * ------------------ + */ + if (command_type == CMD_UPDATE || command_type == CMD_DELETE) { + TargetEntry *ctid; + Resdom *resdom; + Var *var; + + resdom = makeResdom(length(t_list) + 1, + 27, + 6, + "ctid", + 0, + 0, + 1); + + var = makeVar(result_relation, -1, 27, result_relation, -1); + + ctid = makeNode(TargetEntry); + ctid->resdom = resdom; + ctid->expr = (Node *)var; + t_list = lappend(t_list, ctid); + } + + return(t_list); +} + +/***************************************************************************** + * + * TARGETLIST EXPANSION + * + *****************************************************************************/ + +/* + * expand-targetlist-- + * Given a target list as generated by the parser and a result relation, + * add targetlist entries for the attributes which have not been used. + * + * XXX This code is only supposed to work with unnested relations. + * + * 'tlist' is the original target list + * 'relid' is the relid of the result relation + * 'command' is the update command + * + * Returns the expanded target list, sorted in resno order. + */ +static List * +expand_targetlist(List *tlist, + Oid relid, + int command_type, + Index result_relation) +{ + NodeTag node_type = T_Invalid; + + switch (command_type) { + case CMD_INSERT: + node_type = (NodeTag)T_Const; + break; + case CMD_UPDATE: + node_type = (NodeTag)T_Var; + break; + } + + if(node_type != T_Invalid) { + List *ntlist = new_relation_targetlist(relid, + result_relation, + node_type); + + return (replace_matching_resname(ntlist, tlist)); + } else { + return (tlist); + } + +} + + +static List * +replace_matching_resname(List *new_tlist, List *old_tlist) +{ + List *temp, *i; + List *t_list = NIL; + + foreach (i,new_tlist) { + TargetEntry *new_tle = (TargetEntry *)lfirst(i); + TargetEntry *matching_old_tl = NULL; + + foreach (temp, old_tlist) { + TargetEntry *old_tle = (TargetEntry *)lfirst(temp); + + old_tle = lfirst(temp); + if (!strcmp(old_tle->resdom->resname, + new_tle->resdom->resname)) { + matching_old_tl = old_tle; + break; + } + } + + if(matching_old_tl) { + matching_old_tl->resdom->resno = + new_tle->resdom->resno; + t_list = lappend(t_list, matching_old_tl); + } + else { + t_list = lappend(t_list, new_tle); + } + } + + /* + * It is possible that 'old_tlist' has some negative + * attributes (i.e. negative resnos). This only happens + * if this is a replace/append command and we explicitly + * specify a system attribute. Of course this is not a very good + * idea if this is a user query, but on the other hand the rule + * manager uses this mechanism to replace rule locks. + * + * So, copy all these entries to the end of the target list + * and set their 'resjunk' value to 1 to show that these are + * special attributes and have to be treated specially by the + * executor! + */ + foreach (temp, old_tlist) { + TargetEntry *old_tle, *new_tl; + Resdom *newresno; + + old_tle = lfirst(temp); + if (old_tle->resdom->resno < 0) { + newresno = (Resdom*) copyObject((Node*)old_tle->resdom); + newresno->resno = length(t_list) +1; + newresno->resjunk = 1; + new_tl = MakeTLE(newresno, old_tle->expr); + t_list = lappend(t_list, new_tl); + } + } + + return (t_list); +} + +/* + * new-relation-targetlist-- + * Generate a targetlist for the relation with relation OID 'relid' + * and rangetable index 'rt-index'. + * + * Returns the new targetlist. + */ +static List * +new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type) +{ + AttrNumber attno; + List *t_list = NIL; + char *attname; + Oid atttype = 0; + int16 typlen = 0; + bool attisset = false; +/* Oid type_id; */ +/* type_id = RelationIdGetTypeId(relid); */ + + for(attno=1; attno <= get_relnatts(relid); attno++) { + attname = get_attname(/*type_id,*/ relid, attno); + atttype = get_atttype(/*type_id,*/ relid, attno); + /* + * Since this is an append or replace, the size of any set + * attribute is the size of the OID used to represent it. + */ + attisset = get_attisset(/* type_id,*/ relid, attname); + if (attisset) { + typlen = tlen(type("oid")); + } else { + typlen = get_typlen(atttype); + } + + switch (node_type) { + case T_Const: + { + struct varlena *typedefault = get_typdefault(atttype); + int temp = 0; + Const *temp2 = (Const*)NULL; + TargetEntry *temp3 = (TargetEntry *)NULL; + + if (typedefault==NULL) + temp = 0; + else + temp = typlen; + + temp2 = makeConst (atttype, + temp, + (Datum)typedefault, + (typedefault == (struct varlena *)NULL), + /* XXX this is bullshit */ + false, + false /* not a set */); + + temp3 = MakeTLE (makeResdom(attno, + atttype, + typlen, + attname, + 0, + (Oid)0, + 0), + (Node*)temp2); + t_list = lappend(t_list,temp3); + break; + } + case T_Var: + { + Var *temp_var = (Var*)NULL; + TargetEntry *temp_list = NULL; + + temp_var = + makeVar(rt_index, attno, atttype, rt_index, attno); + + temp_list = MakeTLE(makeResdom(attno, + atttype, + typlen, + attname, + 0, + (Oid)0, + 0), + (Node*)temp_var); + t_list = lappend(t_list,temp_list); + break; + } + default: /* do nothing */ + break; + } + } + + return(t_list); +} + + diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c new file mode 100644 index 00000000000..8e1491940db --- /dev/null +++ b/src/backend/optimizer/prep/prepunion.c @@ -0,0 +1,400 @@ +/*------------------------------------------------------------------------- + * + * prepunion.c-- + * Routines to plan archive, inheritance, union, and version queries + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/nodes.h" +#include "nodes/pg_list.h" +#include "nodes/execnodes.h" +#include "nodes/plannodes.h" +#include "nodes/relation.h" + +#include "parser/parse_query.h" +#include "parser/parsetree.h" + +#include "utils/elog.h" +#include "utils/lsyscache.h" + +#include "optimizer/internal.h" +#include "optimizer/prep.h" +#include "optimizer/plancat.h" +#include "optimizer/planner.h" +#include "optimizer/prep.h" + +static List *plan_union_query(List *relids, Index rt_index, + RangeTblEntry *rt_entry, Query *parse, UnionFlag flag, + List **union_rtentriesPtr); +static RangeTblEntry *new_rangetable_entry(Oid new_relid, + RangeTblEntry *old_entry); +static Query *subst_rangetable(Query *root, Index index, + RangeTblEntry *new_entry); +static void fix_parsetree_attnums(Index rt_index, Oid old_relid, + Oid new_relid, Query *parsetree); +static Append *make_append(List *unionplans, Index rt_index, + List *union_rt_entries, List *tlist); + + +/* + * find-all-inheritors - + * Returns a list of relids corresponding to relations that inherit + * attributes from any relations listed in either of the argument relid + * lists. + */ +List * +find_all_inheritors(List *unexamined_relids, + List *examined_relids) +{ + List *new_inheritors = NIL; + List *new_examined_relids = NIL; + List *new_unexamined_relids = NIL; + + /* Find all relations which inherit from members of + * 'unexamined-relids' and store them in 'new-inheritors'. + */ + List *rels = NIL; + List *newrels = NIL; + + foreach(rels,unexamined_relids) { + newrels = (List*)LispUnioni(find_inheritance_children(lfirsti(rels)), + newrels); + } + new_inheritors = newrels; + + new_examined_relids = (List*)LispUnioni(examined_relids,unexamined_relids); + new_unexamined_relids = set_differencei(new_inheritors, + new_examined_relids); + + if (new_unexamined_relids==NULL) { + return(new_examined_relids); + } else { + return (find_all_inheritors (new_unexamined_relids, + new_examined_relids)); + } +} + +/* + * first-matching-rt-entry - + * Given a rangetable, find the first rangetable entry that represents + * the appropriate special case. + * + * Returns a rangetable index., Returns -1 if no matches + */ +int +first_matching_rt_entry (List *rangetable, UnionFlag flag) +{ + int count = 0; + List *temp = NIL; + + foreach(temp, rangetable) { + RangeTblEntry *rt_entry = lfirst(temp); + + switch(flag) { + case INHERITS_FLAG: + if (rt_entry->inh) + return count+1; + break; + case ARCHIVE_FLAG: + if (rt_entry->archive) + return count+1; + break; + default: + break; + } + count++; + } + + return(-1); +} + + +/* + * plan-union-queries-- + * + * Plans the queries for a given parent relation. + * + * Returns a list containing a list of plans and a list of rangetable + * entries to be inserted into an APPEND node. + * XXX - what exactly does this mean, look for make_append + */ +Append * +plan_union_queries(Index rt_index, + Query *parse, + UnionFlag flag) +{ + List *rangetable = parse->rtable; + RangeTblEntry *rt_entry = rt_fetch(rt_index,rangetable); + List *union_relids = NIL; + List *union_plans = NIL; + List *union_rt_entries = NIL; + + switch (flag) { + case INHERITS_FLAG: + union_relids = + find_all_inheritors(lconsi(rt_entry->relid, + NIL), + NIL); + break; + +#if 0 + case UNION_FLAG: + { + Index rt_index = 0; + union_plans = handleunion(root,rangetable,tlist,qual); + return (make_append (union_plans, + rt_index, rangetable, + ((Plan*)lfirst(union_plans))->targetlist )); + } + break; +#endif + + case VERSION_FLAG: + union_relids = VersionGetParents(rt_entry->relid); + break; + + case ARCHIVE_FLAG: + union_relids = find_archive_rels(rt_entry->relid); + break; + + default: + /* do nothing */ + break; + } + + /* + * Remove the flag for this relation, since we're about to handle it + * (do it before recursing!). + * XXX destructive parse tree change + */ + switch(flag) { + case INHERITS_FLAG: + rt_fetch(rt_index,rangetable)->inh = false; + break; + case ARCHIVE_FLAG: + rt_fetch(rt_index,rangetable)->archive = false; + break; + default: + break; + } + + /* XXX - can't find any reason to sort union-relids + * as paul did, so we're leaving it out for now + * (maybe forever) - jeff & lp + * + * [maybe so. btw, jeff & lp did the lisp conversion, according to Paul. + * -- ay 10/94.] + */ + union_plans = plan_union_query(union_relids, rt_index, rt_entry, + parse, flag, &union_rt_entries); + + return (make_append(union_plans, + rt_index, + union_rt_entries, + ((Plan*)lfirst(union_plans))->targetlist)); +} + + +/* + * plan-union-query-- + * Returns a list of plans for 'relids' and a list of range table entries + * in union_rtentries. + */ +static List * +plan_union_query(List *relids, + Index rt_index, + RangeTblEntry *rt_entry, + Query *root, + UnionFlag flag, + List **union_rtentriesPtr) +{ + List *i; + List *union_plans = NIL; + List *union_rtentries = NIL; + + foreach (i, relids) { + int relid = lfirsti(i); + RangeTblEntry *new_rt_entry = new_rangetable_entry(relid, + rt_entry); + Query *new_root = subst_rangetable(root, + rt_index, + new_rt_entry); + + /* reset the uniqueflag and sortclause in parse tree root, so that + * sorting will only be done once after append + */ +/* new_root->uniqueFlag = false; */ + new_root->uniqueFlag = NULL; + new_root->sortClause = NULL; + if (flag == ARCHIVE_FLAG) { + /* + * the entire union query uses the same (most recent) schema. + * to do otherwise would require either ragged tuples or careful + * archiving and interpretation of pg_attribute... + */ + } else { + fix_parsetree_attnums(rt_index, + rt_entry->relid, + relid, + new_root); + } + + union_plans = lappend(union_plans, planner(new_root)); + union_rtentries = lappend(union_rtentries, new_rt_entry); + } + + *union_rtentriesPtr = union_rtentries; + return(union_plans); +} + +/* + * new-rangetable-entry - + * Replaces the name and relid of 'old-entry' with the values for + * 'new-relid'. + * + * Returns a copy of 'old-entry' with the parameters substituted. + */ +static RangeTblEntry * +new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry) +{ + RangeTblEntry *new_entry = copyObject(old_entry); + + /* ??? someone tell me what the following is doing! - ay 11/94 */ + if (!strcmp(new_entry->refname, "*CURRENT*") || + !strcmp(new_entry->refname, "*NEW*")) + new_entry->refname = get_rel_name(new_relid); + else + new_entry->relname = get_rel_name(new_relid); + + new_entry->relid = new_relid; + return(new_entry); +} + +/* + * subst-rangetable-- + * Replaces the 'index'th rangetable entry in 'root' with 'new-entry'. + * + * Returns a new copy of 'root'. + */ +static Query * +subst_rangetable(Query *root, Index index, RangeTblEntry *new_entry) +{ + Query *new_root = copyObject(root); + List *temp = NIL; + int i = 0; + + for(temp = new_root->rtable,i =1; i < index; temp =lnext(temp),i++) + ; + lfirst(temp) = new_entry; + + return (new_root); +} + +static void +fix_parsetree_attnums_nodes(Index rt_index, + Oid old_relid, + Oid new_relid, + Node *node) +{ + if (node==NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid, + tle->expr); + } + break; + case T_Expr: + { + Expr *expr = (Expr *)node; + fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid, + (Node*)expr->args); + } + break; + case T_Var: + { + Var *var = (Var *)node; + Oid old_typeid, new_typeid; + +/* old_typeid = RelationIdGetTypeId(old_relid);*/ +/* new_typeid = RelationIdGetTypeId(new_relid);*/ + old_typeid = old_relid; + new_typeid = new_relid; + + if (var->varno == rt_index && var->varattno != 0) { + var->varattno = + get_attnum(new_typeid, + get_attname(old_typeid, var->varattno)); + } + } + break; + case T_List: + { + List *l; + foreach(l, (List*)node) { + fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid, + (Node*)lfirst(l)); + } + } + break; + default: + break; + } +} + +/* + * fix-parsetree-attnums-- + * Replaces attribute numbers from the relation represented by + * 'old-relid' in 'parsetree' with the attribute numbers from + * 'new-relid'. + * + * Returns the destructively-modified parsetree. + * + */ +static void +fix_parsetree_attnums(Index rt_index, + Oid old_relid, + Oid new_relid, + Query *parsetree) +{ + if (old_relid == new_relid) + return; + + fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid, + (Node*)parsetree->targetList); + fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid, + parsetree->qual); +} + +static Append * +make_append(List *unionplans, + Index rt_index, + List *union_rt_entries, + List *tlist) +{ + Append *node = makeNode(Append); + + node->unionplans = unionplans; + node->unionrelid = rt_index; + node->unionrtentries = union_rt_entries; + node->plan.cost = 0.0; + node->plan.state = (EState*)NULL; + node->plan.targetlist = tlist; + node->plan.qual = NIL; + node->plan.lefttree = (Plan*)NULL; + node->plan.righttree = (Plan*)NULL; + + return(node); +} |