1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* preptlist.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to preprocess the parse tree target list
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-08-09 05:13:31 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.29 1999/08/09 03:13:31 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "nodes/makefuncs.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "optimizer/clauses.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "optimizer/prep.h"
|
|
|
|
#include "parser/parsetree.h"
|
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 06:40:42 +02:00
|
|
|
static List *expand_targetlist(List *tlist, Oid relid, int command_type,
|
1997-09-07 07:04:48 +02:00
|
|
|
Index result_relation);
|
1998-09-01 06:40:42 +02:00
|
|
|
static List *replace_matching_resname(List *new_tlist,
|
1997-09-08 23:56:23 +02:00
|
|
|
List *old_tlist);
|
1998-09-01 06:40:42 +02:00
|
|
|
static List *new_relation_targetlist(Oid relid, Index rt_index,
|
1997-09-07 07:04:48 +02:00
|
|
|
NodeTag node_type);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* preprocess_targetlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
List *
|
1997-09-08 23:56:23 +02:00
|
|
|
preprocess_targetlist(List *tlist,
|
1997-09-07 07:04:48 +02:00
|
|
|
int command_type,
|
|
|
|
Index result_relation,
|
1997-09-08 23:56:23 +02:00
|
|
|
List *range_table)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid relid = InvalidOid;
|
1999-08-09 02:51:26 +02:00
|
|
|
List *expanded_tlist;
|
|
|
|
List *t_list;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
1999-02-03 22:18:02 +01:00
|
|
|
expanded_tlist = expand_targetlist(tlist, relid, command_type, result_relation);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* XXX should the fix-opids be this early?? */
|
1999-08-09 02:51:26 +02:00
|
|
|
fix_opids(expanded_tlist);
|
1997-09-07 07:04:48 +02:00
|
|
|
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)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *ctid;
|
|
|
|
Resdom *resdom;
|
|
|
|
Var *var;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
resdom = makeResdom(length(t_list) + 1,
|
1998-07-20 21:21:45 +02:00
|
|
|
TIDOID,
|
1998-02-10 05:02:59 +01:00
|
|
|
-1,
|
1997-09-07 07:04:48 +02:00
|
|
|
"ctid",
|
|
|
|
0,
|
|
|
|
0,
|
1999-05-17 19:03:51 +02:00
|
|
|
true);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-07-20 21:21:45 +02:00
|
|
|
var = makeVar(result_relation, -1, TIDOID, -1, 0, result_relation, -1);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-07-20 21:53:53 +02:00
|
|
|
ctid = makeTargetEntry(resdom, (Node *) var);
|
1997-09-07 07:04:48 +02:00
|
|
|
t_list = lappend(t_list, ctid);
|
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return t_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* TARGETLIST EXPANSION
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* expand_targetlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
* Returns the expanded target list, sorted in resno order.
|
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1997-09-08 23:56:23 +02:00
|
|
|
expand_targetlist(List *tlist,
|
1997-09-07 07:04:48 +02:00
|
|
|
Oid relid,
|
|
|
|
int command_type,
|
|
|
|
Index result_relation)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
NodeTag node_type = T_Invalid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
switch (command_type)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case CMD_INSERT:
|
|
|
|
node_type = (NodeTag) T_Const;
|
|
|
|
break;
|
|
|
|
case CMD_UPDATE:
|
|
|
|
node_type = (NodeTag) T_Var;
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (node_type != T_Invalid)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *ntlist = new_relation_targetlist(relid,
|
|
|
|
result_relation,
|
|
|
|
node_type);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return replace_matching_resname(ntlist, tlist);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return tlist;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1997-09-08 23:56:23 +02:00
|
|
|
replace_matching_resname(List *new_tlist, List *old_tlist)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *temp,
|
|
|
|
*i;
|
|
|
|
List *t_list = NIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(i, new_tlist)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *new_tle = (TargetEntry *) lfirst(i);
|
|
|
|
TargetEntry *matching_old_tl = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
foreach(temp, old_tlist)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
old_tle = lfirst(temp);
|
|
|
|
if (!strcmp(old_tle->resdom->resname,
|
|
|
|
new_tle->resdom->resname))
|
|
|
|
{
|
|
|
|
matching_old_tl = old_tle;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matching_old_tl)
|
|
|
|
{
|
1999-02-03 22:18:02 +01:00
|
|
|
matching_old_tl->resdom->resno = new_tle->resdom->resno;
|
1997-09-07 07:04:48 +02:00
|
|
|
t_list = lappend(t_list, matching_old_tl);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
t_list = lappend(t_list, new_tle);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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!
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
foreach(temp, old_tlist)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
TargetEntry *old_tle,
|
|
|
|
*new_tl;
|
|
|
|
Resdom *newresno;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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;
|
1998-07-20 21:53:53 +02:00
|
|
|
new_tl = makeTargetEntry(newresno, old_tle->expr);
|
1997-09-07 07:04:48 +02:00
|
|
|
t_list = lappend(t_list, new_tl);
|
|
|
|
}
|
1999-05-12 17:02:39 +02:00
|
|
|
|
|
|
|
/*
|
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.
|
1999-05-12 17:02:39 +02:00
|
|
|
*/
|
|
|
|
if (old_tle->resdom->resgroupref > 0)
|
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
bool already_there = FALSE;
|
|
|
|
TargetEntry *new_tle;
|
|
|
|
Resdom *newresno;
|
1999-05-12 17:02:39 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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-12 17:02:39 +02:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
if (new_tle->resdom->resgroupref ==
|
|
|
|
old_tle->resdom->resgroupref)
|
1999-05-12 17:02:39 +02:00
|
|
|
{
|
|
|
|
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;
|
1999-05-12 17:02:39 +02:00
|
|
|
new_tl = makeTargetEntry(newresno, old_tle->expr);
|
|
|
|
t_list = lappend(t_list, new_tl);
|
|
|
|
}
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return t_list;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1999-02-14 00:22:53 +01:00
|
|
|
* new_relation_targetlist
|
1997-09-07 07:04:48 +02:00
|
|
|
* Generate a targetlist for the relation with relation OID 'relid'
|
1999-02-14 00:22:53 +01:00
|
|
|
* and rangetable index 'rt_index'.
|
1997-09-07 07:04:48 +02:00
|
|
|
*
|
|
|
|
* Returns the new targetlist.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1996-07-09 08:22:35 +02:00
|
|
|
new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
List *t_list = NIL;
|
1999-05-29 03:48:06 +02:00
|
|
|
int natts = get_relnatts(relid);
|
|
|
|
AttrNumber attno;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-05-29 03:48:06 +02:00
|
|
|
for (attno = 1; attno <= natts; attno++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-29 03:48:06 +02:00
|
|
|
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;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (node_type)
|
|
|
|
{
|
1999-05-29 03:48:06 +02:00
|
|
|
case T_Const: /* INSERT command */
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
1999-08-09 05:13:31 +02:00
|
|
|
Datum typedefault = get_typdefault(atttype);
|
1999-05-29 03:48:06 +02:00
|
|
|
int typlen;
|
|
|
|
Const *temp_const;
|
|
|
|
TargetEntry *temp_tle;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
1999-08-09 05:13:31 +02:00
|
|
|
if (typedefault == PointerGetDatum(NULL))
|
1999-05-29 03:48:06 +02:00
|
|
|
typlen = 0;
|
1997-09-08 04:41:22 +02:00
|
|
|
else
|
1999-05-29 03:48:06 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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,
|
1999-08-09 05:13:31 +02:00
|
|
|
typedefault,
|
|
|
|
(typedefault == PointerGetDatum(NULL)),
|
1999-05-29 03:48:06 +02:00
|
|
|
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);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
}
|
1999-05-29 03:48:06 +02:00
|
|
|
case T_Var: /* UPDATE command */
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
1999-05-29 03:48:06 +02:00
|
|
|
Var *temp_var;
|
|
|
|
TargetEntry *temp_tle;
|
1997-09-08 04:41:22 +02:00
|
|
|
|
1999-05-29 03:48:06 +02:00
|
|
|
temp_var = makeVar(rt_index, attno, atttype, atttypmod,
|
1999-05-25 18:15:34 +02:00
|
|
|
0, rt_index, attno);
|
1997-09-08 04:41:22 +02:00
|
|
|
|
1999-05-29 03:48:06 +02:00
|
|
|
temp_tle = makeTargetEntry(makeResdom(attno,
|
|
|
|
atttype,
|
|
|
|
atttypmod,
|
|
|
|
attname,
|
|
|
|
0,
|
|
|
|
(Oid) 0,
|
|
|
|
false),
|
|
|
|
(Node *) temp_var);
|
|
|
|
t_list = lappend(t_list, temp_tle);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: /* do nothing */
|
1997-09-07 07:04:48 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return t_list;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|