aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_target.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_target.c')
-rw-r--r--src/backend/parser/parse_target.c874
1 files changed, 244 insertions, 630 deletions
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 35d064b0980..48973f67d9b 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.45 1999/07/17 20:17:26 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.46 1999/07/19 00:26:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,636 +24,244 @@
#include "utils/syscache.h"
+static Node *SizeTargetExpr(ParseState *pstate, Node *expr,
+ Oid attrtype, int32 attrtypmod);
static List *ExpandAllTables(ParseState *pstate);
static char *FigureColname(Node *expr, Node *resval);
-static Node *SizeTargetExpr(ParseState *pstate,
- Node *expr,
- Oid attrtype,
- int32 attrtypmod);
-/* MakeTargetEntryIdent()
- * Transforms an Ident Node to a Target Entry
- * Created this function to allow the ORDER/GROUP BY clause to be able
- * to construct a TargetEntry from an Ident.
- *
- * resjunk = TRUE will hide the target entry in the final result tuple.
- * daveh@insightdist.com 5/20/98
+/*
+ * transformTargetEntry()
+ * Transform any ordinary "expression-type" node into a targetlist entry.
+ * This is exported so that parse_clause.c can generate targetlist entries
+ * for ORDER/GROUP BY items that are not already in the targetlist.
*
- * Added more conversion logic to match up types from source to target.
- * - thomas 1998-06-02
+ * node the (untransformed) parse tree for the value expression.
+ * expr the transformed expression, or NULL if caller didn't do it yet.
+ * colname the column name to be assigned, or NULL if none yet set.
+ * resjunk true if the target should be marked resjunk, ie, it is not
+ * wanted in the final projected tuple.
*/
TargetEntry *
-MakeTargetEntryIdent(ParseState *pstate,
+transformTargetEntry(ParseState *pstate,
Node *node,
- char **resname,
- char *refname,
+ Node *expr,
char *colname,
bool resjunk)
{
- Node *expr = NULL;
- Oid attrtype_target;
- TargetEntry *tent = makeNode(TargetEntry);
-
- if (pstate->p_is_insert && !resjunk)
- {
-
- /*
- * Assign column name of destination column to the new TLE. XXX
- * this is probably WRONG in INSERT ... SELECT case, since
- * handling of GROUP BY and so forth probably should use the
- * source table's names not the destination's names.
- */
- if (pstate->p_insert_columns != NIL)
- {
- Ident *id = lfirst(pstate->p_insert_columns);
-
- *resname = id->name;
- pstate->p_insert_columns = lnext(pstate->p_insert_columns);
- }
- else
- elog(ERROR, "INSERT has more expressions than target columns");
- }
-
- if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
- {
- Oid attrtype_id;
- int resdomno_id,
- resdomno_target;
- RangeTblEntry *rte;
- char *target_colname;
- int32 attrtypmod,
- attrtypmod_target;
-
- target_colname = *resname;
-
- /*
- * this looks strange to me, returning an empty TargetEntry bjm
- * 1998/08/24
- */
- if (target_colname == NULL || colname == NULL)
- return tent;
-
- if (refname != NULL)
- rte = refnameRangeTableEntry(pstate, refname);
- else
- {
- rte = colnameRangeTableEntry(pstate, colname);
- if (rte == (RangeTblEntry *) NULL)
- elog(ERROR, "Attribute %s not found", colname);
- refname = rte->refname;
- }
-
- resdomno_id = get_attnum(rte->relid, colname);
- attrtype_id = get_atttype(rte->relid, resdomno_id);
- attrtypmod = get_atttypmod(rte->relid, resdomno_id);
-
- resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
- attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
- attrtypmod_target = get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target);
-
- if ((attrtype_id != attrtype_target)
- || ((attrtypmod_target >= 0) && (attrtypmod_target != attrtypmod)))
- {
- if (can_coerce_type(1, &attrtype_id, &attrtype_target))
- {
- expr = coerce_type(pstate, node, attrtype_id,
- attrtype_target,
- get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
- expr = transformExpr(pstate, expr, EXPR_COLUMN_FIRST);
- tent = MakeTargetEntryExpr(pstate, *resname, expr, false, false);
- expr = tent->expr;
- }
- else
- {
- elog(ERROR, "Unable to convert %s to %s for column %s",
- typeidTypeName(attrtype_id), typeidTypeName(attrtype_target),
- target_colname);
- }
- }
- }
-
- /*
- * here we want to look for column names only, not relation names
- * (even though they can be stored in Ident nodes, too)
- */
- if (expr == NULL)
- {
- char *name;
- int32 type_mod;
-
- name = ((*resname != NULL) ? *resname : colname);
-
- expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-
- attrtype_target = exprType(expr);
- if (nodeTag(expr) == T_Var)
- type_mod = ((Var *) expr)->vartypmod;
- else
- type_mod = -1;
-
- tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
- (Oid) attrtype_target,
- type_mod,
- name,
- (Index) 0,
- (Oid) 0,
- resjunk);
- tent->expr = expr;
- }
-
- return tent;
-} /* MakeTargetEntryIdent() */
-
-
-/* MakeTargetEntryExpr()
- * Make a TargetEntry from an expression.
- * arrayRef is a list of transformed A_Indices.
- *
- * For type mismatches between expressions and targets, use the same
- * techniques as for function and operator type coersion.
- * - thomas 1998-05-08
- *
- * Added resjunk flag and made extern so that it can be use by GROUP/
- * ORDER BY a function or expression not in the target_list
- * - daveh@insightdist.com 1998-07-31
- */
-TargetEntry *
-MakeTargetEntryExpr(ParseState *pstate,
- char *colname,
- Node *expr,
- List *arrayRef,
- bool resjunk)
-{
- Oid type_id,
- attrtype;
- int32 type_mod,
- attrtypmod;
- int resdomno;
- Relation rd;
- bool attrisset;
+ Oid type_id;
+ int32 type_mod;
Resdom *resnode;
+ /* Transform the node if caller didn't do it already */
if (expr == NULL)
- elog(ERROR, "Invalid use of NULL expression (internal error)");
+ expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
type_id = exprType(expr);
- if (nodeTag(expr) == T_Var)
- type_mod = ((Var *) expr)->vartypmod;
- else
- type_mod = -1;
+ type_mod = exprTypmod(expr);
- /* Process target columns that will be receiving results */
- if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
+ if (colname == NULL)
{
-
- /*
- * insert or update query -- insert, update work only on one
- * relation, so multiple occurence of same resdomno is bogus
+ /* Generate a suitable column name for a column without any
+ * explicit 'AS ColumnName' clause.
*/
- rd = pstate->p_target_relation;
- Assert(rd != NULL);
- resdomno = attnameAttNum(rd, colname);
- if (resdomno <= 0)
- elog(ERROR, "Cannot assign to system attribute '%s'", colname);
- attrisset = attnameIsSet(rd, colname);
- attrtype = attnumTypeId(rd, resdomno);
- if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
- attrtype = GetArrayElementType(attrtype);
- attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
-
- /*
- * Check for InvalidOid since that seems to indicate a NULL
- * constant...
- */
- if (type_id != InvalidOid)
- {
- /* Mismatch on types? then try to coerce to target... */
- if (attrtype != type_id)
- {
- Oid typelem;
-
- if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
- typelem = typeTypElem(typeidType(attrtype));
- else
- typelem = attrtype;
-
- expr = CoerceTargetExpr(pstate, expr, type_id, typelem);
-
- if (!HeapTupleIsValid(expr))
- elog(ERROR, "Attribute '%s' is of type '%s'"
- " but expression is of type '%s'"
- "\n\tYou will need to rewrite or cast the expression",
- colname,
- typeidTypeName(attrtype),
- typeidTypeName(type_id));
- }
-
- /*
- * Apparently going to a fixed-length string? Then explicitly
- * size for storage...
- */
- if (attrtypmod > 0)
- expr = SizeTargetExpr(pstate, expr, attrtype, attrtypmod);
- }
-
- if (arrayRef != NIL)
- {
- Expr *target_expr;
- Attr *att = makeNode(Attr);
- List *ar = arrayRef;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
-
- att->relname = pstrdup(RelationGetRelationName(rd)->data);
- att->attrs = lcons(makeString(colname), NIL);
- target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
- &pstate->p_last_resno,
- EXPR_COLUMN_FIRST);
- while (ar != NIL)
- {
- A_Indices *ind = lfirst(ar);
-
- if (lowerIndexpr || (!upperIndexpr && ind->lidx))
- {
-
- /*
- * XXX assume all lowerIndexpr is non-null in this
- * case
- */
- lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
- }
- upperIndexpr = lappend(upperIndexpr, ind->uidx);
- ar = lnext(ar);
- }
-
- expr = (Node *) make_array_set(target_expr,
- upperIndexpr,
- lowerIndexpr,
- (Expr *) expr);
- attrtype = attnumTypeId(rd, resdomno);
- attrtypmod = get_atttypmod(RelationGetRelid(rd), resdomno);
- }
- }
- else
- {
- resdomno = pstate->p_last_resno++;
- attrtype = type_id;
- attrtypmod = type_mod;
+ colname = FigureColname(expr, node);
}
- resnode = makeResdom((AttrNumber) resdomno,
- (Oid) attrtype,
- attrtypmod,
+ resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
+ type_id,
+ type_mod,
colname,
(Index) 0,
- (Oid) 0,
+ (Oid) InvalidOid,
resjunk);
return makeTargetEntry(resnode, expr);
-} /* MakeTargetEntryExpr() */
-
-/*
- * MakeTargetEntryCase()
- * Make a TargetEntry from a case node.
- */
-static TargetEntry *
-MakeTargetEntryCase(ParseState *pstate,
- ResTarget *res)
-{
- TargetEntry *tent;
- CaseExpr *expr;
- Resdom *resnode;
- int resdomno;
- Oid type_id;
- int32 type_mod;
-
- expr = (CaseExpr *) transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-
- type_id = expr->casetype;
- type_mod = -1;
- handleTargetColname(pstate, &res->name, NULL, NULL);
- if (res->name == NULL)
- res->name = FigureColname((Node *) expr, res->val);
-
- resdomno = pstate->p_last_resno++;
- resnode = makeResdom((AttrNumber) resdomno,
- (Oid) type_id,
- type_mod,
- res->name,
- (Index) 0,
- (Oid) 0,
- false);
-
- tent = makeNode(TargetEntry);
- tent->resdom = resnode;
- tent->expr = (Node *) expr;
+}
- return tent;
-} /* MakeTargetEntryCase() */
/*
- * MakeTargetEntryComplex()
- * Make a TargetEntry from a complex node.
+ * transformTargetList()
+ * Turns a list of ResTarget's into a list of TargetEntry's.
+ *
+ * At this point, we don't care whether we are doing SELECT, INSERT,
+ * or UPDATE; we just transform the given expressions.
*/
-static TargetEntry *
-MakeTargetEntryComplex(ParseState *pstate,
- ResTarget *res)
+List *
+transformTargetList(ParseState *pstate, List *targetlist)
{
- Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
+ List *p_target = NIL;
- handleTargetColname(pstate, &res->name, NULL, NULL);
- /* note indirection has not been transformed */
- if (pstate->p_is_insert && res->indirection != NIL)
+ while (targetlist != NIL)
{
- /* this is an array assignment */
- char *val;
- char *str,
- *save_str;
- List *elt;
- int i = 0,
- ndims;
- int lindx[MAXDIM],
- uindx[MAXDIM];
- int resdomno;
- Relation rd;
- Value *constval;
-
- if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
- elog(ERROR, "String constant expected (internal error)");
-
- val = (char *) textout((struct varlena *)
- ((Const *) expr)->constvalue);
- str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
- foreach(elt, res->indirection)
- {
- A_Indices *aind = (A_Indices *) lfirst(elt);
+ ResTarget *res = (ResTarget *) lfirst(targetlist);
- aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
- if (!IsA(aind->uidx, Const))
- elog(ERROR, "Array Index for Append should be a constant");
+ if (IsA(res->val, Attr))
+ {
+ Attr *att = (Attr *) res->val;
- uindx[i] = ((Const *) aind->uidx)->constvalue;
- if (aind->lidx != NULL)
+ if (att->relname != NULL && strcmp(att->relname, "*") == 0)
{
- aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
- if (!IsA(aind->lidx, Const))
- elog(ERROR, "Array Index for Append should be a constant");
-
- lindx[i] = ((Const *) aind->lidx)->constvalue;
+ /*
+ * Target item is a single '*', expand all tables
+ * (eg. SELECT * FROM emp)
+ */
+ p_target = nconc(p_target,
+ ExpandAllTables(pstate));
+ }
+ else if (att->attrs != NIL &&
+ strcmp(strVal(lfirst(att->attrs)), "*") == 0)
+ {
+ /*
+ * Target item is relation.*, expand that table
+ * (eg. SELECT emp.*, dname FROM emp, dept)
+ */
+ p_target = nconc(p_target,
+ expandAll(pstate,
+ att->relname,
+ att->relname,
+ &pstate->p_last_resno));
}
else
- lindx[i] = 1;
- if (lindx[i] > uindx[i])
- elog(ERROR, "Lower index cannot be greater than upper index");
-
- sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
- str += strlen(str);
- i++;
+ {
+ /* Plain Attr node, treat it as an expression */
+ p_target = lappend(p_target,
+ transformTargetEntry(pstate,
+ res->val,
+ NULL,
+ res->name,
+ false));
+ }
}
- sprintf(str, "=%s", val);
- rd = pstate->p_target_relation;
- Assert(rd != NULL);
- resdomno = attnameAttNum(rd, res->name);
- ndims = attnumAttNelems(rd, resdomno);
- if (i != ndims)
- elog(ERROR, "Array dimensions do not match");
-
- constval = makeNode(Value);
- constval->type = T_String;
- constval->val.str = save_str;
- return MakeTargetEntryExpr(pstate, res->name,
- (Node *) make_const(constval),
- NULL, false);
- pfree(save_str);
- }
- else
- {
- /* this is not an array assignment */
- char *colname = res->name;
-
- if (colname == NULL)
+ else
{
-
- /*
- * if you're wondering why this is here, look at the yacc
- * grammar for why a name can be missing. -ay
- */
- colname = FigureColname(expr, res->val);
+ /* Everything else but Attr */
+ p_target = lappend(p_target,
+ transformTargetEntry(pstate,
+ res->val,
+ NULL,
+ res->name,
+ false));
}
- if (res->indirection)
- {
- List *ilist = res->indirection;
-
- while (ilist != NIL)
- {
- A_Indices *ind = lfirst(ilist);
- ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
- ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
- ilist = lnext(ilist);
- }
- }
- res->name = colname;
- return MakeTargetEntryExpr(pstate, res->name, expr,
- res->indirection, false);
+ targetlist = lnext(targetlist);
}
+
+ return p_target;
}
+
/*
- * MakeTargetEntryAttr()
- * Make a TargetEntry from a complex node.
+ * updateTargetListEntry()
+ * This is used in INSERT and UPDATE statements only. It prepares a
+ * TargetEntry for assignment to a column of the target table.
+ * This includes coercing the given value to the target column's type
+ * (if necessary), and dealing with any subscripts attached to the target
+ * column itself.
+ *
+ * pstate parse state
+ * tle target list entry to be modified
+ * colname target column name (ie, name of attribute to be assigned to)
+ * indirection subscripts for target column, if any
*/
-static TargetEntry *
-MakeTargetEntryAttr(ParseState *pstate,
- ResTarget *res)
+void
+updateTargetListEntry(ParseState *pstate,
+ TargetEntry *tle,
+ char *colname,
+ List *indirection)
{
- Oid type_id;
- int32 type_mod;
- Attr *att = (Attr *) res->val;
- Node *result;
- char *attrname;
- char *resname;
- Resdom *resnode;
+ Oid type_id = exprType(tle->expr); /* type of value provided */
+ Oid attrtype; /* type of target column */
+ int32 attrtypmod;
+ Resdom *resnode = tle->resdom;
+ Relation rd = pstate->p_target_relation;
int resdomno;
- List *attrs = att->attrs;
- TargetEntry *tent;
- attrname = strVal(lfirst(att->attrs));
+ Assert(rd != NULL);
+ resdomno = attnameAttNum(rd, colname);
+ if (resdomno <= 0)
+ elog(ERROR, "Cannot assign to system attribute '%s'", colname);
+ attrtype = attnumTypeId(rd, resdomno);
+ attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
/*
- * Target item is fully specified: ie. relation.attribute
+ * If there are subscripts on the target column, prepare an
+ * array assignment expression. This will generate an array value
+ * that the source value has been inserted into, which can then
+ * be placed in the new tuple constructed by INSERT or UPDATE.
+ * Note that transformArraySubscripts takes care of type coercion.
*/
- result = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno, EXPR_COLUMN_FIRST);
- handleTargetColname(pstate, &res->name, att->relname, attrname);
- if (att->indirection != NIL)
+ if (indirection)
{
- List *ilist = att->indirection;
-
- while (ilist != NIL)
+ Attr *att = makeNode(Attr);
+ Node *arrayBase;
+ ArrayRef *aref;
+
+ att->relname = pstrdup(RelationGetRelationName(rd)->data);
+ att->attrs = lcons(makeString(colname), NIL);
+ arrayBase = ParseNestedFuncOrColumn(pstate, att,
+ &pstate->p_last_resno,
+ EXPR_COLUMN_FIRST);
+ aref = transformArraySubscripts(pstate, arrayBase,
+ indirection,
+ pstate->p_is_insert,
+ tle->expr);
+ if (pstate->p_is_insert)
{
- A_Indices *ind = lfirst(ilist);
-
- ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
- ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
- ilist = lnext(ilist);
+ /*
+ * The command is INSERT INTO table (arraycol[subscripts]) ...
+ * so there is not really a source array value to work with.
+ * Let the executor do something reasonable, if it can.
+ * Notice that we forced transformArraySubscripts to treat
+ * the subscripting op as an array-slice op above, so the
+ * source data will have been coerced to array type.
+ */
+ aref->refexpr = NULL; /* signal there is no source array */
}
- result = (Node *) make_array_ref(result, att->indirection);
+ tle->expr = (Node *) aref;
}
- type_id = exprType(result);
- if (nodeTag(result) == T_Var)
- type_mod = ((Var *) result)->vartypmod;
else
- type_mod = -1;
- /* move to last entry */
- while (lnext(attrs) != NIL)
- attrs = lnext(attrs);
- resname = (res->name) ? res->name : strVal(lfirst(attrs));
- if (pstate->p_is_insert || pstate->p_is_update)
{
- Relation rd;
-
/*
- * insert or update query -- insert, update work only on one
- * relation, so multiple occurence of same resdomno is bogus
+ * For normal non-subscripted target column, do type checking
+ * and coercion. But accept InvalidOid, which indicates the
+ * source is a NULL constant.
*/
- rd = pstate->p_target_relation;
- Assert(rd != NULL);
- resdomno = attnameAttNum(rd, res->name);
- }
- else
- resdomno = pstate->p_last_resno++;
- resnode = makeResdom((AttrNumber) resdomno,
- (Oid) type_id,
- type_mod,
- resname,
- (Index) 0,
- (Oid) 0,
- false);
- tent = makeNode(TargetEntry);
- tent->resdom = resnode;
- tent->expr = result;
- return tent;
-}
-
-
-/* transformTargetList()
- * Turns a list of ResTarget's into a list of TargetEntry's.
- */
-List *
-transformTargetList(ParseState *pstate, List *targetlist)
-{
- List *p_target = NIL;
- List *tail_p_target = NIL;
-
- while (targetlist != NIL)
- {
- ResTarget *res = (ResTarget *) lfirst(targetlist);
- TargetEntry *tent = NULL;
-
- switch (nodeTag(res->val))
- {
- case T_Ident:
- {
- char *identname;
-
- identname = ((Ident *) res->val)->name;
- tent = MakeTargetEntryIdent(pstate,
- (Node *) res->val, &res->name, NULL, identname, false);
- break;
- }
- case T_ParamNo:
- case T_FuncCall:
- case T_A_Const:
- case T_A_Expr:
- {
- tent = MakeTargetEntryComplex(pstate, res);
- break;
- }
- case T_CaseExpr:
- {
- tent = MakeTargetEntryCase(pstate, res);
- break;
- }
- case T_Attr:
- {
- bool expand_star = false;
- char *attrname;
- Attr *att = (Attr *) res->val;
-
- /*
- * Target item is a single '*', expand all tables (eg.
- * SELECT * FROM emp)
- */
- if (att->relname != NULL && !strcmp(att->relname, "*"))
- {
- if (tail_p_target == NIL)
- p_target = tail_p_target = ExpandAllTables(pstate);
- else
- lnext(tail_p_target) = ExpandAllTables(pstate);
- expand_star = true;
- }
- else
- {
-
- /*
- * Target item is relation.*, expand the table
- * (eg. SELECT emp.*, dname FROM emp, dept)
- */
- attrname = strVal(lfirst(att->attrs));
- if (att->attrs != NIL && !strcmp(attrname, "*"))
- {
-
- /*
- * tail_p_target is the target list we're
- * building in the while loop. Make sure we
- * fix it after appending more nodes.
- */
- if (tail_p_target == NIL)
- p_target = tail_p_target = expandAll(pstate, att->relname,
- att->relname, &pstate->p_last_resno);
- else
- lnext(tail_p_target) = expandAll(pstate, att->relname, att->relname,
- &pstate->p_last_resno);
- expand_star = true;
- }
- }
- if (expand_star)
- {
- while (lnext(tail_p_target) != NIL)
- /* make sure we point to the last target entry */
- tail_p_target = lnext(tail_p_target);
-
- /*
- * skip rest of while loop
- */
- targetlist = lnext(targetlist);
- continue;
- }
- else
- {
- tent = MakeTargetEntryAttr(pstate, res);
- break;
- }
- }
- default:
- /* internal error */
- elog(ERROR, "Unable to transform targetlist (internal error)");
- break;
- }
-
- if (p_target == NIL)
- p_target = tail_p_target = lcons(tent, NIL);
- else
+ if (type_id != InvalidOid)
{
- lnext(tail_p_target) = lcons(tent, NIL);
- tail_p_target = lnext(tail_p_target);
+ if (type_id != attrtype)
+ {
+ tle->expr = CoerceTargetExpr(pstate, tle->expr,
+ type_id, attrtype);
+ if (tle->expr == NULL)
+ elog(ERROR, "Attribute '%s' is of type '%s'"
+ " but expression is of type '%s'"
+ "\n\tYou will need to rewrite or cast the expression",
+ colname,
+ typeidTypeName(attrtype),
+ typeidTypeName(type_id));
+ }
+ /*
+ * If the target is a fixed-length type, it may need a length
+ * coercion as well as a type coercion.
+ */
+ if (attrtypmod > 0 &&
+ attrtypmod != exprTypmod(tle->expr))
+ tle->expr = SizeTargetExpr(pstate, tle->expr,
+ attrtype, attrtypmod);
}
- targetlist = lnext(targetlist);
}
- return p_target;
-} /* transformTargetList() */
+ /*
+ * The result of the target expression should now match the destination
+ * column's type. Also, reset the resname and resno to identify
+ * the destination column --- rewriter and planner depend on that!
+ */
+ resnode->restype = attrtype;
+ resnode->restypmod = attrtypmod;
+ resnode->resname = colname;
+ resnode->resno = (AttrNumber) resdomno;
+}
Node *
@@ -689,12 +297,19 @@ CoerceTargetExpr(ParseState *pstate,
expr = NULL;
return expr;
-} /* CoerceTargetExpr() */
+}
-/* SizeTargetExpr()
- * Apparently going to a fixed-length string?
- * Then explicitly size for storage...
+/*
+ * SizeTargetExpr()
+ *
+ * If the target column type possesses a function named for the type
+ * and having parameter signature (columntype, int4), we assume that
+ * the type requires coercion to its own length and that the said
+ * function should be invoked to do that.
+ *
+ * Currently, "bpchar" (ie, char(N)) is the only such type, but try
+ * to be more general than a hard-wired test...
*/
static Node *
SizeTargetExpr(ParseState *pstate,
@@ -702,13 +317,10 @@ SizeTargetExpr(ParseState *pstate,
Oid attrtype,
int32 attrtypmod)
{
- int i;
- HeapTuple ftup;
char *funcname;
Oid oid_array[MAXFARGS];
-
- FuncCall *func;
- A_Const *cons;
+ HeapTuple ftup;
+ int i;
funcname = typeidTypeName(attrtype);
oid_array[0] = attrtype;
@@ -725,6 +337,9 @@ SizeTargetExpr(ParseState *pstate,
if (HeapTupleIsValid(ftup))
{
+ FuncCall *func;
+ A_Const *cons;
+
func = makeNode(FuncCall);
func->funcname = funcname;
@@ -737,29 +352,27 @@ SizeTargetExpr(ParseState *pstate,
}
return expr;
-} /* SizeTargetExpr() */
+}
/*
* makeTargetNames -
* generate a list of column names if not supplied or
- * test supplied column names to make sure they are in target table
+ * test supplied column names to make sure they are in target table.
* (used exclusively for inserts)
*/
List *
makeTargetNames(ParseState *pstate, List *cols)
{
- List *tl = NULL;
-
- /* Generate ResTarget if not supplied */
-
if (cols == NIL)
{
- int numcol;
- int i;
+ /*
+ * Generate default column list for INSERT.
+ */
Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
+ int numcol = pstate->p_target_relation->rd_rel->relnatts;
+ int i;
- numcol = pstate->p_target_relation->rd_rel->relnatts;
for (i = 0; i < numcol; i++)
{
Ident *id = makeNode(Ident);
@@ -768,27 +381,30 @@ makeTargetNames(ParseState *pstate, List *cols)
StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN);
id->indirection = NIL;
id->isRel = false;
- if (tl == NIL)
- cols = tl = lcons(id, NIL);
- else
- {
- lnext(tl) = lcons(id, NIL);
- tl = lnext(tl);
- }
+ cols = lappend(cols, id);
}
}
else
{
+ /*
+ * Do initial validation of user-supplied INSERT column list.
+ */
+ List *tl;
+
foreach(tl, cols)
{
- List *nxt;
char *name = ((Ident *) lfirst(tl))->name;
+ List *nxt;
- /* elog on failure */
+ /* Lookup column name, elog on failure */
attnameAttNum(pstate->p_target_relation, name);
+ /* Check for duplicates */
foreach(nxt, lnext(tl))
- if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
- elog(ERROR, "Attribute '%s' should be specified only once", name);
+ {
+ if (strcmp(name, ((Ident *) lfirst(nxt))->name) == 0)
+ elog(ERROR, "Attribute '%s' specified more than once",
+ name);
+ }
}
}
@@ -804,57 +420,37 @@ static List *
ExpandAllTables(ParseState *pstate)
{
List *target = NIL;
- List *legit_rtable = NIL;
List *rt,
*rtable;
rtable = pstate->p_rtable;
if (pstate->p_is_rule)
{
-
/*
* skip first two entries, "*new*" and "*current*"
*/
- rtable = lnext(lnext(pstate->p_rtable));
+ rtable = lnext(lnext(rtable));
}
/* SELECT *; */
- if (rtable == NULL)
+ if (rtable == NIL)
elog(ERROR, "Wildcard with no tables specified.");
- /*
- * go through the range table and make a list of range table entries
- * which we will expand.
- */
foreach(rt, rtable)
{
RangeTblEntry *rte = lfirst(rt);
/*
- * we only expand those specify in the from clause. (This will
+ * we only expand those listed in the from clause. (This will
* also prevent us from using the wrong table in inserts: eg.
* tenk2 in "insert into tenk2 select * from tenk1;")
*/
if (!rte->inFromCl)
continue;
- legit_rtable = lappend(legit_rtable, rte);
- }
-
- foreach(rt, legit_rtable)
- {
- RangeTblEntry *rte = lfirst(rt);
- List *temp = target;
- if (temp == NIL)
- target = expandAll(pstate, rte->relname, rte->refname,
- &pstate->p_last_resno);
- else
- {
- while (temp != NIL && lnext(temp) != NIL)
- temp = lnext(temp);
- lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
- &pstate->p_last_resno);
- }
+ target = nconc(target,
+ expandAll(pstate, rte->relname, rte->refname,
+ &pstate->p_last_resno));
}
return target;
}
@@ -862,29 +458,47 @@ ExpandAllTables(ParseState *pstate)
/*
* FigureColname -
* if the name of the resulting column is not specified in the target
- * list, we have to guess.
+ * list, we have to guess a suitable name. The SQL spec provides some
+ * guidance, but not much...
*
*/
static char *
FigureColname(Node *expr, Node *resval)
{
- switch (nodeTag(expr))
+ /* Some of these are easiest to do with the untransformed node */
+ switch (nodeTag(resval))
{
- case T_Aggref:
- return (char *) ((Aggref *) expr)->aggname;
- case T_Expr:
- if (((Expr *) expr)->opType == FUNC_EXPR)
+ case T_Ident:
+ return ((Ident *) resval)->name;
+ case T_Attr:
{
- if (nodeTag(resval) == T_FuncCall)
- return ((FuncCall *) resval)->funcname;
+ List *attrs = ((Attr *) resval)->attrs;
+ if (attrs)
+ {
+ while (lnext(attrs) != NIL)
+ attrs = lnext(attrs);
+ return strVal(lfirst(attrs));
+ }
}
break;
+ default:
+ break;
+ }
+ /* Otherwise, work with the transformed node */
+ switch (nodeTag(expr))
+ {
+ case T_Expr:
+ if (((Expr *) expr)->opType == FUNC_EXPR && IsA(resval, FuncCall))
+ return ((FuncCall *) resval)->funcname;
+ break;
+ case T_Aggref:
+ return ((Aggref *) expr)->aggname;
case T_CaseExpr:
{
char *name;
name = FigureColname(((CaseExpr *) expr)->defresult, resval);
- if (!strcmp(name, "?column?"))
+ if (strcmp(name, "?column?") == 0)
name = "case";
return name;
}