aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep')
-rw-r--r--src/backend/optimizer/prep/Makefile.inc14
-rw-r--r--src/backend/optimizer/prep/archive.c66
-rw-r--r--src/backend/optimizer/prep/prepqual.c582
-rw-r--r--src/backend/optimizer/prep/preptlist.c322
-rw-r--r--src/backend/optimizer/prep/prepunion.c400
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);
+}