mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Teach ruleutils to drill down into RECORD-type Vars in the same way
that the parser now can, so that it can reverse-list cases involving FieldSelect from a RECORD Var.
This commit is contained in:
		
							parent
							
								
									83b72ee286
								
							
						
					
					
						commit
						6dfe64ee57
					
				| @ -3,7 +3,7 @@ | |||||||
|  *				back to source text |  *				back to source text | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.197 2005/05/30 01:57:27 tgl Exp $ |  *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.198 2005/05/31 03:03:59 tgl Exp $ | ||||||
|  * |  * | ||||||
|  *	  This software is copyrighted by Jan Wieck - Hamburg. |  *	  This software is copyrighted by Jan Wieck - Hamburg. | ||||||
|  * |  * | ||||||
| @ -55,6 +55,7 @@ | |||||||
| #include "catalog/pg_shadow.h" | #include "catalog/pg_shadow.h" | ||||||
| #include "catalog/pg_trigger.h" | #include "catalog/pg_trigger.h" | ||||||
| #include "executor/spi.h" | #include "executor/spi.h" | ||||||
|  | #include "funcapi.h" | ||||||
| #include "lib/stringinfo.h" | #include "lib/stringinfo.h" | ||||||
| #include "miscadmin.h" | #include "miscadmin.h" | ||||||
| #include "nodes/makefuncs.h" | #include "nodes/makefuncs.h" | ||||||
| @ -2420,6 +2421,43 @@ get_utility_query_def(Query *query, deparse_context *context) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get the RTE referenced by a (possibly nonlocal) Var. | ||||||
|  |  * | ||||||
|  |  * In some cases (currently only when recursing into an unnamed join) | ||||||
|  |  * the Var's varlevelsup has to be interpreted with respect to a context | ||||||
|  |  * above the current one; levelsup indicates the offset. | ||||||
|  |  */ | ||||||
|  | static RangeTblEntry * | ||||||
|  | get_rte_for_var(Var *var, int levelsup, deparse_context *context) | ||||||
|  | { | ||||||
|  | 	RangeTblEntry *rte; | ||||||
|  | 	int			netlevelsup; | ||||||
|  | 	deparse_namespace *dpns; | ||||||
|  | 
 | ||||||
|  | 	/* Find appropriate nesting depth */ | ||||||
|  | 	netlevelsup = var->varlevelsup + levelsup; | ||||||
|  | 	if (netlevelsup >= list_length(context->namespaces)) | ||||||
|  | 		elog(ERROR, "bogus varlevelsup: %d offset %d", | ||||||
|  | 			 var->varlevelsup, levelsup); | ||||||
|  | 	dpns = (deparse_namespace *) list_nth(context->namespaces, | ||||||
|  | 										  netlevelsup); | ||||||
|  | 
 | ||||||
|  | 	/* Find the relevant RTE */ | ||||||
|  | 	if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) | ||||||
|  | 		rte = rt_fetch(var->varno, dpns->rtable); | ||||||
|  | 	else if (var->varno == dpns->outer_varno) | ||||||
|  | 		rte = dpns->outer_rte; | ||||||
|  | 	else if (var->varno == dpns->inner_varno) | ||||||
|  | 		rte = dpns->inner_rte; | ||||||
|  | 	else | ||||||
|  | 		rte = NULL; | ||||||
|  | 	if (rte == NULL) | ||||||
|  | 		elog(ERROR, "bogus varno: %d", var->varno); | ||||||
|  | 	return rte; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Get the schemaname, refname and attname for a (possibly nonlocal) Var. |  * Get the schemaname, refname and attname for a (possibly nonlocal) Var. | ||||||
|  * |  * | ||||||
| @ -2442,29 +2480,10 @@ static void | |||||||
| get_names_for_var(Var *var, int levelsup, deparse_context *context, | get_names_for_var(Var *var, int levelsup, deparse_context *context, | ||||||
| 				  char **schemaname, char **refname, char **attname) | 				  char **schemaname, char **refname, char **attname) | ||||||
| { | { | ||||||
| 	int			netlevelsup; |  | ||||||
| 	deparse_namespace *dpns; |  | ||||||
| 	RangeTblEntry *rte; | 	RangeTblEntry *rte; | ||||||
| 
 | 
 | ||||||
| 	/* Find appropriate nesting depth */ | 	/* Find appropriate RTE */ | ||||||
| 	netlevelsup = var->varlevelsup + levelsup; | 	rte = get_rte_for_var(var, levelsup, context); | ||||||
| 	if (netlevelsup >= list_length(context->namespaces)) |  | ||||||
| 		elog(ERROR, "bogus varlevelsup: %d offset %d", |  | ||||||
| 			 var->varlevelsup, levelsup); |  | ||||||
| 	dpns = (deparse_namespace *) list_nth(context->namespaces, |  | ||||||
| 										  netlevelsup); |  | ||||||
| 
 |  | ||||||
| 	/* Find the relevant RTE */ |  | ||||||
| 	if (var->varno >= 1 && var->varno <= list_length(dpns->rtable)) |  | ||||||
| 		rte = rt_fetch(var->varno, dpns->rtable); |  | ||||||
| 	else if (var->varno == dpns->outer_varno) |  | ||||||
| 		rte = dpns->outer_rte; |  | ||||||
| 	else if (var->varno == dpns->inner_varno) |  | ||||||
| 		rte = dpns->inner_rte; |  | ||||||
| 	else |  | ||||||
| 		rte = NULL; |  | ||||||
| 	if (rte == NULL) |  | ||||||
| 		elog(ERROR, "bogus varno: %d", var->varno); |  | ||||||
| 
 | 
 | ||||||
| 	/* Emit results */ | 	/* Emit results */ | ||||||
| 	*schemaname = NULL;			/* default assumptions */ | 	*schemaname = NULL;			/* default assumptions */ | ||||||
| @ -2505,7 +2524,8 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context, | |||||||
| 											var->varattno-1); | 											var->varattno-1); | ||||||
| 				if (IsA(aliasvar, Var)) | 				if (IsA(aliasvar, Var)) | ||||||
| 				{ | 				{ | ||||||
| 					get_names_for_var(aliasvar, netlevelsup, context, | 					get_names_for_var(aliasvar, | ||||||
|  | 									  var->varlevelsup + levelsup, context, | ||||||
| 									  schemaname, refname, attname); | 									  schemaname, refname, attname); | ||||||
| 					return; | 					return; | ||||||
| 				} | 				} | ||||||
| @ -2521,6 +2541,127 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context, | |||||||
| 		*attname = get_rte_attribute_name(rte, var->varattno); | 		*attname = get_rte_attribute_name(rte, var->varattno); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Get the name of a field of a Var of type RECORD. | ||||||
|  |  * | ||||||
|  |  * Since no actual table or view column is allowed to have type RECORD, such | ||||||
|  |  * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.  We | ||||||
|  |  * drill down to find the ultimate defining expression and attempt to infer | ||||||
|  |  * the field name from it.  We ereport if we can't determine the name. | ||||||
|  |  * | ||||||
|  |  * levelsup is an extra offset to interpret the Var's varlevelsup correctly. | ||||||
|  |  * | ||||||
|  |  * Note: this has essentially the same logic as the parser's | ||||||
|  |  * expandRecordVariable() function, but we are dealing with a different | ||||||
|  |  * representation of the input context, and we only need one field name not | ||||||
|  |  * a TupleDesc. | ||||||
|  |  */ | ||||||
|  | static const char * | ||||||
|  | get_name_for_var_field(Var *var, int fieldno, | ||||||
|  | 					   int levelsup, deparse_context *context) | ||||||
|  | { | ||||||
|  | 	RangeTblEntry *rte; | ||||||
|  | 	AttrNumber	attnum; | ||||||
|  | 	TupleDesc	tupleDesc; | ||||||
|  | 	Node	   *expr; | ||||||
|  | 
 | ||||||
|  | 	/* Check my caller didn't mess up */ | ||||||
|  | 	Assert(IsA(var, Var)); | ||||||
|  | 	Assert(var->vartype == RECORDOID); | ||||||
|  | 
 | ||||||
|  | 	/* Find appropriate RTE */ | ||||||
|  | 	rte = get_rte_for_var(var, levelsup, context); | ||||||
|  | 
 | ||||||
|  | 	attnum = var->varattno; | ||||||
|  | 
 | ||||||
|  | 	if (attnum == InvalidAttrNumber) | ||||||
|  | 	{ | ||||||
|  | 		/* Var is whole-row reference to RTE, so select the right field */ | ||||||
|  | 		return get_rte_attribute_name(rte, fieldno); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	expr = (Node *) var;		/* default if we can't drill down */ | ||||||
|  | 
 | ||||||
|  | 	switch (rte->rtekind) | ||||||
|  | 	{ | ||||||
|  | 		case RTE_RELATION: | ||||||
|  | 		case RTE_SPECIAL: | ||||||
|  | 			/*
 | ||||||
|  | 			 * This case should not occur: a column of a table 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 namespace | ||||||
|  | 					 * to keep in step with varlevelsup in the subselect. | ||||||
|  | 					 */ | ||||||
|  | 					deparse_namespace mydpns; | ||||||
|  | 					const char *result; | ||||||
|  | 
 | ||||||
|  | 					mydpns.rtable = rte->subquery->rtable; | ||||||
|  | 					mydpns.outer_varno = mydpns.inner_varno = 0; | ||||||
|  | 					mydpns.outer_rte = mydpns.inner_rte = NULL; | ||||||
|  | 
 | ||||||
|  | 					context->namespaces = lcons(&mydpns, context->namespaces); | ||||||
|  | 
 | ||||||
|  | 					result = get_name_for_var_field((Var *) expr, fieldno, | ||||||
|  | 													0, context); | ||||||
|  | 
 | ||||||
|  | 					context->namespaces = list_delete_first(context->namespaces); | ||||||
|  | 
 | ||||||
|  | 					return result; | ||||||
|  | 				} | ||||||
|  | 				/* 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 get_name_for_var_field((Var *) expr, fieldno, | ||||||
|  | 											  var->varlevelsup + levelsup, | ||||||
|  | 											  context); | ||||||
|  | 			/* 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; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * 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(exprType(expr), exprTypmod(expr)); | ||||||
|  | 
 | ||||||
|  | 	/* Got the tupdesc, so we can extract the field name */ | ||||||
|  | 	Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); | ||||||
|  | 	return NameStr(tupleDesc->attrs[fieldno - 1]->attname); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * find_rte_by_refname		- look up an RTE by refname in a deparse context |  * find_rte_by_refname		- look up an RTE by refname in a deparse context | ||||||
|  * |  * | ||||||
| @ -3109,19 +3250,11 @@ get_rule_expr(Node *node, deparse_context *context, | |||||||
| 		case T_FieldSelect: | 		case T_FieldSelect: | ||||||
| 			{ | 			{ | ||||||
| 				FieldSelect *fselect = (FieldSelect *) node; | 				FieldSelect *fselect = (FieldSelect *) node; | ||||||
| 				Oid			argType = exprType((Node *) fselect->arg); | 				Node	   *arg = (Node *) fselect->arg; | ||||||
| 				Oid			typrelid; | 				int			fno = fselect->fieldnum; | ||||||
| 				char	   *fieldname; | 				const char *fieldname; | ||||||
| 				bool		need_parens; | 				bool		need_parens; | ||||||
| 
 | 
 | ||||||
| 				/* lookup arg type and get the field name */ |  | ||||||
| 				typrelid = get_typ_typrelid(argType); |  | ||||||
| 				if (!OidIsValid(typrelid)) |  | ||||||
| 					elog(ERROR, "argument type %s of FieldSelect is not a tuple type", |  | ||||||
| 						 format_type_be(argType)); |  | ||||||
| 				fieldname = get_relid_attribute_name(typrelid, |  | ||||||
| 													 fselect->fieldnum); |  | ||||||
| 
 |  | ||||||
| 				/*
 | 				/*
 | ||||||
| 				 * Parenthesize the argument unless it's an ArrayRef or | 				 * Parenthesize the argument unless it's an ArrayRef or | ||||||
| 				 * another FieldSelect.  Note in particular that it would | 				 * another FieldSelect.  Note in particular that it would | ||||||
| @ -3129,13 +3262,36 @@ get_rule_expr(Node *node, deparse_context *context, | |||||||
| 				 * is not the issue here, having the right number of names | 				 * is not the issue here, having the right number of names | ||||||
| 				 * is. | 				 * is. | ||||||
| 				 */ | 				 */ | ||||||
| 				need_parens = !IsA(fselect->arg, ArrayRef) && | 				need_parens = !IsA(arg, ArrayRef) && !IsA(arg, FieldSelect); | ||||||
| 					!IsA(fselect->arg, FieldSelect); |  | ||||||
| 				if (need_parens) | 				if (need_parens) | ||||||
| 					appendStringInfoChar(buf, '('); | 					appendStringInfoChar(buf, '('); | ||||||
| 				get_rule_expr((Node *) fselect->arg, context, true); | 				get_rule_expr(arg, context, true); | ||||||
| 				if (need_parens) | 				if (need_parens) | ||||||
| 					appendStringInfoChar(buf, ')'); | 					appendStringInfoChar(buf, ')'); | ||||||
|  | 
 | ||||||
|  | 				/*
 | ||||||
|  | 				 * If it's a Var of type RECORD, we have to find what the Var | ||||||
|  | 				 * refers to; otherwise we can use get_expr_result_type. | ||||||
|  | 				 * If that fails, we try lookup_rowtype_tupdesc, which will | ||||||
|  | 				 * probably fail too, but will ereport an acceptable message. | ||||||
|  | 				 */ | ||||||
|  | 				if (IsA(arg, Var) && | ||||||
|  | 					((Var *) arg)->vartype == RECORDOID) | ||||||
|  | 					fieldname = get_name_for_var_field((Var *) arg, fno, | ||||||
|  | 													   0, context); | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					TupleDesc	tupdesc; | ||||||
|  | 
 | ||||||
|  | 					if (get_expr_result_type(arg, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) | ||||||
|  | 						tupdesc = lookup_rowtype_tupdesc(exprType(arg), | ||||||
|  | 														 exprTypmod(arg)); | ||||||
|  | 					Assert(tupdesc); | ||||||
|  | 					/* Got the tupdesc, so we can extract the field name */ | ||||||
|  | 					Assert(fno >= 1 && fno <= tupdesc->natts); | ||||||
|  | 					fieldname = NameStr(tupdesc->attrs[fno - 1]->attname); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
| 				appendStringInfo(buf, ".%s", quote_identifier(fieldname)); | 				appendStringInfo(buf, ".%s", quote_identifier(fieldname)); | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user