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 + -
显示快捷键?