mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Standardize on the assumption that the arguments of a RowExpr correspond
to the physical layout of the rowtype, ie, there are dummy arguments corresponding to any dropped columns in the rowtype. We formerly had a couple of places that did it this way and several others that did not. Fixes Gaetano Mendola's "cache lookup failed for type 0" bug of 5-Aug.
This commit is contained in:
		
							parent
							
								
									388ffad040
								
							
						
					
					
						commit
						fcaad7e2c1
					
				| @ -8,7 +8,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.165 2004/08/02 01:30:41 tgl Exp $ | ||||
|  *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.166 2004/08/17 18:47:08 tgl Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @ -44,6 +44,7 @@ | ||||
| #include "executor/nodeSubplan.h" | ||||
| #include "funcapi.h" | ||||
| #include "miscadmin.h" | ||||
| #include "nodes/makefuncs.h" | ||||
| #include "optimizer/planmain.h" | ||||
| #include "parser/parse_expr.h" | ||||
| #include "utils/acl.h" | ||||
| @ -2096,7 +2097,7 @@ ExecEvalRow(RowExprState *rstate, | ||||
| 	HeapTuple	tuple; | ||||
| 	Datum	   *values; | ||||
| 	char	   *nulls; | ||||
| 	int			nargs; | ||||
| 	int			natts; | ||||
| 	ListCell   *arg; | ||||
| 	int			i; | ||||
| 
 | ||||
| @ -2106,9 +2107,12 @@ ExecEvalRow(RowExprState *rstate, | ||||
| 		*isDone = ExprSingleResult; | ||||
| 
 | ||||
| 	/* Allocate workspace */ | ||||
| 	nargs = list_length(rstate->args); | ||||
| 	values = (Datum *) palloc(nargs * sizeof(Datum)); | ||||
| 	nulls = (char *) palloc(nargs * sizeof(char)); | ||||
| 	natts = rstate->tupdesc->natts; | ||||
| 	values = (Datum *) palloc0(natts * sizeof(Datum)); | ||||
| 	nulls = (char *) palloc(natts * sizeof(char)); | ||||
| 
 | ||||
| 	/* preset to nulls in case rowtype has some later-added columns */ | ||||
| 	memset(nulls, 'n', natts * sizeof(char)); | ||||
| 
 | ||||
| 	/* Evaluate field values */ | ||||
| 	i = 0; | ||||
| @ -2979,19 +2983,12 @@ ExecInitExpr(Expr *node, PlanState *parent) | ||||
| 			{ | ||||
| 				RowExpr	   *rowexpr = (RowExpr *) node; | ||||
| 				RowExprState *rstate = makeNode(RowExprState); | ||||
| 				Form_pg_attribute *attrs; | ||||
| 				List	   *outlist = NIL; | ||||
| 				ListCell   *l; | ||||
| 				int			i; | ||||
| 
 | ||||
| 				rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow; | ||||
| 				foreach(l, rowexpr->args) | ||||
| 				{ | ||||
| 					Expr	   *e = (Expr *) lfirst(l); | ||||
| 					ExprState  *estate; | ||||
| 
 | ||||
| 					estate = ExecInitExpr(e, parent); | ||||
| 					outlist = lappend(outlist, estate); | ||||
| 				} | ||||
| 				rstate->args = outlist; | ||||
| 				/* Build tupdesc to describe result tuples */ | ||||
| 				if (rowexpr->row_typeid == RECORDOID) | ||||
| 				{ | ||||
| @ -3003,7 +3000,46 @@ ExecInitExpr(Expr *node, PlanState *parent) | ||||
| 				{ | ||||
| 					/* it's been cast to a named type, use that */ | ||||
| 					rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); | ||||
| 					rstate->tupdesc = CreateTupleDescCopy(rstate->tupdesc); | ||||
| 				} | ||||
| 				/* Set up evaluation, skipping any deleted columns */ | ||||
| 				Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts); | ||||
| 				attrs = rstate->tupdesc->attrs; | ||||
| 				i = 0; | ||||
| 				foreach(l, rowexpr->args) | ||||
| 				{ | ||||
| 					Expr	   *e = (Expr *) lfirst(l); | ||||
| 					ExprState  *estate; | ||||
| 
 | ||||
| 					if (!attrs[i]->attisdropped) | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * Guard against ALTER COLUMN TYPE on rowtype | ||||
| 						 * since the RowExpr was created.  XXX should we | ||||
| 						 * check typmod too?  Not sure we can be sure it'll | ||||
| 						 * be the same. | ||||
| 						 */ | ||||
| 						if (exprType((Node *) e) != attrs[i]->atttypid) | ||||
| 							ereport(ERROR, | ||||
| 									(errcode(ERRCODE_DATATYPE_MISMATCH), | ||||
| 									 errmsg("ROW() column has type %s instead of type %s", | ||||
| 											format_type_be(exprType((Node *) e)), | ||||
| 											format_type_be(attrs[i]->atttypid)))); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * Ignore original expression and insert a NULL. | ||||
| 						 * We don't really care what type of NULL it is, | ||||
| 						 * so always make an int4 NULL. | ||||
| 						 */ | ||||
| 						e = (Expr *) makeNullConst(INT4OID); | ||||
| 					} | ||||
| 					estate = ExecInitExpr(e, parent); | ||||
| 					outlist = lappend(outlist, estate); | ||||
| 					i++; | ||||
| 				} | ||||
| 				rstate->args = outlist; | ||||
| 				state = (ExprState *) rstate; | ||||
| 			} | ||||
| 			break; | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.177 2004/08/02 01:30:43 tgl Exp $ | ||||
|  *	  $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.178 2004/08/17 18:47:08 tgl Exp $ | ||||
|  * | ||||
|  * HISTORY | ||||
|  *	  AUTHOR			DATE			MAJOR EVENT | ||||
| @ -40,6 +40,7 @@ | ||||
| #include "utils/lsyscache.h" | ||||
| #include "utils/memutils.h" | ||||
| #include "utils/syscache.h" | ||||
| #include "utils/typcache.h" | ||||
| 
 | ||||
