parse_target.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 672 行 · 第 1/2 页
C
672 行
/*------------------------------------------------------------------------- * * parse_target.c * handle target lists * * Portions Copyright (c) 1996-2003, 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.113 2003/09/25 06:58:01 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "commands/dbcommands.h"#include "miscadmin.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"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, 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)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("relation reference \"%s\" cannot be used as a select-list entry", ((RangeVar *) expr)->relname), errhint("Write \"%s\".* to denote all the columns of the relation.", ((RangeVar *) expr)->relname))); type_id = exprType(expr); type_mod = exprTypmod(expr); if (colname == NULL && !resjunk) { /* * 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. * * 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){ FastList p_target; List *o_target; FastListInit(&p_target); foreach(o_target, targetlist) { ResTarget *res = (ResTarget *) lfirst(o_target); if (IsA(res->val, ColumnRef)) { ColumnRef *cref = (ColumnRef *) res->val; List *fields = cref->fields; if (strcmp(strVal(llast(fields)), "*") == 0) { int numnames = length(fields); if (numnames == 1) { /* * Target item is a single '*', expand all tables * (e.g., SELECT * FROM emp) */ FastConc(&p_target, ExpandAllTables(pstate)); } else { /* * Target item is relation.*, expand that table * (e.g., 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)); /* * We check the catalog name and then ignore * it. */ if (strcmp(name1, get_database_name(MyDatabaseId)) != 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cross-database references are not implemented"))); schemaname = strVal(lsecond(fields)); relname = strVal(lthird(fields)); break; } default: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("improper qualified name (too many dotted names): %s", NameListToString(fields)))); schemaname = NULL; /* keep compiler quiet */ relname = NULL; break; } rte = refnameRangeTblEntry(pstate, schemaname, relname, &sublevels_up); if (rte == NULL) rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname)); FastConc(&p_target, expandRelAttrs(pstate, rte)); } } else { /* Plain ColumnRef node, treat it as an expression */ FastAppend(&p_target, transformTargetEntry(pstate, res->val, NULL, res->name, false)); } } else { /* Everything else but ColumnRef */ FastAppend(&p_target, transformTargetEntry(pstate, res->val, NULL, res->name, false)); } } return FastListValue(&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){ List *l; foreach(l, targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); markTargetListOrigin(pstate, tle->resdom, (Var *) tle->expr); }}/* * 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 voidmarkTargetListOrigin(ParseState *pstate, Resdom *res, Var *var){ Index levelsup; RangeTblEntry *rte; AttrNumber attnum; if (var == NULL || !IsA(var, Var)) return; levelsup = var->varlevelsup; while (levelsup-- > 0) { pstate = pstate->parentParseState; Assert(pstate != NULL); } Assert(var->varno > 0 && (int) var->varno <= length(pstate->p_rtable)); rte = rt_fetch(var->varno, pstate->p_rtable); attnum = var->varattno; 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: { /* Subselect-in-FROM: copy up from the subselect */ TargetEntry *te = get_tle_by_resno(rte->subquery->targetList, attnum); if (te == NULL || te->resdom->resjunk) elog(ERROR, "subquery %s does not have attribute %d", rte->eref->aliasname, attnum); res->resorigtbl = te->resdom->resorigtbl; res->resorigcol = te->resdom->resorigcol; } break; case RTE_JOIN: { /* 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; }}/* * 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 */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; Resdom *resnode = tle->resdom; 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; /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?