postgresql/src/backend/optimizer/prep/preptlist.c

379 lines
9.1 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* preptlist.c
* Routines to preprocess the parse tree target list
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
1999-07-16 05:14:30 +02:00
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.25 1999/07/16 03:13:03 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <string.h>
#include "postgres.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
1999-07-16 05:14:30 +02:00
#include "parser/parsetree.h"
#include "optimizer/prep.h"
#include "optimizer/clauses.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,
TIDOID,
-1,
"ctid",
0,
0,
1999-05-17 19:03:51 +02:00
true);
var = makeVar(result_relation, -1, TIDOID, -1, 0, result_relation, -1);
ctid = makeTargetEntry(resdom, (Node *) var);
t_list = lappend(t_list, ctid);
}
1998-09-01 05:29:17 +02:00
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);
1998-09-01 05:29:17 +02:00
return replace_matching_resname(ntlist, tlist);
}
else
1998-09-01 05:29:17 +02:00
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
1999-05-25 18:15:34 +02:00
* 'resjunk' value to true 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;
1999-05-17 19:03:51 +02:00
newresno->resjunk = true;
new_tl = makeTargetEntry(newresno, old_tle->expr);
t_list = lappend(t_list, new_tl);
}
/*
1999-05-25 18:15:34 +02:00
* Also it is possible that the parser or rewriter added some junk
* attributes to hold GROUP BY expressions which are not part of
* the result attributes. We can simply identify them by looking
* at the resgroupref in the TLE's resdom, which is a unique
* number telling which TLE belongs to which GroupClause.
*/
if (old_tle->resdom->resgroupref > 0)
{
1999-05-25 18:15:34 +02:00
bool already_there = FALSE;
TargetEntry *new_tle;
Resdom *newresno;
/*
* Check if the tle is already in the new list
*/
foreach(i, t_list)
{
1999-05-25 18:15:34 +02:00
new_tle = (TargetEntry *) lfirst(i);
1999-05-25 18:15:34 +02:00
if (new_tle->resdom->resgroupref ==
old_tle->resdom->resgroupref)
{
already_there = TRUE;
break;
}
}
/*
* If not, add it and make sure it is now a junk attribute
*/
if (!already_there)
{
newresno = (Resdom *) copyObject((Node *) old_tle->resdom);
newresno->resno = length(t_list) + 1;
1999-05-17 19:03:51 +02:00
newresno->resjunk = true;
new_tl = makeTargetEntry(newresno, old_tle->expr);
t_list = lappend(t_list, new_tl);
}
}
}
1998-09-01 05:29:17 +02:00
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)
{
List *t_list = NIL;
int natts = get_relnatts(relid);
AttrNumber attno;
for (attno = 1; attno <= natts; attno++)
{
HeapTuple tp;
Form_pg_attribute att_tup;
char *attname;
Oid atttype;
int32 atttypmod;
bool attisset;
tp = SearchSysCacheTuple(ATTNUM,
ObjectIdGetDatum(relid),
UInt16GetDatum(attno),
0, 0);
if (! HeapTupleIsValid(tp))
{
elog(ERROR, "new_relation_targetlist: no attribute tuple %u %d",
relid, attno);
}
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
attname = pstrdup(att_tup->attname.data);
atttype = att_tup->atttypid;
atttypmod = att_tup->atttypmod;
attisset = att_tup->attisset;
switch (node_type)
{
case T_Const: /* INSERT command */
{
struct varlena *typedefault = get_typdefault(atttype);
int typlen;
Const *temp_const;
TargetEntry *temp_tle;
if (typedefault == NULL)
typlen = 0;
else
{
/*
* Since this is an append or replace, the size of
* any set attribute is the size of the OID used to
* represent it.
*/
if (attisset)
typlen = get_typlen(OIDOID);
else
typlen = get_typlen(atttype);
}
temp_const = makeConst(atttype,
typlen,
(Datum) typedefault,
(typedefault == NULL),
/* XXX ? */
false,
false, /* not a set */
false);
temp_tle = makeTargetEntry(makeResdom(attno,
atttype,
-1,
attname,
0,
(Oid) 0,
false),
(Node *) temp_const);
t_list = lappend(t_list, temp_tle);
break;
}
case T_Var: /* UPDATE command */
{
Var *temp_var;
TargetEntry *temp_tle;
temp_var = makeVar(rt_index, attno, atttype, atttypmod,
1999-05-25 18:15:34 +02:00
0, rt_index, attno);
temp_tle = makeTargetEntry(makeResdom(attno,
atttype,
atttypmod,
attname,
0,
(Oid) 0,
false),
(Node *) temp_var);
t_list = lappend(t_list, temp_tle);
break;
}
default: /* do nothing */
break;
}
}
1998-09-01 05:29:17 +02:00
return t_list;
}