| 
 | ||||
| typedef struct | ||||
| @ -1054,6 +1055,33 @@ set_coercionform_dontcare_walker(Node *node, void *context) | ||||
| 								  context); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Helper for eval_const_expressions: check that datatype of an attribute | ||||
|  * is still what it was when the expression was parsed.  This is needed to | ||||
|  * guard against improper simplification after ALTER COLUMN TYPE.  (XXX we | ||||
|  * may well need to make similar checks elsewhere?) | ||||
|  */ | ||||
| static bool | ||||
| rowtype_field_matches(Oid rowtypeid, int fieldnum, | ||||
| 					  Oid expectedtype, int32 expectedtypmod) | ||||
| { | ||||
| 	TupleDesc	tupdesc; | ||||
| 	Form_pg_attribute attr; | ||||
| 
 | ||||
| 	/* No issue for RECORD, since there is no way to ALTER such a type */ | ||||
| 	if (rowtypeid == RECORDOID) | ||||
| 		return true; | ||||
| 	tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1); | ||||
| 	if (fieldnum <= 0 || fieldnum > tupdesc->natts) | ||||
| 		return false; | ||||
| 	attr = tupdesc->attrs[fieldnum - 1]; | ||||
| 	if (attr->attisdropped || | ||||
| 		attr->atttypid != expectedtype || | ||||
| 		attr->atttypmod != expectedtypmod) | ||||
| 		return false; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*--------------------
 | ||||
|  * eval_const_expressions | ||||
| @ -1630,6 +1658,10 @@ eval_const_expressions_mutator(Node *node, | ||||
| 		 * parser, because ParseComplexProjection short-circuits it. But | ||||
| 		 * it can arise while simplifying functions.)  Also, we can | ||||
| 		 * optimize field selection from a RowExpr construct. | ||||
| 		 * | ||||
| 		 * We must however check that the declared type of the field is | ||||
| 		 * still the same as when the FieldSelect was created --- this | ||||
| 		 * can change if someone did ALTER COLUMN TYPE on the rowtype. | ||||
| 		 */ | ||||
| 		FieldSelect *fselect = (FieldSelect *) node; | ||||
| 		FieldSelect *newfselect; | ||||
| @ -1640,6 +1672,10 @@ eval_const_expressions_mutator(Node *node, | ||||
| 		if (arg && IsA(arg, Var) && | ||||
| 			((Var *) arg)->varattno == InvalidAttrNumber) | ||||
| 		{ | ||||
| 			if (rowtype_field_matches(((Var *) arg)->vartype, | ||||
| 									  fselect->fieldnum, | ||||
| 									  fselect->resulttype, | ||||
| 									  fselect->resulttypmod)) | ||||
| 				return (Node *) makeVar(((Var *) arg)->varno, | ||||
| 										fselect->fieldnum, | ||||
| 										fselect->resulttype, | ||||
| @ -1652,7 +1688,18 @@ eval_const_expressions_mutator(Node *node, | ||||
| 
 | ||||
| 			if (fselect->fieldnum > 0 && | ||||
| 				fselect->fieldnum <= list_length(rowexpr->args)) | ||||
| 				return (Node *) list_nth(rowexpr->args, fselect->fieldnum - 1); | ||||
| 			{ | ||||
| 				Node *fld = (Node *) list_nth(rowexpr->args, | ||||
| 											  fselect->fieldnum - 1); | ||||
| 
 | ||||
| 				if (rowtype_field_matches(rowexpr->row_typeid, | ||||
| 										  fselect->fieldnum, | ||||
| 										  fselect->resulttype, | ||||
| 										  fselect->resulttypmod) && | ||||
| 					fselect->resulttype == exprType(fld) && | ||||
| 					fselect->resulttypmod == exprTypmod(fld)) | ||||
| 					return fld; | ||||
| 			} | ||||
| 		} | ||||
| 		newfselect = makeNode(FieldSelect); | ||||
| 		newfselect->arg = (Expr *) arg; | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.119 2004/06/16 01:26:44 tgl Exp $ | ||||
|  *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.120 2004/08/17 18:47:08 tgl Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @ -648,10 +648,15 @@ coerce_record_to_complex(ParseState *pstate, Node *node, | ||||
| 	List	   *args = NIL; | ||||
| 	List	   *newargs; | ||||
| 	int			i; | ||||
| 	int			ucolno; | ||||
| 	ListCell   *arg; | ||||
| 
 | ||||
| 	if (node && IsA(node, RowExpr)) | ||||
| 	{ | ||||
| 		/*
 | ||||
| 		 * Since the RowExpr must be of type RECORD, we needn't worry | ||||
| 		 * about it containing any dropped columns. | ||||
| 		 */ | ||||
| 		args = ((RowExpr *) node)->args; | ||||
| 	} | ||||
| 	else if (node && IsA(node, Var) && | ||||
| @ -670,6 +675,8 @@ coerce_record_to_complex(ParseState *pstate, Node *node, | ||||
| 			Oid		vartype; | ||||
| 			int32	vartypmod; | ||||
| 
 | ||||
| 			if (get_rte_attribute_is_dropped(rte, nf)) | ||||
| 				continue; | ||||
| 			get_rte_attribute_type(rte, nf, &vartype, &vartypmod); | ||||
| 			args = lappend(args, | ||||
| 						   makeVar(((Var *) node)->varno, | ||||
| @ -687,19 +694,34 @@ coerce_record_to_complex(ParseState *pstate, Node *node, | ||||
| 						format_type_be(targetTypeId)))); | ||||
| 
 | ||||
| 	tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1); | ||||
| 	if (list_length(args) != tupdesc->natts) | ||||
| 	newargs = NIL; | ||||
| 	ucolno = 1; | ||||
| 	arg = list_head(args); | ||||
| 	for (i = 0; i < tupdesc->natts; i++) | ||||
| 	{ | ||||
| 		Node   *expr; | ||||
| 		Oid		exprtype; | ||||
| 
 | ||||
| 		/* Fill in NULLs for dropped columns in rowtype */ | ||||
| 		if (tupdesc->attrs[i]->attisdropped) | ||||
| 		{ | ||||
| 			/*
 | ||||
| 			 * can't use atttypid here, but it doesn't really matter | ||||
| 			 * what type the Const claims to be. | ||||
| 			 */ | ||||
| 			newargs = lappend(newargs, makeNullConst(INT4OID)); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (arg == NULL) | ||||
| 			ereport(ERROR, | ||||
| 					(errcode(ERRCODE_CANNOT_COERCE), | ||||
| 					 errmsg("cannot cast type %s to %s", | ||||
| 							format_type_be(RECORDOID), | ||||
| 							format_type_be(targetTypeId)), | ||||
| 				 errdetail("Input has wrong number of columns."))); | ||||
| 	newargs = NIL; | ||||
| 	i = 0; | ||||
| 	foreach(arg, args) | ||||
| 	{ | ||||
| 		Node   *expr = (Node *) lfirst(arg); | ||||
| 		Oid		exprtype = exprType(expr); | ||||
| 					 errdetail("Input has too few columns."))); | ||||
| 		expr = (Node *) lfirst(arg); | ||||
| 		exprtype = exprType(expr); | ||||
| 
 | ||||
| 		expr = coerce_to_target_type(pstate, | ||||
| 									 expr, exprtype, | ||||
| @ -716,10 +738,18 @@ coerce_record_to_complex(ParseState *pstate, Node *node, | ||||
| 					 errdetail("Cannot cast type %s to %s in column %d.", | ||||
| 							   format_type_be(exprtype), | ||||
| 							   format_type_be(tupdesc->attrs[i]->atttypid), | ||||
| 							   i + 1))); | ||||
| 							   ucolno))); | ||||
| 		newargs = lappend(newargs, expr); | ||||
| 		i++; | ||||
| 		ucolno++; | ||||
| 		arg = lnext(arg); | ||||
| 	} | ||||
| 	if (arg != NULL) | ||||
| 		ereport(ERROR, | ||||
| 				(errcode(ERRCODE_CANNOT_COERCE), | ||||
| 				 errmsg("cannot cast type %s to %s", | ||||
| 						format_type_be(RECORDOID), | ||||
| 						format_type_be(targetTypeId)), | ||||
| 				 errdetail("Input has too many columns."))); | ||||
| 
 | ||||
| 	rowexpr = makeNode(RowExpr); | ||||
| 	rowexpr->args = newargs; | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.96 2004/05/30 23:40:35 neilc Exp $ | ||||
|  *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.97 2004/08/17 18:47:08 tgl Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @ -42,8 +42,6 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, | ||||
| static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, | ||||
| 						 RangeTblEntry *rte1, const char *aliasname1); | ||||
| static bool isForUpdate(ParseState *pstate, char *refname); | ||||
| static bool get_rte_attribute_is_dropped(RangeTblEntry *rte, | ||||
| 							 AttrNumber attnum); | ||||
| static int	specialAttNum(const char *attname); | ||||
| static void warnAutoRange(ParseState *pstate, RangeVar *relation); | ||||
| 
 | ||||
| @ -1699,7 +1697,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, | ||||
|  * get_rte_attribute_is_dropped | ||||
|  *		Check whether attempted attribute ref is to a dropped column | ||||
|  */ | ||||
| static bool | ||||
| bool | ||||
| get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) | ||||
| { | ||||
| 	bool		result; | ||||
|  | ||||
| @ -7,12 +7,13 @@ | ||||
|  * | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.84 2004/05/30 23:40:35 neilc Exp $ | ||||
|  *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.85 2004/08/17 18:47:09 tgl Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| #include "postgres.h" | ||||
| 
 | ||||
| #include "catalog/pg_type.h" | ||||
| #include "nodes/makefuncs.h" | ||||
| #include "optimizer/clauses.h" | ||||
| #include "optimizer/tlist.h" | ||||
| @ -937,6 +938,17 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) | ||||
| 				AttrNumber nf; | ||||
| 
 | ||||
