diff options
Diffstat (limited to 'src/backend/parser/parse_target.c')
-rw-r--r-- | src/backend/parser/parse_target.c | 874 |
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; } |