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
|
|
|
*
|
1999-10-31 01:06:32 +02:00
|
|
|
* This module takes care of altering the query targetlist as needed for
|
2000-04-12 19:17:23 +02:00
|
|
|
* INSERT, UPDATE, and DELETE queries. For INSERT and UPDATE queries,
|
1999-10-31 01:06:32 +02:00
|
|
|
* the targetlist must contain an entry for each attribute of the target
|
|
|
|
* relation in the correct order. For both UPDATE and DELETE queries,
|
|
|
|
* we need a junk targetlist entry holding the CTID attribute --- the
|
|
|
|
* executor relies on this to find the tuple to be replaced/deleted.
|
|
|
|
*
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2001-11-02 21:23:02 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.45 2001/11/02 20:23:02 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1999-10-31 01:06:32 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "postgres.h"
|
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
#include "access/heapam.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
#include "optimizer/prep.h"
|
|
|
|
#include "parser/parsetree.h"
|
2001-11-02 21:23:02 +01:00
|
|
|
#include "parser/parse_coerce.h"
|
|
|
|
#include "parser/parse_expr.h"
|
|
|
|
#include "parser/parse_target.h"
|
|
|
|
#include "utils/builtins.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
|
|
|
|
static List *expand_targetlist(List *tlist, int command_type,
|
2000-04-12 19:17:23 +02:00
|
|
|
Index result_relation, List *range_table);
|
2000-07-22 08:19:04 +02:00
|
|
|
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
2001-03-22 05:01:46 +01:00
|
|
|
TargetEntry *prior_tle,
|
|
|
|
int attrno);
|
2001-11-02 21:23:02 +01:00
|
|
|
static Node *build_column_default(Relation rel, int attrno);
|
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.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
{
|
2000-10-05 21:11:39 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Sanity check: if there is a result relation, it'd better be a real
|
|
|
|
* relation not a subquery. Else parser or rewriter messed up.
|
2000-10-05 21:11:39 +02:00
|
|
|
*/
|
|
|
|
if (result_relation)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = rt_fetch(result_relation, range_table);
|
|
|
|
|
|
|
|
if (rte->subquery != NULL || rte->relid == InvalidOid)
|
|
|
|
elog(ERROR, "preprocess_targetlist: subquery cannot be result relation");
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* for heap_formtuple to work, the targetlist must match the exact
|
1999-10-31 01:06:32 +02:00
|
|
|
* order of the attributes. We also need to fill in any missing
|
|
|
|
* attributes. -ay 10/94
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-10-31 01:06:32 +02:00
|
|
|
if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
|
|
|
|
tlist = expand_targetlist(tlist, command_type,
|
|
|
|
result_relation, range_table);
|
|
|
|
|
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* for "update" and "delete" queries, add ctid of the result relation
|
|
|
|
* into the target list so that the ctid will propagate through
|
|
|
|
* execution and ExecutePlan() will be able to identify the right
|
|
|
|
* tuple to replace or delete. This extra field is marked "junk" so
|
|
|
|
* that it is not stored back into the tuple.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (command_type == CMD_UPDATE || command_type == CMD_DELETE)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
Resdom *resdom;
|
|
|
|
Var *var;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
resdom = makeResdom(length(tlist) + 1,
|
1998-07-20 21:21:45 +02:00
|
|
|
TIDOID,
|
1998-02-10 05:02:59 +01:00
|
|
|
-1,
|
1999-10-31 01:06:32 +02:00
|
|
|
pstrdup("ctid"),
|
1999-05-17 19:03:51 +02:00
|
|
|
true);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
var = makeVar(result_relation, SelfItemPointerAttributeNumber,
|
|
|
|
TIDOID, -1, 0);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* For an UPDATE, expand_targetlist already created a fresh tlist.
|
1999-10-31 01:06:32 +02:00
|
|
|
* For DELETE, better do a listCopy so that we don't destructively
|
|
|
|
* modify the original tlist (is this really necessary?).
|
|
|
|
*/
|
|
|
|
if (command_type == CMD_DELETE)
|
|
|
|
tlist = listCopy(tlist);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
tlist = lappend(tlist, makeTargetEntry(resdom, (Node *) var));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
return tlist;
|
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,
|
1999-10-31 01:06:32 +02:00
|
|
|
* add targetlist entries for any missing attributes, and order the
|
|
|
|
* non-junk attributes in proper field order.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
static List *
|
1999-10-31 01:06:32 +02:00
|
|
|
expand_targetlist(List *tlist, int command_type,
|
|
|
|
Index result_relation, List *range_table)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1999-10-31 01:06:32 +02:00
|
|
|
int old_tlist_len = length(tlist);
|
|
|
|
List *new_tlist = NIL;
|
|
|
|
bool *tlistentry_used;
|
|
|
|
Relation rel;
|
|
|
|
int attrno,
|
|
|
|
numattrs,
|
|
|
|
old_tlist_index;
|
|
|
|
List *temp;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
/*
|
2000-07-22 08:19:04 +02:00
|
|
|
* Keep a map of which tlist items we have transferred to new list.
|
|
|
|
*
|
|
|
|
* +1 here just keeps palloc from complaining if old_tlist_len==0.
|
1999-10-31 01:06:32 +02:00
|
|
|
*/
|
2000-04-12 19:17:23 +02:00
|
|
|
tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool));
|
|
|
|
memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-10-31 01:06:32 +02:00
|
|
|
* Scan the tuple description in the relation's relcache entry to make
|
|
|
|
* sure we have all the user attributes in the right order.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-10-31 01:06:32 +02:00
|
|
|
rel = heap_open(getrelid(result_relation, range_table), AccessShareLock);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
numattrs = RelationGetNumberOfAttributes(rel);
|
|
|
|
|
|
|
|
for (attrno = 1; attrno <= numattrs; attrno++)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
|
|
|
|
char *attrname = NameStr(att_tup->attname);
|
|
|
|
TargetEntry *new_tle = NULL;
|
1999-08-21 05:49:17 +02:00
|
|
|
|
1999-05-12 17:02:39 +02:00
|
|
|
/*
|
1999-10-31 01:06:32 +02:00
|
|
|
* We match targetlist entries to attributes using the resname.
|
2000-07-22 08:19:04 +02:00
|
|
|
* Junk attributes are not candidates to be matched.
|
1999-05-12 17:02:39 +02:00
|
|
|
*/
|
1999-10-31 01:06:32 +02:00
|
|
|
old_tlist_index = 0;
|
|
|
|
foreach(temp, tlist)
|
1999-05-12 17:02:39 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
|
|
|
|
Resdom *resdom = old_tle->resdom;
|
1999-05-12 17:02:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!tlistentry_used[old_tlist_index] &&
|
2000-07-22 08:19:04 +02:00
|
|
|
!resdom->resjunk &&
|
|
|
|
strcmp(resdom->resname, attrname) == 0)
|
1999-05-12 17:02:39 +02:00
|
|
|
{
|
2000-07-22 08:19:04 +02:00
|
|
|
new_tle = process_matched_tle(old_tle, new_tle, attrno);
|
1999-10-31 01:06:32 +02:00
|
|
|
tlistentry_used[old_tlist_index] = true;
|
2001-11-02 21:23:02 +01:00
|
|
|
/* keep scanning to detect multiple assignments to attr */
|
1999-05-12 17:02:39 +02:00
|
|
|
}
|
1999-10-31 01:06:32 +02:00
|
|
|
old_tlist_index++;
|
1999-05-12 17:02:39 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
if (new_tle == NULL)
|
1999-05-29 03:48:06 +02:00
|
|
|
{
|
1999-10-31 01:06:32 +02:00
|
|
|
/*
|
|
|
|
* Didn't find a matching tlist entry, so make one.
|
|
|
|
*
|
2001-11-02 21:23:02 +01:00
|
|
|
* For INSERT, generate an appropriate default value.
|
1999-10-31 01:06:32 +02:00
|
|
|
*
|
2000-04-12 19:17:23 +02:00
|
|
|
* For UPDATE, generate a Var reference to the existing value of
|
|
|
|
* the attribute, so that it gets copied to the new tuple.
|
1999-10-31 01:06:32 +02:00
|
|
|
*/
|
|
|
|
Oid atttype = att_tup->atttypid;
|
|
|
|
int32 atttypmod = att_tup->atttypmod;
|
2001-11-02 21:23:02 +01:00
|
|
|
Node *new_expr;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
switch (command_type)
|
|
|
|
{
|
|
|
|
case CMD_INSERT:
|
2001-11-02 21:23:02 +01:00
|
|
|
new_expr = build_column_default(rel, attrno);
|
|
|
|
break;
|
1999-10-31 01:06:32 +02:00
|
|
|
case CMD_UPDATE:
|
2000-03-09 06:00:26 +01:00
|
|
|
#ifdef _DROP_COLUMN_HACK__
|
2001-11-02 21:23:02 +01:00
|
|
|
if (COLUMN_IS_DROPPED(att_tup))
|
|
|
|
new_expr = (Node *) makeNullConst(atttype);
|
|
|
|
else
|
2000-04-12 19:17:23 +02:00
|
|
|
#endif /* _DROP_COLUMN_HACK__ */
|
2001-11-02 21:23:02 +01:00
|
|
|
new_expr = (Node *) makeVar(result_relation,
|
|
|
|
attrno,
|
|
|
|
atttype,
|
|
|
|
atttypmod,
|
|
|
|
0);
|
|
|
|
break;
|
1999-10-31 01:06:32 +02:00
|
|
|
default:
|
|
|
|
elog(ERROR, "expand_targetlist: unexpected command_type");
|
2001-11-02 21:23:02 +01:00
|
|
|
new_expr = NULL; /* keep compiler quiet */
|
1999-10-31 01:06:32 +02:00
|
|
|
break;
|
|
|
|
}
|
2001-11-02 21:23:02 +01:00
|
|
|
|
|
|
|
new_tle = makeTargetEntry(makeResdom(attrno,
|
|
|
|
atttype,
|
|
|
|
atttypmod,
|
|
|
|
pstrdup(attrname),
|
|
|
|
false),
|
|
|
|
new_expr);
|
1999-10-31 01:06:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
new_tlist = lappend(new_tlist, new_tle);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy all unprocessed tlist entries to the end of the new tlist,
|
2000-04-12 19:17:23 +02:00
|
|
|
* making sure they are marked resjunk = true. Typical junk entries
|
|
|
|
* include ORDER BY or GROUP BY expressions (are these actually
|
|
|
|
* possible in an INSERT or UPDATE?), system attribute references,
|
|
|
|
* etc.
|
1999-10-31 01:06:32 +02:00
|
|
|
*/
|
|
|
|
old_tlist_index = 0;
|
|
|
|
foreach(temp, tlist)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
|
1999-10-31 01:06:32 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!tlistentry_used[old_tlist_index])
|
1999-10-31 01:06:32 +02:00
|
|
|
{
|
2000-07-22 08:19:04 +02:00
|
|
|
Resdom *resdom = old_tle->resdom;
|
1999-10-31 01:06:32 +02:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!resdom->resjunk)
|
2000-07-22 08:19:04 +02:00
|
|
|
elog(ERROR, "Unexpected assignment to attribute \"%s\"",
|
|
|
|
resdom->resname);
|
|
|
|
/* Get the resno right, but don't copy unnecessarily */
|
|
|
|
if (resdom->resno != attrno)
|
|
|
|
{
|
|
|
|
resdom = (Resdom *) copyObject((Node *) resdom);
|
|
|
|
resdom->resno = attrno;
|
|
|
|
old_tle = makeTargetEntry(resdom, old_tle->expr);
|
|
|
|
}
|
|
|
|
new_tlist = lappend(new_tlist, old_tle);
|
|
|
|
attrno++;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-10-31 01:06:32 +02:00
|
|
|
old_tlist_index++;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1999-10-31 01:06:32 +02:00
|
|
|
heap_close(rel, AccessShareLock);
|
|
|
|
|
|
|
|
pfree(tlistentry_used);
|
|
|
|
|
|
|
|
return new_tlist;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-07-22 08:19:04 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert a matched TLE from the original tlist into a correct new TLE.
|
|
|
|
*
|
|
|
|
* This routine checks for multiple assignments to the same target attribute,
|
|
|
|
* such as "UPDATE table SET foo = 42, foo = 43". This is OK only if they
|
|
|
|
* are array assignments, ie, "UPDATE table SET foo[2] = 42, foo[4] = 43".
|
|
|
|
* If so, we need to merge the operations into a single assignment op.
|
|
|
|
* Essentially, the expression we want to produce in this case is like
|
|
|
|
* foo = array_set(array_set(foo, 2, 42), 4, 43)
|
|
|
|
*/
|
2001-03-22 05:01:46 +01:00
|
|
|
static TargetEntry *
|
|
|
|
process_matched_tle(TargetEntry *src_tle,
|
|
|
|
TargetEntry *prior_tle,
|
|
|
|
int attrno)
|
2000-07-22 08:19:04 +02:00
|
|
|
{
|
|
|
|
Resdom *resdom = src_tle->resdom;
|
|
|
|
Node *priorbottom;
|
|
|
|
ArrayRef *newexpr;
|
|
|
|
|
|
|
|
if (prior_tle == NULL)
|
|
|
|
{
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Normal case where this is the first assignment to the
|
|
|
|
* attribute.
|
2000-07-22 08:19:04 +02:00
|
|
|
*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We can recycle the old TLE+resdom if right resno; else make a new
|
|
|
|
* one to avoid modifying the old tlist structure. (Is preserving
|
2000-07-22 08:19:04 +02:00
|
|
|
* old tlist actually necessary? Not sure, be safe.)
|
|
|
|
*/
|
|
|
|
if (resdom->resno == attrno)
|
|
|
|
return src_tle;
|
|
|
|
resdom = (Resdom *) copyObject((Node *) resdom);
|
|
|
|
resdom->resno = attrno;
|
|
|
|
return makeTargetEntry(resdom, src_tle->expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Multiple assignments to same attribute. Allow only if all are
|
2000-07-22 08:19:04 +02:00
|
|
|
* array-assign operators with same bottom array object.
|
|
|
|
*/
|
|
|
|
if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
|
|
|
|
((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
|
|
|
|
prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
|
|
|
|
((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
|
|
|
|
((ArrayRef *) src_tle->expr)->refelemtype !=
|
|
|
|
((ArrayRef *) prior_tle->expr)->refelemtype)
|
|
|
|
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
|
|
|
|
resdom->resname);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2000-07-22 08:19:04 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Prior TLE could be a nest of ArrayRefs if we do this more than
|
|
|
|
* once.
|
2000-07-22 08:19:04 +02:00
|
|
|
*/
|
|
|
|
priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr;
|
|
|
|
while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
|
|
|
|
((ArrayRef *) priorbottom)->refassgnexpr != NULL)
|
|
|
|
priorbottom = ((ArrayRef *) priorbottom)->refexpr;
|
2001-03-22 05:01:46 +01:00
|
|
|
if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
|
2000-07-22 08:19:04 +02:00
|
|
|
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
|
|
|
|
resdom->resname);
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2000-07-22 08:19:04 +02:00
|
|
|
/*
|
|
|
|
* Looks OK to nest 'em.
|
|
|
|
*/
|
|
|
|
newexpr = makeNode(ArrayRef);
|
|
|
|
memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
|
|
|
|
newexpr->refexpr = prior_tle->expr;
|
|
|
|
|
|
|
|
resdom = (Resdom *) copyObject((Node *) resdom);
|
|
|
|
resdom->resno = attrno;
|
|
|
|
return makeTargetEntry(resdom, (Node *) newexpr);
|
|
|
|
}
|
2001-11-02 21:23:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make an expression tree for the default value for a column.
|
|
|
|
*
|
|
|
|
* This is used to fill in missing attributes in an INSERT targetlist.
|
|
|
|
* We look first to see if the column has a default value expression.
|
|
|
|
* If not, generate a constant of the default value for the attribute type,
|
|
|
|
* or a NULL if the type has no default value either.
|
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
build_column_default(Relation rel, int attrno)
|
|
|
|
{
|
|
|
|
TupleDesc rd_att = rel->rd_att;
|
|
|
|
Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
|
|
|
|
Oid atttype = att_tup->atttypid;
|
|
|
|
int32 atttypmod = att_tup->atttypmod;
|
|
|
|
bool hasdefault;
|
|
|
|
Datum typedefault;
|
|
|
|
int16 typlen;
|
|
|
|
bool typbyval;
|
|
|
|
Node *expr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan to see if relation has a default for this column.
|
|
|
|
*/
|
|
|
|
if (rd_att->constr && rd_att->constr->num_defval > 0)
|
|
|
|
{
|
|
|
|
AttrDefault *defval = rd_att->constr->defval;
|
|
|
|
int ndef = rd_att->constr->num_defval;
|
|
|
|
|
|
|
|
while (--ndef >= 0)
|
|
|
|
{
|
|
|
|
if (attrno == defval[ndef].adnum)
|
|
|
|
{
|
|
|
|
Oid type_id;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Found it, convert string representation to node tree.
|
|
|
|
*/
|
|
|
|
expr = stringToNode(defval[ndef].adbin);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the value is coerced to the target column type
|
|
|
|
* (might not be right type yet if it's not a constant!)
|
|
|
|
* This should match the parser's processing of non-defaulted
|
|
|
|
* expressions --- see updateTargetListEntry().
|
|
|
|
*/
|
|
|
|
type_id = exprType(expr);
|
|
|
|
|
|
|
|
if (type_id != atttype)
|
|
|
|
{
|
|
|
|
expr = CoerceTargetExpr(NULL, expr, type_id,
|
|
|
|
atttype, atttypmod);
|
|
|
|
/*
|
|
|
|
* This really shouldn't fail; should have checked the
|
|
|
|
* default's type when it was created ...
|
|
|
|
*/
|
|
|
|
if (expr == NULL)
|
|
|
|
elog(ERROR, "Column \"%s\" is of type %s"
|
|
|
|
" but default expression is of type %s"
|
|
|
|
"\n\tYou will need to rewrite or cast the expression",
|
|
|
|
NameStr(att_tup->attname),
|
|
|
|
format_type_be(atttype),
|
|
|
|
format_type_be(type_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the column is a fixed-length type, it may need a
|
|
|
|
* length coercion as well as a type coercion.
|
|
|
|
*/
|
|
|
|
expr = coerce_type_typmod(NULL, expr,
|
|
|
|
atttype, atttypmod);
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No per-column default, so look for a default for the type itself.
|
|
|
|
* If there isn't one, we generate a NULL constant of the correct type.
|
|
|
|
*/
|
|
|
|
if (att_tup->attisset)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Set attributes are represented as OIDs no matter what the set
|
|
|
|
* element type is, and the element type's default is irrelevant too.
|
|
|
|
*/
|
|
|
|
hasdefault = false;
|
|
|
|
typedefault = (Datum) 0;
|
|
|
|
typlen = sizeof(Oid);
|
|
|
|
typbyval = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef _DROP_COLUMN_HACK__
|
|
|
|
if (COLUMN_IS_DROPPED(att_tup))
|
|
|
|
{
|
|
|
|
hasdefault = false;
|
|
|
|
typedefault = (Datum) 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */
|
|
|
|
hasdefault = get_typdefault(atttype, &typedefault);
|
|
|
|
|
|
|
|
get_typlenbyval(atttype, &typlen, &typbyval);
|
|
|
|
}
|
|
|
|
|
|
|
|
expr = (Node *) makeConst(atttype,
|
|
|
|
typlen,
|
|
|
|
typedefault,
|
|
|
|
!hasdefault,
|
|
|
|
typbyval,
|
|
|
|
false, /* not a set */
|
|
|
|
false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the column is a fixed-length type, it may need a length coercion
|
|
|
|
* as well as a type coercion. But NULLs don't need that.
|
|
|
|
*/
|
|
|
|
if (hasdefault)
|
|
|
|
expr = coerce_type_typmod(NULL, expr,
|
|
|
|
atttype, atttypmod);
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
}
|