#include "parser/parse_node.h"
Go to the source code of this file.
Functions | |
List * | transformTargetList (ParseState *pstate, List *targetlist, ParseExprKind exprKind) |
List * | transformExpressionList (ParseState *pstate, List *exprlist, ParseExprKind exprKind) |
void | markTargetListOrigins (ParseState *pstate, List *targetlist) |
TargetEntry * | transformTargetEntry (ParseState *pstate, Node *node, Node *expr, ParseExprKind exprKind, char *colname, bool resjunk) |
Expr * | transformAssignedExpr (ParseState *pstate, Expr *expr, ParseExprKind exprKind, char *colname, int attrno, List *indirection, int location) |
void | updateTargetListEntry (ParseState *pstate, TargetEntry *tle, char *colname, int attrno, List *indirection, int location) |
List * | checkInsertTargets (ParseState *pstate, List *cols, List **attrnos) |
TupleDesc | expandRecordVariable (ParseState *pstate, Var *var, int levelsup) |
char * | FigureColname (Node *node) |
char * | FigureIndexColname (Node *node) |
List* checkInsertTargets | ( | ParseState * | pstate, | |
List * | cols, | |||
List ** | attrnos | |||
) |
Definition at line 868 of file parse_target.c.
References attnameAttNum(), tupleDesc::attrs, bms_add_member(), bms_is_member(), ereport, errcode(), errmsg(), ERROR, i, ResTarget::indirection, InvalidAttrNumber, lappend(), lappend_int(), lfirst, ResTarget::location, makeNode, name, ResTarget::name, NameStr, NIL, ParseState::p_target_relation, parser_errposition(), pstrdup(), RelationData::rd_att, RelationData::rd_rel, RelationGetRelationName, and ResTarget::val.
Referenced by transformInsertStmt().
{ *attrnos = NIL; if (cols == NIL) { /* * 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; for (i = 0; i < numcol; i++) { ResTarget *col; if (attr[i]->attisdropped) continue; col = makeNode(ResTarget); col->name = pstrdup(NameStr(attr[i]->attname)); col->indirection = NIL; col->val = NULL; col->location = -1; cols = lappend(cols, col); *attrnos = lappend_int(*attrnos, i + 1); } } else { /* * Do initial validation of user-supplied INSERT column list. */ Bitmapset *wholecols = NULL; Bitmapset *partialcols = NULL; ListCell *tl; foreach(tl, cols) { ResTarget *col = (ResTarget *) lfirst(tl); char *name = col->name; int attrno; /* Lookup column name, ereport on failure */ attrno = attnameAttNum(pstate->p_target_relation, name, false); if (attrno == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, col->location))); /* * Check for duplicates, but only of whole columns --- we allow * INSERT INTO foo (col.subcol1, col.subcol2) */ if (col->indirection == NIL) { /* whole column; must not have any other assignment */ if (bms_is_member(attrno, wholecols) || bms_is_member(attrno, partialcols)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" specified more than once", name), parser_errposition(pstate, col->location))); wholecols = bms_add_member(wholecols, attrno); } else { /* partial column; must not have any whole assignment */ if (bms_is_member(attrno, wholecols)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column \"%s\" specified more than once", name), parser_errposition(pstate, col->location))); partialcols = bms_add_member(partialcols, attrno); } *attrnos = lappend_int(*attrnos, attrno); } } return cols; }
TupleDesc expandRecordVariable | ( | ParseState * | pstate, | |
Var * | var, | |||
int | levelsup | |||
) |
Definition at line 1369 of file parse_target.c.
References Alias::aliasname, Assert, CreateTemplateTupleDesc(), RangeTblEntry::ctelevelsup, CommonTableExpr::ctequery, elog, RangeTblEntry::eref, ERROR, expandRecordVariable(), expandRTE(), TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), forboth, get_expr_result_type(), get_tle_by_resno(), GetCTEForRTE(), GetCTETargetList, GetRTEByRangeTablePosn(), i, InvalidAttrNumber, IsA, RangeTblEntry::joinaliasvars, label, lfirst, list_length(), list_nth(), Var::location, lookup_rowtype_tupdesc_copy(), MemSet, NULL, ParseState::p_rtable, ParseState::parentParseState, RECORDOID, TargetEntry::resjunk, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::self_reference, strVal, RangeTblEntry::subquery, Query::targetList, TupleDescInitEntry(), TupleDescInitEntryCollation(), TYPEFUNC_COMPOSITE, Var::varattno, Var::varlevelsup, Var::varno, and Var::vartype.
Referenced by expandRecordVariable(), ExpandRowReference(), and ParseComplexProjection().
{ TupleDesc tupleDesc; int netlevelsup; RangeTblEntry *rte; AttrNumber attnum; Node *expr; /* Check my caller didn't mess up */ Assert(IsA(var, Var)); Assert(var->vartype == RECORDOID); netlevelsup = var->varlevelsup + levelsup; rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); attnum = var->varattno; if (attnum == InvalidAttrNumber) { /* Whole-row reference to an RTE, so expand the known fields */ List *names, *vars; ListCell *lname, *lvar; int i; expandRTE(rte, var->varno, 0, var->location, false, &names, &vars); tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); i = 1; forboth(lname, names, lvar, vars) { char *label = strVal(lfirst(lname)); Node *varnode = (Node *) lfirst(lvar); TupleDescInitEntry(tupleDesc, i, label, exprType(varnode), exprTypmod(varnode), 0); TupleDescInitEntryCollation(tupleDesc, i, exprCollation(varnode)); i++; } Assert(lname == NULL && lvar == NULL); /* lists same length? */ return tupleDesc; } expr = (Node *) var; /* default if we can't drill down */ switch (rte->rtekind) { case RTE_RELATION: case RTE_VALUES: /* * This case should not occur: a column of a table or values list * shouldn't have type RECORD. Fall through and fail (most * likely) at the bottom. */ break; case RTE_SUBQUERY: { /* Subselect-in-FROM: examine sub-select's output expr */ TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList, attnum); if (ste == NULL || ste->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, attnum); expr = (Node *) ste->expr; if (IsA(expr, Var)) { /* * Recurse into the sub-select to see what its Var refers * to. We have to build an additional level of ParseState * to keep in step with varlevelsup in the subselect. */ ParseState mypstate; MemSet(&mypstate, 0, sizeof(mypstate)); mypstate.parentParseState = pstate; mypstate.p_rtable = rte->subquery->rtable; /* don't bother filling the rest of the fake pstate */ return expandRecordVariable(&mypstate, (Var *) expr, 0); } /* else fall through to inspect the expression */ } break; case RTE_JOIN: /* Join RTE --- recursively inspect the alias variable */ Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); if (IsA(expr, Var)) return expandRecordVariable(pstate, (Var *) expr, netlevelsup); /* else fall through to inspect the expression */ break; case RTE_FUNCTION: /* * We couldn't get here unless a function is declared with one of * its result columns as RECORD, which is not allowed. */ break; case RTE_CTE: /* CTE reference: examine subquery's output expr */ if (!rte->self_reference) { CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup); TargetEntry *ste; ste = get_tle_by_resno(GetCTETargetList(cte), attnum); if (ste == NULL || ste->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, attnum); expr = (Node *) ste->expr; if (IsA(expr, Var)) { /* * Recurse into the CTE to see what its Var refers to. We * have to build an additional level of ParseState to keep * in step with varlevelsup in the CTE; furthermore it * could be an outer CTE. */ ParseState mypstate; Index levelsup; MemSet(&mypstate, 0, sizeof(mypstate)); /* this loop must work, since GetCTEForRTE did */ for (levelsup = 0; levelsup < rte->ctelevelsup + netlevelsup; levelsup++) pstate = pstate->parentParseState; mypstate.parentParseState = pstate; mypstate.p_rtable = ((Query *) cte->ctequery)->rtable; /* don't bother filling the rest of the fake pstate */ return expandRecordVariable(&mypstate, (Var *) expr, 0); } /* else fall through to inspect the expression */ } break; } /* * We now have an expression we can't expand any more, so see if * get_expr_result_type() can do anything with it. If not, pass to * lookup_rowtype_tupdesc() which will probably fail, but will give an * appropriate error message while failing. */ if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), exprTypmod(expr)); return tupleDesc; }
char* FigureColname | ( | Node * | node | ) |
Definition at line 1539 of file parse_target.c.
References FigureColnameInternal(), name, and NULL.
Referenced by transformRangeFunction(), transformTargetEntry(), and transformXmlExpr().
{ char *name = NULL; (void) FigureColnameInternal(node, &name); if (name != NULL) return name; /* default result if we can't guess anything */ return "?column?"; }
char* FigureIndexColname | ( | Node * | node | ) |
Definition at line 1558 of file parse_target.c.
References FigureColnameInternal(), and name.
Referenced by transformIndexStmt().
{ char *name = NULL; (void) FigureColnameInternal(node, &name); return name; }
void markTargetListOrigins | ( | ParseState * | pstate, | |
List * | targetlist | |||
) |
Definition at line 250 of file parse_target.c.
References TargetEntry::expr, lfirst, and markTargetListOrigin().
Referenced by transformReturningList(), and transformSelectStmt().
{ ListCell *l; foreach(l, targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); markTargetListOrigin(pstate, tle, (Var *) tle->expr, 0); } }
Expr* transformAssignedExpr | ( | ParseState * | pstate, | |
Expr * | expr, | |||
ParseExprKind | exprKind, | |||
char * | colname, | |||
int | attrno, | |||
List * | indirection, | |||
int | location | |||
) |
Definition at line 373 of file parse_target.c.
References Assert, attnumTypeId(), tupleDesc::attrs, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, SetToDefault::collation, ereport, errcode(), errhint(), errmsg(), ERROR, EXPR_KIND_NONE, exprLocation(), exprType(), format_type_be(), IsA, linitial, list_head(), make_var(), makeNullConst(), NULL, ParseState::p_expr_kind, ParseState::p_is_insert, ParseState::p_target_rangetblentry, ParseState::p_target_relation, parser_errposition(), RelationData::rd_att, transformAssignmentIndirection(), SetToDefault::typeId, and SetToDefault::typeMod.
Referenced by transformInsertRow(), and updateTargetListEntry().
{ Relation rd = pstate->p_target_relation; Oid type_id; /* type of value provided */ Oid attrtype; /* type of target column */ int32 attrtypmod; Oid attrcollation; /* collation of target column */ ParseExprKind sv_expr_kind; /* * Save and restore identity of expression type we're parsing. We must * set p_expr_kind here because we can parse subscripts without going * through transformExpr(). */ Assert(exprKind != EXPR_KIND_NONE); sv_expr_kind = pstate->p_expr_kind; pstate->p_expr_kind = exprKind; Assert(rd != NULL); if (attrno <= 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot assign to system column \"%s\"", colname), parser_errposition(pstate, location))); attrtype = attnumTypeId(rd, attrno); attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation; /* * If the expression is a DEFAULT placeholder, insert the attribute's * type/typmod/collation into it so that exprType etc will report the * right things. (We expect that the eventually substituted default * expression will in fact have this type and typmod. The collation * likely doesn't matter, but let's set it correctly anyway.) Also, * reject trying to update a subfield or array element with DEFAULT, since * there can't be any default for portions of a column. */ if (expr && IsA(expr, SetToDefault)) { SetToDefault *def = (SetToDefault *) expr; def->typeId = attrtype; def->typeMod = attrtypmod; def->collation = attrcollation; if (indirection) { if (IsA(linitial(indirection), A_Indices)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot set an array element to DEFAULT"), parser_errposition(pstate, location))); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot set a subfield to DEFAULT"), parser_errposition(pstate, location))); } } /* Now we can use exprType() safely. */ type_id = exprType((Node *) expr); /* * If there is indirection on the target column, prepare an array or * subfield assignment expression. This will generate a new column value * that the source value has been inserted into, which can then be placed * in the new tuple constructed by INSERT or UPDATE. */ if (indirection) { Node *colVar; if (pstate->p_is_insert) { /* * The command is INSERT INTO table (col.something) ... so there * is not really a source value to work with. Insert a NULL * constant as the source value. */ colVar = (Node *) makeNullConst(attrtype, attrtypmod, attrcollation); } else { /* * Build a Var for the column to be updated. */ colVar = (Node *) make_var(pstate, pstate->p_target_rangetblentry, attrno, location); } expr = (Expr *) transformAssignmentIndirection(pstate, colVar, colname, false, attrtype, attrtypmod, attrcollation, list_head(indirection), (Node *) expr, location); } else { /* * For normal non-qualified target column, do type checking and * coercion. */ Node *orig_expr = (Node *) expr; expr = (Expr *) coerce_to_target_type(pstate, orig_expr, type_id, attrtype, attrtypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); if (expr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" is of type %s" " but expression is of type %s", colname, format_type_be(attrtype), format_type_be(type_id)), errhint("You will need to rewrite or cast the expression."), parser_errposition(pstate, exprLocation(orig_expr)))); } pstate->p_expr_kind = sv_expr_kind; return expr; }
List* transformExpressionList | ( | ParseState * | pstate, | |
List * | exprlist, | |||
ParseExprKind | exprKind | |||
) |
Definition at line 188 of file parse_target.c.
References ExpandColumnRefStar(), ExpandIndirectionStar(), ColumnRef::fields, A_Indirection::indirection, IsA, lappend(), lfirst, list_concat(), llast, and transformExpr().
Referenced by transformRowExpr(), and transformValuesClause().
{ List *result = NIL; ListCell *lc; foreach(lc, exprlist) { Node *e = (Node *) lfirst(lc); /* * Check for "something.*". Depending on the complexity of the * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(e, ColumnRef)) { ColumnRef *cref = (ColumnRef *) e; if (IsA(llast(cref->fields), A_Star)) { /* It is something.*, expand into multiple items */ result = list_concat(result, ExpandColumnRefStar(pstate, cref, false)); continue; } } else if (IsA(e, A_Indirection)) { A_Indirection *ind = (A_Indirection *) e; if (IsA(llast(ind->indirection), A_Star)) { /* It is something.*, expand into multiple items */ result = list_concat(result, ExpandIndirectionStar(pstate, ind, false, exprKind)); continue; } } /* * Not "something.*", so transform as a single expression */ result = lappend(result, transformExpr(pstate, e, exprKind)); } return result; }
TargetEntry* transformTargetEntry | ( | ParseState * | pstate, | |
Node * | node, | |||
Node * | expr, | |||
ParseExprKind | exprKind, | |||
char * | colname, | |||
bool | resjunk | |||
) |
Definition at line 85 of file parse_target.c.
References FigureColname(), makeTargetEntry(), NULL, ParseState::p_next_resno, and transformExpr().
Referenced by findTargetlistEntrySQL99(), and transformTargetList().
{ /* Transform the node if caller didn't do it already */ if (expr == NULL) expr = transformExpr(pstate, node, exprKind); if (colname == NULL && !resjunk) { /* * Generate a suitable column name for a column without any explicit * 'AS ColumnName' clause. */ colname = FigureColname(node); } return makeTargetEntry((Expr *) expr, (AttrNumber) pstate->p_next_resno++, colname, resjunk); }
List* transformTargetList | ( | ParseState * | pstate, | |
List * | targetlist, | |||
ParseExprKind | exprKind | |||
) |
Definition at line 121 of file parse_target.c.
References ExpandColumnRefStar(), ExpandIndirectionStar(), ColumnRef::fields, A_Indirection::indirection, IsA, lappend(), lfirst, list_concat(), llast, ResTarget::name, NULL, transformTargetEntry(), and ResTarget::val.
Referenced by transformReturningList(), transformSelectStmt(), and transformUpdateStmt().
{ List *p_target = NIL; ListCell *o_target; foreach(o_target, targetlist) { ResTarget *res = (ResTarget *) lfirst(o_target); /* * Check for "something.*". Depending on the complexity of the * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(res->val, ColumnRef)) { ColumnRef *cref = (ColumnRef *) res->val; if (IsA(llast(cref->fields), A_Star)) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandColumnRefStar(pstate, cref, true)); continue; } } else if (IsA(res->val, A_Indirection)) { A_Indirection *ind = (A_Indirection *) res->val; if (IsA(llast(ind->indirection), A_Star)) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandIndirectionStar(pstate, ind, true, exprKind)); continue; } } /* * Not "something.*", so transform as a single expression */ p_target = lappend(p_target, transformTargetEntry(pstate, res->val, NULL, exprKind, res->name, false)); } return p_target; }
void updateTargetListEntry | ( | ParseState * | pstate, | |
TargetEntry * | tle, | |||
char * | colname, | |||
int | attrno, | |||
List * | indirection, | |||
int | location | |||
) |
Definition at line 534 of file parse_target.c.
References TargetEntry::expr, EXPR_KIND_UPDATE_TARGET, TargetEntry::resname, TargetEntry::resno, and transformAssignedExpr().
Referenced by transformUpdateStmt().
{ /* Fix up expression as needed */ tle->expr = transformAssignedExpr(pstate, tle->expr, EXPR_KIND_UPDATE_TARGET, colname, attrno, indirection, location); /* * Set the resno to identify the target column --- the rewriter and * planner depend on this. We also set the resname to identify the target * column, but this is only for debugging purposes; it should not be * relied on. (In particular, it might be out of date in a stored rule.) */ tle->resno = (AttrNumber) attrno; tle->resname = colname; }