============================================================
*** src/backend/parser/analyze.c	ee280ecd5150ce51d4c3962f1a105cd790f557bf
--- src/backend/parser/analyze.c	c0423f774b8082b2617727a2440f53d9275bb39c
***************
*** 2584,2593 ****
  transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
  {
  	Query	   *result = makeNode(Query);
! 	List	   *argtype_oids = NIL;		/* argtype OIDs in a list */
  	Oid		   *argtoids = NULL;	/* and as an array */
  	int			nargs;
  	List	   *queries;
  
  	result->commandType = CMD_UTILITY;
  	result->utilityStmt = (Node *) stmt;
--- 2584,2594 ----
  transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
  {
  	Query	   *result = makeNode(Query);
! 	List	   *argtype_oids;		/* argtype OIDs in a list */
  	Oid		   *argtoids = NULL;	/* and as an array */
  	int			nargs;
  	List	   *queries;
+ 	int			i;
  
  	result->commandType = CMD_UTILITY;
  	result->utilityStmt = (Node *) stmt;
***************
*** 2598,2624 ****
  	if (nargs)
  	{
  		ListCell   *l;
- 		int			i = 0;
  
  		argtoids = (Oid *) palloc(nargs * sizeof(Oid));
  
  		foreach(l, stmt->argtypes)
  		{
  			TypeName   *tn = lfirst(l);
! 			Oid			toid = typenameTypeId(tn);
  
! 			argtype_oids = lappend_oid(argtype_oids, toid);
  			argtoids[i++] = toid;
  		}
  	}
  
- 	stmt->argtype_oids = argtype_oids;
- 
  	/*
! 	 * Analyze the statement using these parameter types (any parameters
! 	 * passed in from above us will not be visible to it).
  	 */
! 	queries = parse_analyze((Node *) stmt->query, argtoids, nargs);
  
  	/*
  	 * Shouldn't get any extra statements, since grammar only allows
--- 2599,2637 ----
  	if (nargs)
  	{
  		ListCell   *l;
  
  		argtoids = (Oid *) palloc(nargs * sizeof(Oid));
+ 		i = 0;
  
  		foreach(l, stmt->argtypes)
  		{
  			TypeName   *tn = lfirst(l);
! 			Oid			toid;
  
! 			/*
! 			 * For UNKNOWN, we have the type OID already. Otherwise,
! 			 * lookup the OID from the TypeName.
! 			 */
! 			if (tn->names == NULL)
! 			{
! 				Assert(tn->typeid == UNKNOWNOID);
! 				toid = tn->typeid;
! 			}
! 			else
! 				toid = typenameTypeId(tn);
! 
  			argtoids[i++] = toid;
  		}
  	}
  
  	/*
! 	 * Analyze the statement using these parameter types (any
! 	 * parameters passed in from above us will not be visible to it),
! 	 * allowing information about unknown parameters to be deduced
! 	 * from context.
  	 */
! 	queries = parse_analyze_varparams((Node *) stmt->query,
! 									  &argtoids, &nargs);
  
  	/*
  	 * Shouldn't get any extra statements, since grammar only allows
***************
*** 2627,2634 ****
  	if (list_length(queries) != 1)
  		elog(ERROR, "unexpected extra stuff in prepared statement");
  
! 	stmt->query = linitial(queries);
  
  	return result;
  }
  
--- 2640,2665 ----
  	if (list_length(queries) != 1)
  		elog(ERROR, "unexpected extra stuff in prepared statement");
  
! 	/*
! 	 * Check that all parameter types were determined, and convert the
! 	 * array of OIDs into a list for storage.
! 	 */
! 	argtype_oids = NIL;
! 	for (i = 0; i < nargs; i++)
! 	{
! 		Oid			argtype = argtoids[i];
  
+ 		if (argtype == InvalidOid || argtype == UNKNOWNOID)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INDETERMINATE_DATATYPE),
+ 					 errmsg("could not determine data type of parameter $%d",
+ 							i + 1)));
+ 
+ 		argtype_oids = lappend_oid(argtype_oids, argtype);
+ 	}
+ 
+ 	stmt->argtype_oids = argtype_oids;
+ 	stmt->query = linitial(queries);
  	return result;
  }
  
============================================================
*** src/backend/parser/gram.y	5f713badc9a33150446537ceced6fc0043ea2d79
--- src/backend/parser/gram.y	f305df1c4ea437626215e90005c02ed1beef2adb
***************
*** 53,58 ****
--- 53,59 ----
  
  #include "catalog/index.h"
  #include "catalog/namespace.h"
+ #include "catalog/pg_type.h"
  #include "nodes/makefuncs.h"
  #include "parser/gramparse.h"
  #include "storage/lmgr.h"
***************
*** 299,304 ****
--- 300,306 ----
  				CharacterWithLength CharacterWithoutLength
  				ConstDatetime ConstInterval
  				Bit ConstBit BitWithLength BitWithoutLength
+ 				prep_type_elem
  %type <str>		character
  %type <str>		extract_arg
  %type <str>		opt_charset
***************
*** 4995,5005 ****
  				| /* EMPTY */				{ $$ = NIL; }
  		;
  
! prep_type_list: Typename			{ $$ = list_make1($1); }
! 			  | prep_type_list ',' Typename
  									{ $$ = lappend($1, $3); }
  		;
  
  PreparableStmt:
  			SelectStmt
  			| InsertStmt
--- 4997,5019 ----
  				| /* EMPTY */				{ $$ = NIL; }
  		;
  
! prep_type_list: prep_type_elem		{ $$ = list_make1($1); }
! 			  | prep_type_list ',' prep_type_elem
  									{ $$ = lappend($1, $3); }
  		;
  
+ /*
+  * We allow a normal type name, or "UNKNOWN": the type of an unknown
+  * parameter will be inferred by the analysis phase.
+  */
+ prep_type_elem: Typename			{ $$ = $1; }
+ 			  | UNKNOWN
+ 				{
+ 					$$ = makeNode(TypeName);
+ 					$$->typeid = UNKNOWNOID;
+ 				}
+ 		;
+ 
  PreparableStmt:
  			SelectStmt
  			| InsertStmt
***************
*** 8314,8320 ****
  			| TYPE_P
  			| UNCOMMITTED
  			| UNENCRYPTED
- 			| UNKNOWN
  			| UNLISTEN
  			| UNTIL
  			| UPDATE
--- 8328,8333 ----
***************
*** 8381,8386 ****
--- 8394,8400 ----
  			| TREAT
  			| TRIM
  			| VARCHAR
+ 			| UNKNOWN
  		;
  
  /* Function identifier --- keywords that can be function names.
