📄 parse_target.c
字号:
/*------------------------------------------------------------------------- * * parse_target.c * handle target lists * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.138.2.2 2006/01/17 17:33:20 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "commands/dbcommands.h"#include "funcapi.h"#include "miscadmin.h"#include "nodes/bitmapset.h"#include "nodes/makefuncs.h"#include "parser/parsetree.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_func.h"#include "parser/parse_relation.h"#include "parser/parse_target.h"#include "parser/parse_type.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/typcache.h"static void markTargetListOrigin(ParseState *pstate, TargetEntry *tle, Var *var, int levelsup);static Node *transformAssignmentIndirection(ParseState *pstate, Node *basenode, const char *targetName, bool targetIsArray, Oid targetTypeId, int32 targetTypMod, ListCell *indirection, Node *rhs);static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);static List *ExpandAllTables(ParseState *pstate);static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);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, char *colname, bool resjunk){ /* Transform the node if caller didn't do it already */ if (expr == NULL) expr = transformExpr(pstate, node); 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);}/* * 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 (the "val" fields). */List *transformTargetList(ParseState *pstate, List *targetlist){ 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 name in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(res->val, ColumnRef)) { ColumnRef *cref = (ColumnRef *) res->val; if (strcmp(strVal(llast(cref->fields)), "*") == 0) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandColumnRefStar(pstate, cref)); continue; } } else if (IsA(res->val, A_Indirection)) { A_Indirection *ind = (A_Indirection *) res->val; Node *lastitem = llast(ind->indirection); if (IsA(lastitem, String) && strcmp(strVal(lastitem), "*") == 0) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandIndirectionStar(pstate, ind)); continue; } } /* * Not "something.*", so transform as a single expression */ p_target = lappend(p_target, transformTargetEntry(pstate, res->val, NULL, res->name, false)); } return p_target;}/* * markTargetListOrigins() * Mark targetlist columns that are simple Vars with the source * table's OID and column number. * * Currently, this is done only for SELECT targetlists, since we only * need the info if we are going to send it to the frontend. */voidmarkTargetListOrigins(ParseState *pstate, List *targetlist){ ListCell *l; foreach(l, targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); markTargetListOrigin(pstate, tle, (Var *) tle->expr, 0); }}/* * markTargetListOrigin() * If 'var' is a Var of a plain relation, mark 'tle' with its origin * * levelsup is an extra offset to interpret the Var's varlevelsup correctly. * * 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 voidmarkTargetListOrigin(ParseState *pstate, TargetEntry *tle, Var *var, int levelsup){ int netlevelsup; RangeTblEntry *rte; AttrNumber attnum; if (var == NULL || !IsA(var, Var)) return; netlevelsup = var->varlevelsup + levelsup; rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup); attnum = var->varattno; switch (rte->rtekind) { case RTE_RELATION: /* It's a table or view, report it */ tle->resorigtbl = rte->relid; tle->resorigcol = attnum; break; case RTE_SUBQUERY: /* Subselect-in-FROM: copy up from the subselect */ if (attnum != InvalidAttrNumber) { 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); tle->resorigtbl = ste->resorigtbl; tle->resorigcol = ste->resorigcol; } break; case RTE_JOIN: /* Join RTE --- recursively inspect the alias variable */ if (attnum != InvalidAttrNumber) { Var *aliasvar; Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); markTargetListOrigin(pstate, tle, aliasvar, netlevelsup); } break; case RTE_SPECIAL: case RTE_FUNCTION: /* not a simple relation, leave it unmarked */ break; }}/* * 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 subfield names or 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/field names for target column, if any */voidupdateTargetListEntry(ParseState *pstate, TargetEntry *tle, char *colname, int attrno, List *indirection){ Oid type_id; /* type of value provided */ Oid attrtype; /* type of target column */ int32 attrtypmod; Relation rd = pstate->p_target_relation; Assert(rd != NULL); if (attrno <= 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot assign to system column \"%s\"", colname))); attrtype = attnumTypeId(rd, attrno); attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; /* * If the expression is a DEFAULT placeholder, insert the attribute's * type/typmod into it so that exprType will report the right things. (We * expect that the eventually substituted default expression will in fact * have this type and typmod.) 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 (tle->expr && IsA(tle->expr, SetToDefault)) { SetToDefault *def = (SetToDefault *) tle->expr; def->typeId = attrtype; def->typeMod = attrtypmod; if (indirection) { if (IsA(linitial(indirection), A_Indices)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot set an array element to DEFAULT"))); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot set a subfield to DEFAULT"))); } } /* Now we can use exprType() safely. */ type_id = exprType((Node *) tle->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); } else { /* * Build a Var for the column to be updated. */ colVar = (Node *) make_var(pstate, pstate->p_target_rangetblentry, attrno); } tle->expr = (Expr *) transformAssignmentIndirection(pstate, colVar, colname, false, attrtype, attrtypmod, list_head(indirection), (Node *) tle->expr); } else { /* * For normal non-qualified target column, do type checking and * coercion. */ tle->expr = (Expr *) coerce_to_target_type(pstate, (Node *) tle->expr, type_id, attrtype, attrtypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (tle->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."))); } /* * 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;}/* * Process indirection (field selection or subscripting) of the target * column in INSERT/UPDATE. This routine recurses for multiple levels * of indirection --- but note that several adjacent A_Indices nodes in * the indirection list are treated as a single multidimensional subscript * operation. * * In the initial call, basenode is a Var for the target column in UPDATE, * or a null Const of the target's type in INSERT. In recursive calls, * basenode is NULL, indicating that a substitute node should be consed up if
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -