X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fparser%2Fparse_target.c;h=10892bc292d73014c9645a36e2b2b25ad30150d9;hb=2cf57c8f8d060711c1ad7e1dd6cc1115a2839b47;hp=271645573e0f04401c259136a592d5312481d93b;hpb=2c482cdbf20854f75b36fdf15b9e68403473228f;p=postgresql diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 271645573e..10892bc292 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -3,314 +3,201 @@ * parse_target.c * handle target lists * - * Copyright (c) 1994, Regents of the University of California + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.8 1998/02/10 04:01:57 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.101 2003/05/06 00:20:32 tgl Exp $ * *------------------------------------------------------------------------- */ -#include -#include -#include - #include "postgres.h" -#include "catalog/pg_type.h" + +#include "miscadmin.h" #include "nodes/makefuncs.h" -#include "nodes/primnodes.h" +#include "parser/parsetree.h" +#include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" -#include "parser/parse_node.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" +#include "parser/parse_type.h" #include "utils/builtins.h" -#include "utils/lsyscache.h" -static List *expandAllTables(ParseState *pstate); -static char *figureColname(Node *expr, Node *resval); -static TargetEntry *make_targetlist_expr(ParseState *pstate, - char *colname, + +static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var); +static List *ExpandAllTables(ParseState *pstate); +static char *FigureColname(Node *node); +static int FigureColnameInternal(Node *node, char **name); + + +/* + * 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. + * + * 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 * +transformTargetEntry(ParseState *pstate, + Node *node, Node *expr, - List *arrayRef); + char *colname, + bool resjunk) +{ + Oid type_id; + int32 type_mod; + Resdom *resnode; + + /* Transform the node if caller didn't do it already */ + if (expr == NULL) + expr = transformExpr(pstate, node); + + if (IsA(expr, RangeVar)) + elog(ERROR, "You can't use relation names alone in the target list, try relation.*."); + + type_id = exprType(expr); + type_mod = exprTypmod(expr); + + if (colname == NULL) + { + /* + * Generate a suitable column name for a column without any + * explicit 'AS ColumnName' clause. + */ + colname = FigureColname(node); + } + + resnode = makeResdom((AttrNumber) pstate->p_next_resno++, + type_id, + type_mod, + colname, + resjunk); + + return makeTargetEntry(resnode, (Expr *) expr); +} + /* - * transformTargetList - - * turns a list of ResTarget's into a list of TargetEntry's + * 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. */ 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 = makeNode(TargetEntry); - switch (nodeTag(res->val)) + if (IsA(res->val, ColumnRef)) { - case T_Ident: - { - Node *expr; - Oid type_id; - int type_mod; - char *identname; - char *resname; - - identname = ((Ident *) res->val)->name; - handleTargetColname(pstate, &res->name, NULL, identname); - - /* - * here we want to look for column names only, not relation - * names (even though they can be stored in Ident nodes, too) - */ - expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); - type_id = exprType(expr); - if (nodeTag(expr) == T_Var) - type_mod = ((Var *)expr)->vartypmod; - else - type_mod = -1; - resname = (res->name) ? res->name : identname; - tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++, - (Oid) type_id, - type_mod, - resname, - (Index) 0, - (Oid) 0, - 0); - - tent->expr = expr; - break; - } - case T_ParamNo: - case T_FuncCall: - case T_A_Const: - case T_A_Expr: - { - Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); - - handleTargetColname(pstate, &res->name, NULL, NULL); - /* note indirection has not been transformed */ - if (pstate->p_is_insert && res->indirection != 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, "yyparse: string constant expected"); - - 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); - - aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); - if (!IsA(aind->uidx, Const)) - elog(ERROR, - "Array Index for Append should be a constant"); - uindx[i] = ((Const *) aind->uidx)->constvalue; - if (aind->lidx != NULL) - { - 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; - } - else - { - lindx[i] = 1; - } - if (lindx[i] > uindx[i]) - elog(ERROR, "yyparse: lower index cannot be greater than upper index"); - sprintf(str, "[%d:%d]", lindx[i], uindx[i]); - str += strlen(str); - i++; - } - 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, "yyparse: array dimensions do not match"); - constval = makeNode(Value); - constval->type = T_String; - constval->val.str = save_str; - tent = make_targetlist_expr(pstate, res->name, - (Node *) make_const(constval), - NULL); - pfree(save_str); - } - else - { - char *colname = res->name; + ColumnRef *cref = (ColumnRef *) res->val; + List *fields = cref->fields; + int numnames = length(fields); - /* this is not an array assignment */ - if (colname == NULL) + if (numnames == 1 && strcmp(strVal(lfirst(fields)), "*") == 0) + { + /* + * Target item is a single '*', expand all tables (eg. + * SELECT * FROM emp) + */ + p_target = nconc(p_target, + ExpandAllTables(pstate)); + } + else if (strcmp(strVal(nth(numnames - 1, fields)), "*") == 0) + { + /* + * Target item is relation.*, expand that table (eg. + * SELECT emp.*, dname FROM emp, dept) + */ + char *schemaname; + char *relname; + RangeTblEntry *rte; + int sublevels_up; + + switch (numnames) + { + case 2: + schemaname = NULL; + relname = strVal(lfirst(fields)); + break; + case 3: + schemaname = strVal(lfirst(fields)); + relname = strVal(lsecond(fields)); + break; + case 4: { + char *name1 = strVal(lfirst(fields)); /* - * if you're wondering why this is here, look - * at the yacc grammar for why a name can be - * missing. -ay + * We check the catalog name and then ignore + * it. */ - colname = figureColname(expr, res->val); + if (strcmp(name1, DatabaseName) != 0) + elog(ERROR, "Cross-database references are not implemented"); + schemaname = strVal(lsecond(fields)); + relname = strVal(lthird(fields)); + break; } - 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; - tent = make_targetlist_expr(pstate, res->name, expr, - res->indirection); - } - break; + default: + elog(ERROR, "Invalid qualified name syntax (too many names)"); + schemaname = NULL; /* keep compiler quiet */ + relname = NULL; + break; } - case T_Attr: - { - Oid type_id; - int type_mod; - Attr *att = (Attr *) res->val; - Node *result; - char *attrname; - char *resname; - Resdom *resnode; - List *attrs = att->attrs; - - /* - * 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); - - 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; - } - /* - * 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, "*")) - { + rte = refnameRangeTblEntry(pstate, schemaname, relname, + &sublevels_up); + if (rte == NULL) + rte = addImplicitRTE(pstate, makeRangeVar(schemaname, + relname)); - /* - * 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); - while (lnext(tail_p_target) != NIL) - /* make sure we point to the last target entry */ - tail_p_target = lnext(tail_p_target); - - /* - * skip the rest of the while loop - */ - targetlist = lnext(targetlist); - continue; - } - - - /* - * Target item is fully specified: ie. relation.attribute - */ - result = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,EXPR_COLUMN_FIRST); - handleTargetColname(pstate, &res->name, att->relname, attrname); - if (att->indirection != NIL) - { - List *ilist = att->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); - } - result = (Node *) make_array_ref(result, att->indirection); - } - 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)); - resnode = makeResdom((AttrNumber) pstate->p_last_resno++, - (Oid) type_id, - type_mod, - resname, - (Index) 0, - (Oid) 0, - 0); - tent->resdom = resnode; - tent->expr = result; - break; - } - default: - /* internal error */ - elog(ERROR, - "internal error: do not know how to transform targetlist"); - break; + p_target = nconc(p_target, + expandRelAttrs(pstate, rte)); + } + else + { + /* Plain ColumnRef node, treat it as an expression */ + p_target = lappend(p_target, + transformTargetEntry(pstate, + res->val, + NULL, + res->name, + false)); + } } - - if (p_target == NIL) + else if (IsA(res->val, InsertDefault)) { - p_target = tail_p_target = lcons(tent, NIL); + InsertDefault *newnode = makeNode(InsertDefault); + + /* + * If this is a DEFAULT element, we make a junk entry which + * will get dropped on return to transformInsertStmt(). + */ + p_target = lappend(p_target, newnode); } else { - lnext(tail_p_target) = lcons(tent, NIL); - tail_p_target = lnext(tail_p_target); + /* Everything else but ColumnRef and InsertDefault */ + p_target = lappend(p_target, + transformTargetEntry(pstate, + res->val, + NULL, + res->name, + false)); } + targetlist = lnext(targetlist); } @@ -319,342 +206,425 @@ transformTargetList(ParseState *pstate, List *targetlist) /* - * make_targetlist_expr - - * make a TargetEntry from an expression + * markTargetListOrigins() + * Mark targetlist columns that are simple Vars with the source + * table's OID and column number. * - * arrayRef is a list of transformed A_Indices + * Currently, this is done only for SELECT targetlists, since we only + * need the info if we are going to send it to the frontend. */ -static TargetEntry * -make_targetlist_expr(ParseState *pstate, - char *colname, - Node *expr, - List *arrayRef) +void +markTargetListOrigins(ParseState *pstate, List *targetlist) { - Oid type_id, - attrtype; - int type_mod, - attrtypmod; - int resdomno; - Relation rd; - bool attrisset; - TargetEntry *tent; - Resdom *resnode; + List *l; - if (expr == NULL) - elog(ERROR, "make_targetlist_expr: invalid use of NULL expression"); - - type_id = exprType(expr); - if (nodeTag(expr) == T_Var) - type_mod = ((Var *)expr)->vartypmod; - else - type_mod = -1; - - /* Processes target columns that will be receiving results */ - if (pstate->p_is_insert || pstate->p_is_update) + foreach(l, targetlist) { + TargetEntry *tle = (TargetEntry *) lfirst(l); - /* - * insert or update query -- insert, update work only on one - * relation, so multiple occurence of same resdomno is bogus - */ - rd = pstate->p_target_relation; - Assert(rd != NULL); - resdomno = attnameAttNum(rd, 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; -#if 0 - if (Input_is_string && Typecast_ok) - { - Datum val; + markTargetListOrigin(pstate, tle->resdom, (Var *) tle->expr); + } +} - if (type_id == typeTypeId(type("unknown"))) - { - val = (Datum) textout((struct varlena *) - ((Const) lnext(expr))->constvalue); - } - else - { - val = ((Const) lnext(expr))->constvalue; - } - if (attrisset) - { - lnext(expr) = makeConst(attrtype, - attrlen, - val, - false, - true, - true, /* is set */ - false); - } - else - { - lnext(expr) = - makeConst(attrtype, - attrlen, - (Datum) fmgr(typeidRetinfunc(attrtype), - val, typeidTypElem(attrtype), -1), - false, - true /* Maybe correct-- 80% chance */ , - false, /* is not a set */ - false); - } - } - else if ((Typecast_ok) && (attrtype != type_id)) - { - lnext(expr) = - parser_typecast2(expr, typeidType(attrtype)); - } - else if (attrtype != type_id) - { - if ((attrtype == INT2OID) && (type_id == INT4OID)) - lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */ - else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID)) - lfirst(expr) = lispInteger(FLOAT4OID); - else - elog(ERROR, "unequal type in tlist : %s \n", colname); - } +/* + * markTargetListOrigin() + * If 'var' is a Var of a plain relation, mark 'res' with its origin + * + * This is split out so it can recurse for join references. Note that we + * do not drill down into views, but report the view as the column owner. + */ +static void +markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var) +{ + RangeTblEntry *rte; + AttrNumber attnum; - Input_is_string = false; - Input_is_integer = false; - Typecast_ok = true; -#endif + if (var == NULL || !IsA(var, Var)) + return; + Assert(var->varno > 0 && + (int) var->varno <= length(pstate->p_rtable)); + rte = rt_fetch(var->varno, pstate->p_rtable); + attnum = var->varattno; - if (attrtype != type_id) - { - if (IsA(expr, Const)) + switch (rte->rtekind) + { + case RTE_RELATION: + /* It's a table or view, report it */ + res->resorigtbl = rte->relid; + res->resorigcol = attnum; + break; + case RTE_SUBQUERY: { - /* try to cast the constant */ - if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx)) + /* Subselect-in-FROM: copy up from the subselect */ + List *subtl; + + foreach(subtl, rte->subquery->targetList) { - /* updating a single item */ - Oid typelem = typeidTypElem(attrtype); + TargetEntry *subte = (TargetEntry *) lfirst(subtl); - expr = (Node *) parser_typecast2(expr, - type_id, - typeidType(typelem), - attrtypmod); + if (subte->resdom->resjunk || + subte->resdom->resno != attnum) + continue; + res->resorigtbl = subte->resdom->resorigtbl; + res->resorigcol = subte->resdom->resorigcol; + break; } - else - expr = (Node *) parser_typecast2(expr, - type_id, - typeidType(attrtype), - attrtypmod); + /* falling off end of list shouldn't happen... */ + if (subtl == NIL) + elog(ERROR, "Subquery %s does not have attribute %d", + rte->eref->aliasname, attnum); } - else + break; + case RTE_JOIN: { - /* currently, we can't handle casting of expressions */ - elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'", - colname, - typeidTypeName(attrtype), - typeidTypeName(type_id)); + /* Join RTE --- recursively inspect the alias variable */ + Var *aliasvar; + + Assert(attnum > 0 && attnum <= length(rte->joinaliasvars)); + aliasvar = (Var *) nth(attnum - 1, rte->joinaliasvars); + markTargetListOrigin(pstate, res, aliasvar); } - } + break; + case RTE_SPECIAL: + case RTE_FUNCTION: + /* not a simple relation, leave it unmarked */ + break; + } +} - 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)) - { +/* + * 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) + * attrno target attribute number + * indirection subscripts for target column, if any + */ +void +updateTargetListEntry(ParseState *pstate, + TargetEntry *tle, + char *colname, + int attrno, + List *indirection) +{ + Oid type_id = exprType((Node *) tle->expr); /* type of value provided */ + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Resdom *resnode = tle->resdom; + Relation rd = pstate->p_target_relation; + + Assert(rd != NULL); + if (attrno <= 0) + elog(ERROR, "Cannot assign to system attribute '%s'", colname); + attrtype = attnumTypeId(rd, attrno); + attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; - /* - * XXX assume all lowerIndexpr is non-null in this - * case - */ - lowerIndexpr = lappend(lowerIndexpr, ind->lidx); - } - upperIndexpr = lappend(upperIndexpr, ind->uidx); - ar = lnext(ar); - } + /* + * 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. + */ + if (indirection) + { + Node *arrayBase; + ArrayRef *aref; - expr = (Node *) make_array_set(target_expr, - upperIndexpr, - lowerIndexpr, - (Expr *) expr); - attrtype = attnumTypeId(rd, resdomno); - attrtypmod = get_atttypmod(rd->rd_id, resdomno); + if (pstate->p_is_insert) + { + /* + * 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 force transformArraySubscripts to treat the + * subscripting op as an array-slice op below, so the source + * data will have been coerced to the array type. + */ + arrayBase = NULL; /* signal there is no source array */ + } + else + { + /* + * Build a Var for the array to be updated. + */ + arrayBase = (Node *) make_var(pstate, + pstate->p_target_rangetblentry, + attrno); } + + aref = transformArraySubscripts(pstate, + arrayBase, + attrtype, + attrtypmod, + indirection, + pstate->p_is_insert, + (Node *) tle->expr); + tle->expr = (Expr *) aref; } else { - resdomno = pstate->p_last_resno++; - attrtype = type_id; - attrtypmod = type_mod; + /* + * For normal non-subscripted target column, do type checking and + * coercion. But accept InvalidOid, which indicates the source is + * a NULL constant. (XXX is that still true?) + */ + if (type_id != InvalidOid) + { + tle->expr = (Expr *) + coerce_to_target_type(pstate, + (Node *) tle->expr, type_id, + attrtype, attrtypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); + if (tle->expr == NULL) + elog(ERROR, "column \"%s\" is of type %s" + " but expression is of type %s" + "\n\tYou will need to rewrite or cast the expression", + colname, + format_type_be(attrtype), + format_type_be(type_id)); + } } - tent = makeNode(TargetEntry); - - resnode = makeResdom((AttrNumber) resdomno, - (Oid) attrtype, - attrtypmod, - colname, - (Index) 0, - (Oid) 0, - 0); - - tent->resdom = resnode; - tent->expr = expr; - return tent; + /* + * 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) attrno; } + /* - * makeTargetNames - - * generate a list of column names if not supplied or - * test supplied column names to make sure they are in target table - * (used exclusively for inserts) + * checkInsertTargets - + * generate a list of INSERT column targets if not supplied, or + * test supplied column names to make sure they are in target table. + * Also return an integer list of the columns' attribute numbers. */ List * -makeTargetNames(ParseState *pstate, List *cols) +checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) { - List *tl = NULL; - - /* Generate ResTarget if not supplied */ + *attrnos = NIL; if (cols == NIL) { - int numcol; + /* + * 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; - AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs; - numcol = pstate->p_target_relation->rd_rel->relnatts; for (i = 0; i < numcol; i++) { - Ident *id = makeNode(Ident); - - id->name = palloc(NAMEDATALEN); - 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); - } + ResTarget *col; + + if (attr[i]->attisdropped) + continue; + + col = makeNode(ResTarget); + col->name = pstrdup(NameStr(attr[i]->attname)); + col->indirection = NIL; + col->val = NULL; + cols = lappend(cols, col); + *attrnos = lappendi(*attrnos, i + 1); } } else { + /* + * Do initial validation of user-supplied INSERT column list. + */ + List *tl; + foreach(tl, cols) { - List *nxt; - char *name = ((Ident *) lfirst(tl))->name; - - /* elog on failure */ - attnameAttNum(pstate->p_target_relation, name); - foreach(nxt, lnext(tl)) - if (!strcmp(name, ((Ident *) lfirst(nxt))->name)) - elog(ERROR, "Attribute '%s' should be specified only once", name); + char *name = ((ResTarget *) lfirst(tl))->name; + int attrno; + + /* Lookup column name, elog on failure */ + attrno = attnameAttNum(pstate->p_target_relation, name, false); + /* Check for duplicates */ + if (intMember(attrno, *attrnos)) + elog(ERROR, "Attribute '%s' specified more than once", name); + *attrnos = lappendi(*attrnos, attrno); } } - + return cols; } -/* - * expandAllTables - - * turns '*' (in the target list) into a list of attributes - * (of all relations in the range table) +/* ExpandAllTables() + * Turns '*' (in the target list) into a list of targetlist entries. + * + * tlist entries are generated for each relation appearing at the top level + * of the query's namespace, except for RTEs marked not inFromCl. (These + * may include NEW/OLD pseudo-entries, implicit RTEs, etc.) */ static List * -expandAllTables(ParseState *pstate) +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)); - } + bool found_table = false; + List *ns; - /* this should not happen */ - if (rtable == NULL) - elog(ERROR, "cannot expand: null p_rtable"); - - /* - * go through the range table and make a list of range table entries - * which we will expand. - */ - foreach(rt, rtable) + foreach(ns, pstate->p_namespace) { - RangeTblEntry *rte = lfirst(rt); + Node *n = (Node *) lfirst(ns); + RangeTblEntry *rte; + + if (IsA(n, RangeTblRef)) + rte = rt_fetch(((RangeTblRef *) n)->rtindex, + pstate->p_rtable); + else if (IsA(n, JoinExpr)) + rte = rt_fetch(((JoinExpr *) n)->rtindex, + pstate->p_rtable); + else + { + elog(ERROR, "ExpandAllTables: unexpected node (internal error)" + "\n\t%s", nodeToString(n)); + rte = NULL; /* keep compiler quiet */ + } /* - * we only expand those specify 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;") + * Ignore added-on relations that were not listed in the FROM + * clause. */ if (!rte->inFromCl) continue; - legit_rtable = lappend(legit_rtable, rte); + + found_table = true; + target = nconc(target, expandRelAttrs(pstate, rte)); } - foreach(rt, legit_rtable) - { - RangeTblEntry *rte = lfirst(rt); - List *temp = target; + /* Check for SELECT *; */ + if (!found_table) + elog(ERROR, "Wildcard with no tables specified not allowed"); - 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); - } - } return target; } /* - * figureColname - + * 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... * + * Note that the argument is the *untransformed* parse tree for the target + * item. This is a shade easier to work with than the transformed tree. */ static char * -figureColname(Node *expr, Node *resval) +FigureColname(Node *node) { - switch (nodeTag(expr)) + char *name = NULL; + + FigureColnameInternal(node, &name); + if (name != NULL) + return name; + /* default result if we can't guess anything */ + return "?column?"; +} + +static int +FigureColnameInternal(Node *node, char **name) +{ + int strength = 0; + + if (node == NULL) + return strength; + + switch (nodeTag(node)) { - case T_Aggreg: - return (char *) ((Aggreg *) expr)->aggname; - case T_Expr: - if (((Expr *) expr)->opType == FUNC_EXPR) + case T_ColumnRef: { - if (nodeTag(resval) == T_FuncCall) - return ((FuncCall *) resval)->funcname; + char *cname = strVal(llast(((ColumnRef *) node)->fields)); + + if (strcmp(cname, "*") != 0) + { + *name = cname; + return 2; + } + } + break; + case T_ExprFieldSelect: + { + ExprFieldSelect *efs = (ExprFieldSelect *) node; + + if (efs->fields) + { + char *fname = strVal(llast(efs->fields)); + + if (strcmp(fname, "*") != 0) + { + *name = fname; + return 2; + } + } + return FigureColnameInternal(efs->arg, name); + } + break; + case T_FuncCall: + *name = strVal(llast(((FuncCall *) node)->funcname)); + return 2; + case T_A_Expr: + /* make nullif() act like a regular function */ + if (((A_Expr *) node)->kind == AEXPR_NULLIF) + { + *name = "nullif"; + return 2; + } + break; + case T_A_Const: + if (((A_Const *) node)->typename != NULL) + { + *name = strVal(llast(((A_Const *) node)->typename->names)); + return 1; } break; + case T_TypeCast: + strength = FigureColnameInternal(((TypeCast *) node)->arg, + name); + if (strength <= 1) + { + if (((TypeCast *) node)->typename != NULL) + { + *name = strVal(llast(((TypeCast *) node)->typename->names)); + return 1; + } + } + break; + case T_CaseExpr: + strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, + name); + if (strength <= 1) + { + *name = "case"; + return 1; + } + break; + case T_ArrayExpr: + /* make ARRAY[] act like a function */ + *name = "array"; + return 2; + case T_CoalesceExpr: + /* make coalesce() act like a regular function */ + *name = "coalesce"; + return 2; default: break; } - return "?column?"; + return strength; }