| 				for (nf = 1; nf <= nfields; nf++) | ||||
| 				{ | ||||
| 					if (get_rte_attribute_is_dropped(rte, nf)) | ||||
| 					{ | ||||
| 						/*
 | ||||
| 						 * can't determine att type here, but it doesn't | ||||
| 						 * really matter what type the Const claims to be. | ||||
| 						 */ | ||||
| 						fields = lappend(fields, | ||||
| 										 makeNullConst(INT4OID)); | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						Oid		vartype; | ||||
| 						int32	vartypmod; | ||||
| @ -951,6 +963,7 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) | ||||
| 						fields = lappend(fields, | ||||
| 										 resolve_one_var(newvar, context)); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				rowexpr = makeNode(RowExpr); | ||||
| 				rowexpr->args = fields; | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
|  *				back to source text | ||||
|  * | ||||
|  * IDENTIFICATION | ||||
|  *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.176 2004/08/02 04:27:15 tgl Exp $ | ||||
|  *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.177 2004/08/17 18:47:09 tgl Exp $ | ||||
|  * | ||||
|  *	  This software is copyrighted by Jan Wieck - Hamburg. | ||||
|  * | ||||
| @ -3283,23 +3283,55 @@ get_rule_expr(Node *node, deparse_context *context, | ||||
| 		case T_RowExpr: | ||||
| 			{ | ||||
| 				RowExpr	   *rowexpr = (RowExpr *) node; | ||||
| 				TupleDesc	tupdesc = NULL; | ||||
| 				ListCell   *arg; | ||||
| 				int			i; | ||||
| 				char	   *sep; | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * SQL99 allows "ROW" to be omitted when list_length(args) > 1, | ||||
| 				 * but for simplicity we always print it. | ||||
| 				 * If it's a named type and not RECORD, we may have to skip | ||||
| 				 * dropped columns and/or claim there are NULLs for added | ||||
| 				 * columns. | ||||
| 				 */ | ||||
| 				if (rowexpr->row_typeid != RECORDOID) | ||||
| 				{ | ||||
| 					tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); | ||||
| 					Assert(list_length(rowexpr->args) <= tupdesc->natts); | ||||
| 				} | ||||
| 
 | ||||
| 				/*
 | ||||
| 				 * SQL99 allows "ROW" to be omitted when there is more than | ||||
| 				 * one column, but for simplicity we always print it. | ||||
| 				 */ | ||||
| 				appendStringInfo(buf, "ROW("); | ||||
| 				sep = ""; | ||||
| 				i = 0; | ||||
| 				foreach(arg, rowexpr->args) | ||||
| 				{ | ||||
| 					Node	   *e = (Node *) lfirst(arg); | ||||
| 
 | ||||
| 					if (tupdesc == NULL || | ||||
| 						!tupdesc->attrs[i]->attisdropped) | ||||
| 					{ | ||||
| 						appendStringInfo(buf, sep); | ||||
| 						get_rule_expr(e, context, true); | ||||
| 						sep = ", "; | ||||
| 					} | ||||
| 					i++; | ||||
| 				} | ||||
| 				if (tupdesc != NULL) | ||||
| 				{ | ||||
| 					while (i < tupdesc->natts) | ||||
| 					{ | ||||
| 						if (!tupdesc->attrs[i]->attisdropped) | ||||
| 						{ | ||||
| 							appendStringInfo(buf, sep); | ||||
| 							appendStringInfo(buf, "NULL"); | ||||
| 							sep = ", "; | ||||
| 						} | ||||
| 						i++; | ||||
| 					} | ||||
| 				} | ||||
| 				appendStringInfo(buf, ")"); | ||||
| 				if (rowexpr->row_format == COERCE_EXPLICIT_CAST) | ||||
| 					appendStringInfo(buf, "::%s", | ||||
|  | ||||
| @ -10,7 +10,7 @@ | ||||
|  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group | ||||
|  * Portions Copyright (c) 1994, Regents of the University of California | ||||
|  * | ||||
|  * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.100 2004/06/09 19:08:18 tgl Exp $ | ||||
|  * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.101 2004/08/17 18:47:09 tgl Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @ -97,13 +97,16 @@ typedef struct Resdom | ||||
|  * Alias - | ||||
|  *	  specifies an alias for a range variable; the alias might also | ||||
|  *	  specify renaming of columns within the table. | ||||
|  * | ||||
|  * Note: colnames is a list of Value nodes (always strings).  In an RTE's | ||||
|  * eref Alias, the colnames list includes dropped columns, so that the | ||||
|  * colname list position matches the physical attribute number. | ||||
|  */ | ||||
| typedef struct Alias | ||||
| { | ||||
| 	NodeTag		type; | ||||
| 	char	   *aliasname;		/* aliased rel name (never qualified) */ | ||||
| 	List	   *colnames;		/* optional list of column aliases */ | ||||
| 	/* Note: colnames is a list of Value nodes (always strings) */ | ||||
| } Alias; | ||||
| 
 | ||||
| typedef enum InhOption | ||||
| @ -663,6 +666,16 @@ typedef struct ArrayExpr | ||||
| 
 | ||||
| /*
 | ||||
|  * RowExpr - a ROW() expression | ||||
|  * | ||||
|  * Note: the list of fields must have a one-for-one correspondence with | ||||
|  * physical fields of the associated rowtype, although it is okay for it | ||||
|  * to be shorter than the rowtype.  That is, the N'th list element must | ||||
|  * match up with the N'th physical field.  When the N'th physical field | ||||
|  * is a dropped column (attisdropped) then the N'th list element can just | ||||
|  * be a NULL constant.  (This case can only occur for named composite types, | ||||
|  * not RECORD types, since those are built from the RowExpr itself rather | ||||
|  * than vice versa.)  It is important not to assume that length(args) is | ||||
|  * the same as the number of columns logically present in the rowtype. | ||||
|  */ | ||||
| typedef struct RowExpr | ||||
| { | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group | ||||
|  * Portions Copyright (c) 1994, Regents of the University of California | ||||
|  * | ||||
|  * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.24 2004/05/26 04:41:46 neilc Exp $ | ||||
|  * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.25 2004/08/17 18:47:09 tgl Exp $ | ||||
|  * | ||||
|  *------------------------------------------------------------------------- | ||||
|  */ | ||||
| @ -55,6 +55,13 @@ extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum); | ||||
| extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, | ||||
| 					   Oid *vartype, int32 *vartypmod); | ||||
| 
 | ||||
| /*
 | ||||
|  * Check whether an attribute of an RTE has been dropped (note that | ||||
|  * get_rte_attribute_type will fail on such an attr) | ||||
|  */ | ||||
| extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte, | ||||
| 										 AttrNumber attnum); | ||||
| 
 | ||||
| 
 | ||||
| /* ----------------
 | ||||
|  *		target list operations | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user