From aa282d44464df0fbfa0672dc353d36734ec1092e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 29 Apr 2003 22:13:11 +0000
Subject: [PATCH] Infrastructure for deducing Param types from context, in the
 same way that the types of untyped string-literal constants are deduced (ie,
 when coerce_type is applied to 'em, that's what the type must be). Remove the
 ancient hack of storing the input Param-types array as a global variable, and
 put the info into ParseState instead.  This touches a lot of files because of
 adjustment of routine parameter lists, but it's really not a large patch. 
 Note: PREPARE statement still insists on exact specification of parameter
 types, but that could easily be relaxed now, if we wanted to do so.

---
 src/backend/catalog/heap.c             |   6 +-
 src/backend/commands/schemacmds.c      |   4 +-
 src/backend/commands/tablecmds.c       |   4 +-
 src/backend/commands/typecmds.c        |   4 +-
 src/backend/executor/spi.c             |   6 +-
 src/backend/optimizer/plan/subselect.c |   9 +-
 src/backend/optimizer/prep/prepunion.c |   5 +-
 src/backend/optimizer/util/clauses.c   |  14 +--
 src/backend/parser/analyze.c           | 163 ++++++++++++++++++-------
 src/backend/parser/parse_clause.c      |  22 ++--
 src/backend/parser/parse_coerce.c      | 116 +++++++++++++-----
 src/backend/parser/parse_expr.c        | 102 ++++++++++++----
 src/backend/parser/parse_func.c        |  16 ++-
 src/backend/parser/parse_node.c        |  18 ++-
 src/backend/parser/parse_oper.c        |  17 ++-
 src/backend/parser/parse_relation.c    |   4 +-
 src/backend/parser/parse_target.c      |   7 +-
 src/backend/parser/parse_type.c        |   4 +-
 src/backend/parser/parser.c            |  44 +------
 src/backend/rewrite/rewriteHandler.c   |   5 +-
 src/backend/tcop/postgres.c            |  33 ++---
 src/include/parser/analyze.h           |   8 +-
 src/include/parser/gramparse.h         |   5 +-
 src/include/parser/parse_coerce.h      |  17 ++-
 src/include/parser/parse_func.h        |   6 +-
 src/include/parser/parse_node.h        |  17 ++-
 src/include/parser/parse_oper.h        |  11 +-
 src/include/parser/parser.h            |   8 +-
 src/include/tcop/tcopprot.h            |  12 +-
 29 files changed, 441 insertions(+), 246 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 802f5932f73..5a6ec98e1b8 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.241 2003/03/23 05:14:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.242 2003/04/29 22:13:08 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1592,7 +1592,7 @@ AddRelationRawConstraints(Relation rel,
 		/*
 		 * Make sure it yields a boolean result.
 		 */
-		expr = coerce_to_boolean(expr, "CHECK");
+		expr = coerce_to_boolean(pstate, expr, "CHECK");
 
 		/*
 		 * Make sure no outside relations are referred to.
@@ -1743,7 +1743,7 @@ cookDefault(ParseState *pstate,
 	{
 		Oid			type_id = exprType(expr);
 
-		if (coerce_to_target_type(expr, type_id,
+		if (coerce_to_target_type(pstate, expr, type_id,
 								  atttypid, atttypmod,
 								  COERCION_ASSIGNMENT,
 								  COERCE_IMPLICIT_CAST) == NULL)
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index ba37c249873..4e2224e189b 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.7 2002/12/05 04:04:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.8 2003/04/29 22:13:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -124,7 +124,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
 		List	   *querytree_list,
 				   *querytree_item;
 
-		querytree_list = parse_analyze(parsetree, NULL);
+		querytree_list = parse_analyze(parsetree, NULL, 0);
 
 		foreach(querytree_item, querytree_list)
 		{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index b4576893c66..5f60ab9cf01 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.71 2003/04/21 15:19:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.72 2003/04/29 22:13:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2852,7 +2852,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
 	/*
 	 * Make sure it yields a boolean result.
 	 */
-	expr = coerce_to_boolean(expr, "CHECK");
+	expr = coerce_to_boolean(pstate, expr, "CHECK");
 
 	/*
 	 * Make sure no outside relations are referred to.
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 3cc11ed81a5..0523878f2a7 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.33 2003/04/08 16:57:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.34 2003/04/29 22:13:08 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -1601,7 +1601,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
 	/*
 	 * Make sure it yields a boolean result.
 	 */
-	expr = coerce_to_boolean(expr, "CHECK");
+	expr = coerce_to_boolean(pstate, expr, "CHECK");
 
 	/*
 	 * Make sure no outside relations are
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 218b56a6013..aca27d0aebf 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.92 2003/04/29 03:21:29 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.93 2003/04/29 22:13:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -993,7 +993,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 	/*
 	 * Parse the request string into a list of raw parse trees.
 	 */
-	raw_parsetree_list = pg_parse_query(src, argtypes, nargs);
+	raw_parsetree_list = pg_parse_query(src);
 
 	/*
 	 * Do parse analysis and rule rewrite for each raw parsetree.
@@ -1036,7 +1036,7 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
 		if (plan)
 			plan->origCmdType = origCmdType;
 
-		query_list = pg_analyze_and_rewrite(parsetree);
+		query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
 
 		query_list_list = lappend(query_list_list, query_list);
 
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 15d16d00221..4be69d77cfd 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.74 2003/04/08 23:20:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.75 2003/04/29 22:13:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -495,10 +495,13 @@ convert_sublink_opers(List *lefthand, List *operOids,
 		 * Make the expression node.
 		 *
 		 * Note: we use make_op_expr in case runtime type conversion
-		 * function calls must be inserted for this operator!
+		 * function calls must be inserted for this operator!  (But we
+		 * are not expecting to have to resolve unknown Params, so
+		 * it's okay to pass a null pstate.)
 		 */
 		result = lappend(result,
-						 make_op_expr(tup,
+						 make_op_expr(NULL,
+									  tup,
 									  leftop,
 									  rightop,
 									  exprType(leftop),
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index fb7bf9e7070..d2b91c2ec6d 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.93 2003/04/24 23:43:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.94 2003/04/29 22:13:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -418,7 +418,8 @@ generate_setop_tlist(List *colTypes, int flag,
 		}
 		else
 		{
-			expr = coerce_to_common_type(expr,
+			expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
+										 expr,
 										 colType,
 										 "UNION/INTERSECT/EXCEPT");
 			colTypmod = -1;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 778fc3a2bb9..c0ffd939cbe 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.135 2003/04/27 20:09:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.136 2003/04/29 22:13:09 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1747,17 +1747,17 @@ inline_function(Oid funcid, Oid result_type, List *args,
 
 	/*
 	 * We just do parsing and parse analysis, not rewriting, because
-	 * rewriting will not affect SELECT-only queries, which is all that
-	 * we care about.  Also, we can punt as soon as we detect more than
+	 * rewriting will not affect table-free-SELECT-only queries, which is all
+	 * that we care about.  Also, we can punt as soon as we detect more than
 	 * one command in the function body.
 	 */
-	raw_parsetree_list = pg_parse_query(src,
-										funcform->proargtypes,
-										funcform->pronargs);
+	raw_parsetree_list = pg_parse_query(src);
 	if (length(raw_parsetree_list) != 1)
 		goto fail;
 
-	querytree_list = parse_analyze(lfirst(raw_parsetree_list), NULL);
+	querytree_list = parse_analyze(lfirst(raw_parsetree_list),
+								   funcform->proargtypes,
+								   funcform->pronargs);
 
 	if (length(querytree_list) != 1)
 		goto fail;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5dc3d82bea8..c2159a70e0e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.267 2003/04/29 03:21:29 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.268 2003/04/29 22:13:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -84,6 +84,7 @@ typedef struct
 } CreateStmtContext;
 
 
+static List *do_parse_analyze(Node *parseTree, ParseState *pstate);
 static Query *transformStmt(ParseState *pstate, Node *stmt,
 			  List **extras_before, List **extras_after);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
@@ -125,10 +126,12 @@ static void release_pstate_resources(ParseState *pstate);
 static FromExpr *makeFromExpr(List *fromlist, Node *quals);
 
 
-
 /*
- * parse_analyze -
- *	  analyze a raw parse tree and transform it to Query form.
+ * parse_analyze
+ *		Analyze a raw parse tree and transform it to Query form.
+ *
+ * Optionally, information about $n parameter types can be supplied.
+ * References to $n indexes not defined by paramTypes[] are disallowed.
  *
  * The result is a List of Query nodes (we need a list since some commands
  * produce multiple Queries).  Optimizable statements require considerable
@@ -136,11 +139,74 @@ static FromExpr *makeFromExpr(List *fromlist, Node *quals);
  * a dummy CMD_UTILITY Query node.
  */
 List *
-parse_analyze(Node *parseTree, ParseState *parentParseState)
+parse_analyze(Node *parseTree, Oid *paramTypes, int numParams)
+{
+	ParseState *pstate = make_parsestate(NULL);
+	List	   *result;
+
+	pstate->p_paramtypes = paramTypes;
+	pstate->p_numparams = numParams;
+	pstate->p_variableparams = false;
+
+	result = do_parse_analyze(parseTree, pstate);
+
+	pfree(pstate);
+
+	return result;
+}
+
+/*
+ * parse_analyze_varparams
+ *
+ * This variant is used when it's okay to deduce information about $n
+ * symbol datatypes from context.  The passed-in paramTypes[] array can
+ * be modified or enlarged (via repalloc).
+ */
+List *
+parse_analyze_varparams(Node *parseTree, Oid **paramTypes, int *numParams)
+{
+	ParseState *pstate = make_parsestate(NULL);
+	List	   *result;
+
+	pstate->p_paramtypes = *paramTypes;
+	pstate->p_numparams = *numParams;
+	pstate->p_variableparams = true;
+
+	result = do_parse_analyze(parseTree, pstate);
+
+	*paramTypes = pstate->p_paramtypes;
+	*numParams = pstate->p_numparams;
+
+	pfree(pstate);
+
+	return result;
+}
+
+/*
+ * parse_sub_analyze
+ *		Entry point for recursively analyzing a sub-statement.
+ */
+List *
+parse_sub_analyze(Node *parseTree, ParseState *parentParseState)
 {
-	List	   *result = NIL;
 	ParseState *pstate = make_parsestate(parentParseState);
+	List	   *result;
+
+	result = do_parse_analyze(parseTree, pstate);
+
+	pfree(pstate);
 
+	return result;
+}
+
+/*
+ * do_parse_analyze
+ *		Workhorse code shared by the above variants of parse_analyze.
+ */
+static List *
+do_parse_analyze(Node *parseTree, ParseState *pstate)
+{
+	List	   *result = NIL;
 	/* Lists to return extra commands from transformation */
 	List	   *extras_before = NIL;
 	List	   *extras_after = NIL;
@@ -148,11 +214,14 @@ parse_analyze(Node *parseTree, ParseState *parentParseState)
 	List	   *listscan;
 
 	query = transformStmt(pstate, parseTree, &extras_before, &extras_after);
+
+	/* don't need to access result relation any more */
 	release_pstate_resources(pstate);
 
 	while (extras_before != NIL)
 	{
-		result = nconc(result, parse_analyze(lfirst(extras_before), pstate));
+		result = nconc(result,
+					   parse_sub_analyze(lfirst(extras_before), pstate));
 		extras_before = lnext(extras_before);
 	}
 
@@ -160,13 +229,14 @@ parse_analyze(Node *parseTree, ParseState *parentParseState)
 
 	while (extras_after != NIL)
 	{
-		result = nconc(result, parse_analyze(lfirst(extras_after), pstate));
+		result = nconc(result,
+					   parse_sub_analyze(lfirst(extras_after), pstate));
 		extras_after = lnext(extras_after);
 	}
 
 	/*
 	 * Make sure that only the original query is marked original. We have
-	 * to do this explicitly since recursive calls of parse_analyze will
+	 * to do this explicitly since recursive calls of do_parse_analyze will
 	 * have marked some of the added-on queries as "original".
 	 */
 	foreach(listscan, result)
@@ -176,8 +246,6 @@ parse_analyze(Node *parseTree, ParseState *parentParseState)
 		q->querySource = (q == query ? QSRC_ORIGINAL : QSRC_PARSER);
 	}
 
-	pfree(pstate);
-
 	return result;
 }
 
@@ -423,7 +491,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 	 */
 	if (stmt->selectStmt)
 	{
-		ParseState *sub_pstate = make_parsestate(pstate->parentParseState);
+		/*
+		 * We make the sub-pstate a child of the outer pstate so that it
+		 * can see any Param definitions supplied from above.  Since the
+		 * outer pstate's rtable and namespace are presently empty, there
+		 * are no side-effects of exposing names the sub-SELECT shouldn't
+		 * be able to see.
+		 */
+		ParseState *sub_pstate = make_parsestate(pstate);
 		Query	   *selectQuery;
 		RangeTblEntry *rte;
 		RangeTblRef *rtr;
@@ -475,12 +550,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 		 * separate from the subquery's tlist because we may add columns,
 		 * insert datatype coercions, etc.)
 		 *
-		 * HACK: constants in the INSERT's targetlist are copied up as-is
-		 * rather than being referenced as subquery outputs.  This is
-		 * mainly to ensure that when we try to coerce them to the target
-		 * column's datatype, the right things happen for UNKNOWN
-		 * constants. Otherwise this fails: INSERT INTO foo SELECT 'bar',
-		 * ... FROM baz
+		 * HACK: unknown-type constants and params in the INSERT's targetlist
+		 * are copied up as-is rather than being referenced as subquery
+		 * outputs.  This is to ensure that when we try to coerce them
+		 * to the target column's datatype, the right things happen (see
+		 * special cases in coerce_type).  Otherwise, this fails:
+		 *		INSERT INTO foo SELECT 'bar', ... FROM baz
 		 */
 		qry->targetList = NIL;
 		foreach(tl, selectQuery->targetList)
@@ -491,7 +566,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 
 			if (resnode->resjunk)
 				continue;
-			if (tle->expr && IsA(tle->expr, Const))
+			if (tle->expr &&
+				(IsA(tle->expr, Const) || IsA(tle->expr, Param)) &&
+				exprType((Node *) tle->expr) == UNKNOWNOID)
 				expr = tle->expr;
 			else
 				expr = (Expr *) makeVar(rtr->rtindex,
@@ -500,7 +577,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 										resnode->restypmod,
 										0);
 			resnode = copyObject(resnode);
-			resnode->resno = (AttrNumber) pstate->p_last_resno++;
+			resnode->resno = (AttrNumber) pstate->p_next_resno++;
 			qry->targetList = lappend(qry->targetList,
 									  makeTargetEntry(resnode, expr));
 		}
@@ -520,8 +597,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
 	 */
 
 	/* Prepare to assign non-conflicting resnos to resjunk attributes */
-	if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
-		pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+	if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts)
+		pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
 
 	/* Validate stmt->cols list, or build default list if no list given */
 	icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
@@ -1484,7 +1561,7 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
 		List	   *newactions = NIL;
 
 		/*
-		 * transform each statement, like parse_analyze()
+		 * transform each statement, like parse_sub_analyze()
 		 */
 		foreach(oldactions, stmt->actions)
 		{
@@ -1789,7 +1866,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 		Resdom	   *resdom;
 		Expr	   *expr;
 
-		resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
+		resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
 							colType,
 							-1,
 							colName,
@@ -1938,7 +2015,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
 		 * of this sub-query, because they are not in the toplevel
 		 * pstate's namespace list.
 		 */
-		selectList = parse_analyze((Node *) stmt, pstate);
+		selectList = parse_sub_analyze((Node *) stmt, pstate);
 
 		Assert(length(selectList) == 1);
 		selectQuery = (Query *) lfirst(selectList);
@@ -2132,8 +2209,8 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	 */
 
 	/* Prepare to assign non-conflicting resnos to resjunk attributes */
-	if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
-		pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+	if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts)
+		pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
 
 	/* Prepare non-junk columns for assignment to target table */
 	origTargetList = stmt->targetList;
@@ -2151,7 +2228,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 			 * columns; else rewriter or planner might get confused.
 			 */
 			resnode->resname = "?resjunk?";
-			resnode->resno = (AttrNumber) pstate->p_last_resno++;
+			resnode->resno = (AttrNumber) pstate->p_next_resno++;
 			continue;
 		}
 		if (origTargetList == NIL)
@@ -2316,11 +2393,10 @@ static Query *
 transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
 {
 	Query	   *result = makeNode(Query);
-	List	   *extras_before = NIL,
-			   *extras_after = NIL;
 	List	   *argtype_oids = NIL;		/* argtype OIDs in a list */
-	Oid		   *argtoids = NULL;	/* as an array for parser_param_set */
+	Oid		   *argtoids = NULL;		/* and as an array */
 	int			nargs;
+	List	   *queries;
 
 	result->commandType = CMD_UTILITY;
 	result->utilityStmt = (Node *) stmt;
@@ -2348,24 +2424,19 @@ transformPrepareStmt(ParseState *pstate, PrepareStmt *stmt)
 	stmt->argtype_oids = argtype_oids;
 
 	/*
-	 * We need to adjust the parameters expected by the rest of the
-	 * system, so that $1, ... $n are parsed properly.
-	 *
-	 * This is somewhat of a hack; however, the main parser interface only
-	 * allows parameters to be specified when working with a raw query
-	 * string, which is not helpful here.
+	 * Analyze the statement using these parameter types (any parameters
+	 * passed in from above us will not be visible to it).
 	 */
-	parser_param_set(argtoids, nargs);
-
-	stmt->query = transformStmt(pstate, (Node *) stmt->query,
-								&extras_before, &extras_after);
+	queries = parse_analyze((Node *) stmt->query, argtoids, nargs);
 
-	/* Shouldn't get any extras, since grammar only allows OptimizableStmt */
-	if (extras_before || extras_after)
+	/*
+	 * Shouldn't get any extra statements, since grammar only allows
+	 * OptimizableStmt
+	 */
+	if (length(queries) != 1)
 		elog(ERROR, "transformPrepareStmt: internal error");
 
-	/* Remove links to our local parameters */
-	parser_param_set(NULL, 0);
+	stmt->query = lfirst(queries);
 
 	return result;
 }
@@ -2409,7 +2480,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
 			given_type_id = exprType(expr);
 			expected_type_id = lfirsto(paramtypes);
 
-			expr = coerce_to_target_type(expr, given_type_id,
+			expr = coerce_to_target_type(pstate, expr, given_type_id,
 										 expected_type_id, -1,
 										 COERCION_ASSIGNMENT,
 										 COERCE_IMPLICIT_CAST);
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 2fd5811000a..1c8cb8bc0e3 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.112 2003/03/22 01:49:38 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.113 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,7 +54,7 @@ static RangeTblRef *transformRangeFunction(ParseState *pstate,
 					   RangeFunction *r);
 static Node *transformFromClauseItem(ParseState *pstate, Node *n,
 						List **containedRels);
-static Node *buildMergedJoinVar(JoinType jointype,
+static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 				   Var *l_colvar, Var *r_colvar);
 static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
 					List *tlist, int clause);
@@ -284,7 +284,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
 	 */
 	result = transformExpr(pstate, result);
 
-	result = coerce_to_boolean(result, "JOIN/USING");
+	result = coerce_to_boolean(pstate, result, "JOIN/USING");
 
 	return result;
 }	/* transformJoinUsingClause() */
@@ -318,7 +318,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
 	/* This part is just like transformWhereClause() */
 	result = transformExpr(pstate, j->quals);
 
-	result = coerce_to_boolean(result, "JOIN/ON");
+	result = coerce_to_boolean(pstate, result, "JOIN/ON");
 
 	pstate->p_namespace = save_namespace;
 
@@ -398,7 +398,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
 	/*
 	 * Analyze and transform the subquery.
 	 */
-	parsetrees = parse_analyze(r->subquery, pstate);
+	parsetrees = parse_sub_analyze(r->subquery, pstate);
 
 	/*
 	 * Check that we got something reasonable.	Some of these conditions
@@ -759,7 +759,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
 
 				res_colnames = lappend(res_colnames, lfirst(ucol));
 				res_colvars = lappend(res_colvars,
-									  buildMergedJoinVar(j->jointype,
+									  buildMergedJoinVar(pstate,
+														 j->jointype,
 														 l_colvar,
 														 r_colvar));
 			}
@@ -836,7 +837,8 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
  *	  generate a suitable replacement expression for a merged join column
  */
 static Node *
-buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
+buildMergedJoinVar(ParseState *pstate, JoinType jointype,
+				   Var *l_colvar, Var *r_colvar)
 {
 	Oid			outcoltype;
 	int32		outcoltypmod;
@@ -869,7 +871,7 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
 	 * typmod is not same as input.
 	 */
 	if (l_colvar->vartype != outcoltype)
-		l_node = coerce_type((Node *) l_colvar, l_colvar->vartype,
+		l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype,
 							 outcoltype,
 							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
 	else if (l_colvar->vartypmod != outcoltypmod)
@@ -880,7 +882,7 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar)
 		l_node = (Node *) l_colvar;
 
 	if (r_colvar->vartype != outcoltype)
-		r_node = coerce_type((Node *) r_colvar, r_colvar->vartype,
+		r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype,
 							 outcoltype,
 							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
 	else if (r_colvar->vartypmod != outcoltypmod)
@@ -953,7 +955,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
 
 	qual = transformExpr(pstate, clause);
 
-	qual = coerce_to_boolean(qual, "WHERE");
+	qual = coerce_to_boolean(pstate, qual, "WHERE");
 
 	return qual;
 }
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a4b739b0f7e..9dc0c7f1c19 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.95 2003/04/10 02:47:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.96 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "catalog/pg_cast.h"
 #include "catalog/pg_proc.h"
 #include "nodes/makefuncs.h"
+#include "nodes/params.h"
 #include "optimizer/clauses.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
@@ -49,6 +50,7 @@ static Node *build_func_call(Oid funcid, Oid rettype, List *args,
  * conversion is not possible.  (We do this, rather than elog'ing directly,
  * so that callers can generate custom error messages indicating context.)
  *
+ * pstate - parse state (can be NULL, see coerce_type)
  * expr - input expression tree (already transformed by transformExpr)
  * exprtype - result type of expr
  * targettype - desired result type
@@ -56,13 +58,13 @@ static Node *build_func_call(Oid funcid, Oid rettype, List *args,
  * ccontext, cformat - context indicators to control coercions
  */
 Node *
-coerce_to_target_type(Node *expr, Oid exprtype,
+coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 					  Oid targettype, int32 targettypmod,
 					  CoercionContext ccontext,
 					  CoercionForm cformat)
 {
 	if (can_coerce_type(1, &exprtype, &targettype, ccontext))
-		expr = coerce_type(expr, exprtype, targettype,
+		expr = coerce_type(pstate, expr, exprtype, targettype,
 						   ccontext, cformat);
 	/*
 	 * String hacks to get transparent conversions for char and varchar:
@@ -79,7 +81,7 @@ coerce_to_target_type(Node *expr, Oid exprtype,
 
 		if (can_coerce_type(1, &exprtype, &text_id, ccontext))
 		{
-			expr = coerce_type(expr, exprtype, text_id,
+			expr = coerce_type(pstate, expr, exprtype, text_id,
 							   ccontext, cformat);
 			/* Need a RelabelType if no typmod coercion is performed */
 			if (targettypmod < 0)
@@ -117,9 +119,14 @@ coerce_to_target_type(Node *expr, Oid exprtype,
  * call coerce_type_typmod as well, if a typmod constraint is wanted.
  * (But if the target type is a domain, it may internally contain a
  * typmod constraint, which will be applied inside coerce_to_domain.)
+ *
+ * pstate is only used in the case that we are able to resolve the type of
+ * a previously UNKNOWN Param.  It is okay to pass pstate = NULL if the
+ * caller does not want type information updated for Params.
  */
 Node *
-coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
+coerce_type(ParseState *pstate, Node *node,
+			Oid inputTypeId, Oid targetTypeId,
 			CoercionContext ccontext, CoercionForm cformat)
 {
 	Node	   *result;
@@ -129,9 +136,9 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
 		node == NULL)
 	{
 		/* no conversion needed */
-		result = node;
+		return node;
 	}
-	else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
+	if (inputTypeId == UNKNOWNOID && IsA(node, Const))
 	{
 		/*
 		 * Input is a string constant with previously undetermined type.
@@ -187,17 +194,62 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
 									  cformat);
 
 		ReleaseSysCache(targetType);
+
+		return result;
 	}
-	else if (targetTypeId == ANYOID ||
-			 targetTypeId == ANYARRAYOID ||
-			 targetTypeId == ANYELEMENTOID)
+	if (inputTypeId == UNKNOWNOID && IsA(node, Param) &&
+		((Param *) node)->paramkind == PARAM_NUM &&
+		pstate != NULL && pstate->p_variableparams)
+	{
+		/*
+		 * Input is a Param of previously undetermined type, and we want
+		 * to update our knowledge of the Param's type.  Find the topmost
+		 * ParseState and update the state.
+		 */
+		Param	   *param = (Param *) node;
+		int			paramno = param->paramid;
+		ParseState *toppstate;
+
+		toppstate = pstate;
+		while (toppstate->parentParseState != NULL)
+			toppstate = toppstate->parentParseState;
+
+		if (paramno <= 0 ||		/* shouldn't happen, but... */
+			paramno > toppstate->p_numparams)
+			elog(ERROR, "Parameter '$%d' is out of range", paramno);
+
+		if (toppstate->p_paramtypes[paramno-1] == UNKNOWNOID)
+		{
+			/* We've successfully resolved the type */
+			toppstate->p_paramtypes[paramno-1] = targetTypeId;
+		}
+		else if (toppstate->p_paramtypes[paramno-1] == targetTypeId)
+		{
+			/* We previously resolved the type, and it matches */
+		}
+		else
+		{
+			/* Ooops */
+			elog(ERROR, "Inconsistent types deduced for parameter '$%d'"
+				 "\n\tCould be either %s or %s",
+				 paramno,
+				 format_type_be(toppstate->p_paramtypes[paramno-1]),
+				 format_type_be(targetTypeId));
+		}
+
+		param->paramtype = targetTypeId;
+		return (Node *) param;
+	}
+	if (targetTypeId == ANYOID ||
+		targetTypeId == ANYARRAYOID ||
+		targetTypeId == ANYELEMENTOID)
 	{
 		/* assume can_coerce_type verified that implicit coercion is okay */
 		/* NB: we do NOT want a RelabelType here */
-		result = node;
+		return node;
 	}
-	else if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
-								   &funcId))
+	if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
+							  &funcId))
 	{
 		if (OidIsValid(funcId))
 		{
@@ -247,27 +299,23 @@ coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
 												  cformat);
 			}
 		}
+		return result;
 	}
-	else if (typeInheritsFrom(inputTypeId, targetTypeId))
+	if (typeInheritsFrom(inputTypeId, targetTypeId))
 	{
 		/*
 		 * Input class type is a subclass of target, so nothing to do ---
 		 * except relabel the type.  This is binary compatibility for
 		 * complex types.
 		 */
-		result = (Node *) makeRelabelType((Expr *) node,
-										  targetTypeId, -1,
-										  cformat);
+		return (Node *) makeRelabelType((Expr *) node,
+										targetTypeId, -1,
+										cformat);
 	}
-	else
-	{
-		/* If we get here, caller blew it */
-		elog(ERROR, "coerce_type: no conversion function from %s to %s",
-			 format_type_be(inputTypeId), format_type_be(targetTypeId));
-		result = NULL;			/* keep compiler quiet */
-	}
-
-	return result;
+	/* If we get here, caller blew it */
+	elog(ERROR, "coerce_type: no conversion function from %s to %s",
+		 format_type_be(inputTypeId), format_type_be(targetTypeId));
+	return NULL;				/* keep compiler quiet */
 }
 
 
@@ -484,15 +532,19 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
  *		(AND, OR, NOT, etc).  Also check that input is not a set.
  *
  * Returns the possibly-transformed node tree.
+ *
+ * As with coerce_type, pstate may be NULL if no special unknown-Param
+ * processing is wanted.
  */
 Node *
-coerce_to_boolean(Node *node, const char *constructName)
+coerce_to_boolean(ParseState *pstate, Node *node,
+				  const char *constructName)
 {
 	Oid			inputTypeId = exprType(node);
 
 	if (inputTypeId != BOOLOID)
 	{
-		node = coerce_to_target_type(node, inputTypeId,
+		node = coerce_to_target_type(pstate, node, inputTypeId,
 									 BOOLOID, -1,
 									 COERCION_ASSIGNMENT,
 									 COERCE_IMPLICIT_CAST);
@@ -594,16 +646,20 @@ select_common_type(List *typeids, const char *context)
  * This is used following select_common_type() to coerce the individual
  * expressions to the desired type.  'context' is a phrase to use in the
  * error message if we fail to coerce.
+ *
+ * As with coerce_type, pstate may be NULL if no special unknown-Param
+ * processing is wanted.
  */
 Node *
-coerce_to_common_type(Node *node, Oid targetTypeId, const char *context)
+coerce_to_common_type(ParseState *pstate, Node *node,
+					  Oid targetTypeId, const char *context)
 {
 	Oid			inputTypeId = exprType(node);
 
 	if (inputTypeId == targetTypeId)
 		return node;			/* no work */
 	if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT))
-		node = coerce_type(node, inputTypeId, targetTypeId,
+		node = coerce_type(pstate, node, inputTypeId, targetTypeId,
 						   COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
 	else
 		elog(ERROR, "%s unable to convert to type %s",
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 10702e9a269..429a9ac8c8a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.147 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.148 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,7 +39,8 @@ static int	expr_depth_counter = 0;
 
 bool		Transform_null_equals = false;
 
-static Node *typecast_expression(Node *expr, TypeName *typename);
+static Node *typecast_expression(ParseState *pstate, Node *expr,
+								 TypeName *typename);
 static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
 static Node *transformIndirection(ParseState *pstate, Node *basenode,
 					 List *indirection);
@@ -112,17 +113,54 @@ transformExpr(ParseState *pstate, Node *expr)
 			{
 				ParamRef   *pref = (ParamRef *) expr;
 				int			paramno = pref->number;
-				Oid			paramtyp = param_type(paramno);
+				ParseState *toppstate;
 				Param	   *param;
 				List	   *fields;
 
-				if (!OidIsValid(paramtyp))
-					elog(ERROR, "Parameter '$%d' is out of range", paramno);
+				/*
+				 * Find topmost ParseState, which is where paramtype info
+				 * lives.
+				 */
+				toppstate = pstate;
+				while (toppstate->parentParseState != NULL)
+					toppstate = toppstate->parentParseState;
+
+				/* Check parameter number is in range */
+				if (paramno <= 0) /* probably can't happen? */
+					elog(ERROR, "Parameter '$%d' is out of range",
+						 paramno);
+				if (paramno > toppstate->p_numparams)
+				{
+					if (!toppstate->p_variableparams)
+						elog(ERROR, "Parameter '$%d' is out of range",
+							 paramno);
+					/* Okay to enlarge param array */
+					if (toppstate->p_paramtypes)
+						toppstate->p_paramtypes =
+							(Oid *) repalloc(toppstate->p_paramtypes,
+											 paramno * sizeof(Oid));
+					else
+						toppstate->p_paramtypes =
+							(Oid *) palloc(paramno * sizeof(Oid));
+					/* Zero out the previously-unreferenced slots */
+					MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
+						   0,
+						   (paramno - toppstate->p_numparams) * sizeof(Oid));
+					toppstate->p_numparams = paramno;
+				}
+				if (toppstate->p_variableparams)
+				{
+					/* If not seen before, initialize to UNKNOWN type */
+					if (toppstate->p_paramtypes[paramno-1] == InvalidOid)
+						toppstate->p_paramtypes[paramno-1] = UNKNOWNOID;
+				}
+
 				param = makeNode(Param);
 				param->paramkind = PARAM_NUM;
 				param->paramid = (AttrNumber) paramno;
-				param->paramtype = paramtyp;
+				param->paramtype = toppstate->p_paramtypes[paramno-1];
 				result = (Node *) param;
+
 				/* handle qualification, if any */
 				foreach(fields, pref->fields)
 				{
@@ -143,7 +181,8 @@ transformExpr(ParseState *pstate, Node *expr)
 
 				result = (Node *) make_const(val);
 				if (con->typename != NULL)
-					result = typecast_expression(result, con->typename);
+					result = typecast_expression(pstate, result,
+												 con->typename);
 				break;
 			}
 		case T_ExprFieldSelect:
@@ -170,7 +209,7 @@ transformExpr(ParseState *pstate, Node *expr)
 				TypeCast   *tc = (TypeCast *) expr;
 				Node	   *arg = transformExpr(pstate, tc->arg);
 
-				result = typecast_expression(arg, tc->typename);
+				result = typecast_expression(pstate, arg, tc->typename);
 				break;
 			}
 		case T_A_Expr:
@@ -212,7 +251,8 @@ transformExpr(ParseState *pstate, Node *expr)
 								Node	   *rexpr = transformExpr(pstate,
 																  a->rexpr);
 
-								result = (Node *) make_op(a->name,
+								result = (Node *) make_op(pstate,
+														  a->name,
 														  lexpr,
 														  rexpr);
 							}
@@ -225,8 +265,8 @@ transformExpr(ParseState *pstate, Node *expr)
 							Node	   *rexpr = transformExpr(pstate,
 															  a->rexpr);
 
-							lexpr = coerce_to_boolean(lexpr, "AND");
-							rexpr = coerce_to_boolean(rexpr, "AND");
+							lexpr = coerce_to_boolean(pstate, lexpr, "AND");
+							rexpr = coerce_to_boolean(pstate, rexpr, "AND");
 
 							result = (Node *) makeBoolExpr(AND_EXPR,
 														   makeList2(lexpr,
@@ -240,8 +280,8 @@ transformExpr(ParseState *pstate, Node *expr)
 							Node	   *rexpr = transformExpr(pstate,
 															  a->rexpr);
 
-							lexpr = coerce_to_boolean(lexpr, "OR");
-							rexpr = coerce_to_boolean(rexpr, "OR");
+							lexpr = coerce_to_boolean(pstate, lexpr, "OR");
+							rexpr = coerce_to_boolean(pstate, rexpr, "OR");
 
 							result = (Node *) makeBoolExpr(OR_EXPR,
 														   makeList2(lexpr,
@@ -253,7 +293,7 @@ transformExpr(ParseState *pstate, Node *expr)
 							Node	   *rexpr = transformExpr(pstate,
 															  a->rexpr);
 
-							rexpr = coerce_to_boolean(rexpr, "NOT");
+							rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
 
 							result = (Node *) makeBoolExpr(NOT_EXPR,
 														   makeList1(rexpr));
@@ -266,7 +306,8 @@ transformExpr(ParseState *pstate, Node *expr)
 							Node	   *rexpr = transformExpr(pstate,
 															  a->rexpr);
 
-							result = (Node *) make_op(a->name,
+							result = (Node *) make_op(pstate,
+													  a->name,
 													  lexpr,
 													  rexpr);
 							if (((OpExpr *) result)->opresulttype != BOOLOID)
@@ -284,7 +325,8 @@ transformExpr(ParseState *pstate, Node *expr)
 							Node	   *rexpr = transformExpr(pstate,
 															  a->rexpr);
 
-							result = (Node *) make_op(a->name,
+							result = (Node *) make_op(pstate,
+													  a->name,
 													  lexpr,
 													  rexpr);
 							if (((OpExpr *) result)->opresulttype != BOOLOID)
@@ -375,7 +417,7 @@ transformExpr(ParseState *pstate, Node *expr)
 					break;
 				}
 				pstate->p_hasSubLinks = true;
-				qtrees = parse_analyze(sublink->subselect, pstate);
+				qtrees = parse_sub_analyze(sublink->subselect, pstate);
 				if (length(qtrees) != 1)
 					elog(ERROR, "Bad query in subselect");
 				qtree = (Query *) lfirst(qtrees);
@@ -523,7 +565,7 @@ transformExpr(ParseState *pstate, Node *expr)
 
 					if (needNot)
 					{
-						expr = coerce_to_boolean(expr, "NOT");
+						expr = coerce_to_boolean(pstate, expr, "NOT");
 						expr = (Node *) makeBoolExpr(NOT_EXPR,
 													 makeList1(expr));
 					}
@@ -561,7 +603,8 @@ transformExpr(ParseState *pstate, Node *expr)
 					}
 					neww->expr = (Expr *) transformExpr(pstate, warg);
 
-					neww->expr = (Expr *) coerce_to_boolean((Node *) neww->expr,
+					neww->expr = (Expr *) coerce_to_boolean(pstate,
+															(Node *) neww->expr,
 															"CASE/WHEN");
 
 					/*
@@ -615,7 +658,8 @@ transformExpr(ParseState *pstate, Node *expr)
 
 				/* Convert default result clause, if necessary */
 				newc->defresult = (Expr *)
-					coerce_to_common_type((Node *) newc->defresult,
+					coerce_to_common_type(pstate,
+										  (Node *) newc->defresult,
 										  ptype,
 										  "CASE/ELSE");
 
@@ -625,7 +669,8 @@ transformExpr(ParseState *pstate, Node *expr)
 					CaseWhen   *w = (CaseWhen *) lfirst(args);
 
 					w->result = (Expr *)
-						coerce_to_common_type((Node *) w->result,
+						coerce_to_common_type(pstate,
+											  (Node *) w->result,
 											  ptype,
 											  "CASE/WHEN");
 				}
@@ -666,7 +711,9 @@ transformExpr(ParseState *pstate, Node *expr)
 					Node *e = (Node *) lfirst(element);
 					Node *newe;
 
-					newe = coerce_to_common_type(e, element_type, "ARRAY");
+					newe = coerce_to_common_type(pstate, e,
+												 element_type,
+												 "ARRAY");
 					newcoercedelems = lappend(newcoercedelems, newe);
 				}
 
@@ -753,7 +800,8 @@ transformExpr(ParseState *pstate, Node *expr)
 					Node *e = (Node *) lfirst(args);
 					Node *newe;
 
-					newe = coerce_to_common_type(e, newc->coalescetype,
+					newe = coerce_to_common_type(pstate, e,
+												 newc->coalescetype,
 												 "COALESCE");
 					newcoercedargs = lappend(newcoercedargs, newe);
 				}
@@ -806,7 +854,9 @@ transformExpr(ParseState *pstate, Node *expr)
 
 				b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
 
-				b->arg = (Expr *) coerce_to_boolean((Node *) b->arg, clausename);
+				b->arg = (Expr *) coerce_to_boolean(pstate,
+													(Node *) b->arg,
+													clausename);
 
 				result = expr;
 				break;
@@ -1404,7 +1454,7 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
  * the type name and then apply any necessary coercion function(s).
  */
 static Node *
-typecast_expression(Node *expr, TypeName *typename)
+typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
 {
 	Oid			inputType = exprType(expr);
 	Oid			targetType;
@@ -1414,7 +1464,7 @@ typecast_expression(Node *expr, TypeName *typename)
 	if (inputType == InvalidOid)
 		return expr;			/* do nothing if NULL input */
 
-	expr = coerce_to_target_type(expr, inputType,
+	expr = coerce_to_target_type(pstate, expr, inputType,
 								 targetType, typename->typmod,
 								 COERCION_EXPLICIT,
 								 COERCE_EXPLICIT_CAST);
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 7cbef965369..058b9aad73d 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.146 2003/04/24 21:16:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.147 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -253,7 +253,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		 * We can do it as a trivial coercion. coerce_type can handle
 		 * these cases, so why duplicate code...
 		 */
-		return coerce_type(lfirst(fargs), actual_arg_types[0], rettype,
+		return coerce_type(pstate, lfirst(fargs), actual_arg_types[0],
+						   rettype,
 						   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL);
 	}
 	else if (fdresult == FUNCDETAIL_NORMAL)
@@ -316,7 +317,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 											   rettype);
 
 	/* perform the necessary typecasting of arguments */
-	make_fn_arguments(fargs, actual_arg_types, declared_arg_types);
+	make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
 
 	/* build the appropriate output structure */
 	if (fdresult == FUNCDETAIL_NORMAL)
@@ -1145,9 +1146,13 @@ typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
  * allowed.
  *
  * Caution: given argument list is modified in-place.
+ *
+ * As with coerce_type, pstate may be NULL if no special unknown-Param
+ * processing is wanted.
  */
 void
-make_fn_arguments(List *fargs,
+make_fn_arguments(ParseState *pstate,
+				  List *fargs,
 				  Oid *actual_arg_types,
 				  Oid *declared_arg_types)
 {
@@ -1159,7 +1164,8 @@ make_fn_arguments(List *fargs,
 		/* types don't match? then force coercion using a function call... */
 		if (actual_arg_types[i] != declared_arg_types[i])
 		{
-			lfirst(current_fargs) = coerce_type(lfirst(current_fargs),
+			lfirst(current_fargs) = coerce_type(pstate,
+												lfirst(current_fargs),
 												actual_arg_types[i],
 												declared_arg_types[i],
 												COERCION_IMPLICIT,
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 700c4b158de..2f5775212cb 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.77 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.78 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,7 +39,12 @@ make_parsestate(ParseState *parentParseState)
 	pstate = palloc0(sizeof(ParseState));
 
 	pstate->parentParseState = parentParseState;
-	pstate->p_last_resno = 1;
+
+	/* Fill in fields that don't start at null/false/zero */
+	pstate->p_next_resno = 1;
+
+	if (parentParseState)
+		pstate->p_variableparams = parentParseState->p_variableparams;
 
 	return pstate;
 }
@@ -166,7 +171,8 @@ transformArraySubscripts(ParseState *pstate,
 			{
 				subexpr = transformExpr(pstate, ai->lidx);
 				/* If it's not int4 already, try to coerce */
-				subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
+				subexpr = coerce_to_target_type(pstate,
+												subexpr, exprType(subexpr),
 												INT4OID, -1,
 												COERCION_ASSIGNMENT,
 												COERCE_IMPLICIT_CAST);
@@ -186,7 +192,8 @@ transformArraySubscripts(ParseState *pstate,
 		}
 		subexpr = transformExpr(pstate, ai->uidx);
 		/* If it's not int4 already, try to coerce */
-		subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
+		subexpr = coerce_to_target_type(pstate,
+										subexpr, exprType(subexpr),
 										INT4OID, -1,
 										COERCION_ASSIGNMENT,
 										COERCE_IMPLICIT_CAST);
@@ -205,7 +212,8 @@ transformArraySubscripts(ParseState *pstate,
 
 		if (typesource != InvalidOid)
 		{
-			assignFrom = coerce_to_target_type(assignFrom, typesource,
+			assignFrom = coerce_to_target_type(pstate,
+											   assignFrom, typesource,
 											   typeneeded, arrayTypMod,
 											   COERCION_ASSIGNMENT,
 											   COERCE_IMPLICIT_CAST);
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 3be29e64def..6238258ed2f 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.62 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.63 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1019,9 +1019,12 @@ unary_op_error(List *op, Oid arg, bool is_left_op)
  *
  * Transform operator expression ensuring type compatibility.
  * This is where some type conversion happens.
+ *
+ * As with coerce_type, pstate may be NULL if no special unknown-Param
+ * processing is wanted.
  */
 Expr *
-make_op(List *opname, Node *ltree, Node *rtree)
+make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
 {
 	Oid			ltypeId,
 				rtypeId;
@@ -1052,7 +1055,7 @@ make_op(List *opname, Node *ltree, Node *rtree)
 	}
 
 	/* Do typecasting and build the expression tree */
-	result = make_op_expr(tup, ltree, rtree, ltypeId, rtypeId);
+	result = make_op_expr(pstate, tup, ltree, rtree, ltypeId, rtypeId);
 
 	ReleaseSysCache(tup);
 
@@ -1063,9 +1066,13 @@ make_op(List *opname, Node *ltree, Node *rtree)
 /*
  * make_op_expr()
  *		Build operator expression using an already-looked-up operator.
+ *
+ * As with coerce_type, pstate may be NULL if no special unknown-Param
+ * processing is wanted.
  */
 Expr *
-make_op_expr(Operator op, Node *ltree, Node *rtree,
+make_op_expr(ParseState *pstate, Operator op,
+			 Node *ltree, Node *rtree,
 			 Oid ltypeId, Oid rtypeId)
 {
 	Form_pg_operator opform = (Form_pg_operator) GETSTRUCT(op);
@@ -1114,7 +1121,7 @@ make_op_expr(Operator op, Node *ltree, Node *rtree,
 											   opform->oprresult);
 
 	/* perform the necessary typecasting of arguments */
-	make_fn_arguments(args, actual_arg_types, declared_arg_types);
+	make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
 
 	/* and build the expression node */
 	result = makeNode(OpExpr);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 4ae3bf23153..41fd98fc071 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.80 2002/12/12 15:49:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.81 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1447,7 +1447,7 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
 		Node	   *varnode = (Node *) lfirst(vars);
 		TargetEntry *te = makeNode(TargetEntry);
 
-		te->resdom = makeResdom((AttrNumber) (pstate->p_last_resno)++,
+		te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
 								exprType(varnode),
 								exprTypmod(varnode),
 								label,
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index e1be47b3ec5..dc8f241d45e 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.99 2003/04/08 23:20:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.100 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,7 +73,7 @@ transformTargetEntry(ParseState *pstate,
 		colname = FigureColname(node);
 	}
 
-	resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
+	resnode = makeResdom((AttrNumber) pstate->p_next_resno++,
 						 type_id,
 						 type_mod,
 						 colname,
@@ -290,7 +290,8 @@ updateTargetListEntry(ParseState *pstate,
 		if (type_id != InvalidOid)
 		{
 			tle->expr = (Expr *)
-				coerce_to_target_type((Node *) tle->expr, type_id,
+				coerce_to_target_type(pstate,
+									  (Node *) tle->expr, type_id,
 									  attrtype, attrtypmod,
 									  COERCION_ASSIGNMENT,
 									  COERCE_IMPLICIT_CAST);
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 85aeafacc59..d1a324dbf0a 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.56 2003/04/27 20:09:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.57 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -437,7 +437,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
 	initStringInfo(&buf);
 	appendStringInfo(&buf, "SELECT (NULL::%s)", str);
 
-	raw_parsetree_list = parser(buf.data, NULL, 0);
+	raw_parsetree_list = raw_parser(buf.data);
 
 	/*
 	 * Make sure we got back exactly what we expected and no more;
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index 16745f7b370..37436d30079 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.56 2003/04/27 20:09:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.57 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,32 +30,27 @@
 
 List	   *parsetree;			/* result of parsing is left here */
 
-static Oid *param_type_info;	/* state for param_type() */
-static int	param_count;
-
 static int	lookahead_token;	/* one-token lookahead */
 static bool have_lookahead;		/* lookahead_token set? */
 
 
 /*
- * parser
- *		Given a query in string form, and optionally info about
- *		parameter types, do lexical and syntactic analysis.
+ * raw_parser
+ *		Given a query in string form, do lexical and grammatical analysis.
  *
  * Returns a list of raw (un-analyzed) parse trees.
  */
 List *
-parser(const char *str, Oid *typev, int nargs)
+raw_parser(const char *str)
 {
 	int			yyresult;
 
-	parsetree = NIL;			/* in case parser forgets to set it */
+	parsetree = NIL;			/* in case grammar forgets to set it */
 	have_lookahead = false;
 
 	scanner_init(str);
 	parser_init();
 	parse_expr_init();
-	parser_param_set(typev, nargs);
 
 	yyresult = yyparse();
 
@@ -69,35 +64,6 @@ parser(const char *str, Oid *typev, int nargs)
 }
 
 
-/*
- * Save information needed to fill out the type of Param references ($n)
- *
- * This is used for SQL functions, PREPARE statements, etc.  It's split
- * out from parser() setup because PREPARE needs to change the info after
- * the grammar runs and before parse analysis is done on the preparable
- * query.
- */
-void
-parser_param_set(Oid *typev, int nargs)
-{
-	param_type_info = typev;
-	param_count = nargs;
-}
-
-/*
- * param_type()
- *
- * Fetch a parameter type previously passed to parser_param_set
- */
-Oid
-param_type(int t)
-{
-	if (t > param_count || t <= 0)
-		return InvalidOid;
-	return param_type_info[t - 1];
-}
-
-
 /*
  * Intermediate filter between parser and base lexer (base_yylex in scan.l).
  *
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index e39ee0efbe7..8ca7fb954f0 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.118 2003/02/25 23:47:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.119 2003/04/29 22:13:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -498,7 +498,8 @@ build_column_default(Relation rel, int attrno)
 	 */
 	exprtype = exprType(expr);
 
-	expr = coerce_to_target_type(expr, exprtype,
+	expr = coerce_to_target_type(NULL, /* no UNKNOWN params here */
+								 expr, exprtype,
 								 atttype, atttypmod,
 								 COERCION_ASSIGNMENT,
 								 COERCE_IMPLICIT_CAST);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index dca6455b0d0..bc98d1d91ec 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.325 2003/04/27 20:09:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.326 2003/04/29 22:13:11 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -339,8 +339,8 @@ ReadCommand(StringInfo inBuf)
  */
 List *
 pg_parse_and_rewrite(const char *query_string, /* string to execute */
-					 Oid *typev,	/* parameter types */
-					 int nargs) /* number of parameters */
+					 Oid *paramTypes,	/* parameter types */
+					 int numParams) /* number of parameters */
 {
 	List	   *raw_parsetree_list;
 	List	   *querytree_list;
@@ -349,7 +349,7 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */
 	/*
 	 * (1) parse the request string into a list of raw parse trees.
 	 */
-	raw_parsetree_list = pg_parse_query(query_string, typev, nargs);
+	raw_parsetree_list = pg_parse_query(query_string);
 
 	/*
 	 * (2) Do parse analysis and rule rewrite.
@@ -360,7 +360,9 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */
 		Node	   *parsetree = (Node *) lfirst(list_item);
 
 		querytree_list = nconc(querytree_list,
-							   pg_analyze_and_rewrite(parsetree));
+							   pg_analyze_and_rewrite(parsetree,
+													  paramTypes,
+													  numParams));
 	}
 
 	return querytree_list;
@@ -380,7 +382,7 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */
  * commands are not processed any further than the raw parse stage.
  */
 List *
-pg_parse_query(const char *query_string, Oid *typev, int nargs)
+pg_parse_query(const char *query_string)
 {
 	List	   *raw_parsetree_list;
 
@@ -390,7 +392,7 @@ pg_parse_query(const char *query_string, Oid *typev, int nargs)
 	if (log_parser_stats)
 		ResetUsage();
 
-	raw_parsetree_list = parser(query_string, typev, nargs);
+	raw_parsetree_list = raw_parser(query_string);
 
 	if (log_parser_stats)
 		ShowUsage("PARSER STATISTICS");
@@ -399,8 +401,8 @@ pg_parse_query(const char *query_string, Oid *typev, int nargs)
 }
 
 /*
- * Given a raw parsetree (gram.y output), perform parse analysis and
- * rule rewriting.
+ * Given a raw parsetree (gram.y output), and optionally information about
+ * types of parameter symbols ($n), perform parse analysis and rule rewriting.
  *
  * A list of Query nodes is returned, since either the analyzer or the
  * rewriter might expand one query to several.
@@ -408,7 +410,7 @@ pg_parse_query(const char *query_string, Oid *typev, int nargs)
  * NOTE: for reasons mentioned above, this must be separate from raw parsing.
  */
 List *
-pg_analyze_and_rewrite(Node *parsetree)
+pg_analyze_and_rewrite(Node *parsetree, Oid *paramTypes, int numParams)
 {
 	List	   *querytree_list;
 	List	   *list_item;
@@ -421,7 +423,7 @@ pg_analyze_and_rewrite(Node *parsetree)
 	if (log_parser_stats)
 		ResetUsage();
 
-	querytree_list = parse_analyze(parsetree, NULL);
+	querytree_list = parse_analyze(parsetree, paramTypes, numParams);
 
 	if (log_parser_stats)
 	{
@@ -562,8 +564,7 @@ pg_plan_query(Query *querytree)
  *
  * ----------------------------------------------------------------
  */
-
-void
+static void
 pg_exec_query_string(const char *query_string,	/* string to execute */
 					 CommandDest dest,	/* where results should go */
 					 MemoryContext parse_context)		/* context for
@@ -614,7 +615,7 @@ pg_exec_query_string(const char *query_string,	/* string to execute */
 	 * Do basic parsing of the query or queries (this should be safe even
 	 * if we are in aborted transaction state!)
 	 */
-	parsetree_list = pg_parse_query(query_string, NULL, 0);
+	parsetree_list = pg_parse_query(query_string);
 
 	/*
 	 * Switch back to execution context to enter the loop.
@@ -710,7 +711,7 @@ pg_exec_query_string(const char *query_string,	/* string to execute */
 		 */
 		oldcontext = MemoryContextSwitchTo(parse_context);
 
-		querytree_list = pg_analyze_and_rewrite(parsetree);
+		querytree_list = pg_analyze_and_rewrite(parsetree, NULL, 0);
 
 		/*
 		 * Switch back to execution context for planning and execution.
@@ -1826,7 +1827,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.325 $ $Date: 2003/04/27 20:09:44 $\n");
+		puts("$Revision: 1.326 $ $Date: 2003/04/29 22:13:11 $\n");
 	}
 
 	/*
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index f780ec4d294..b7d6a7d6658 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: analyze.h,v 1.20 2002/06/20 20:29:51 momjian Exp $
+ * $Id: analyze.h,v 1.21 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,11 @@
 
 #include "parser/parse_node.h"
 
-extern List *parse_analyze(Node *parseTree, ParseState *parentParseState);
+
+extern List *parse_analyze(Node *parseTree, Oid *paramTypes, int numParams);
+extern List *parse_analyze_varparams(Node *parseTree, Oid **paramTypes,
+									 int *numParams);
+extern List *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
 extern List *analyzeCreateSchemaStmt(CreateSchemaStmt *stmt);
 
 extern void CheckSelectForUpdate(Query *qry);
diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h
index 97108f2b1c9..f9a03959116 100644
--- a/src/include/parser/gramparse.h
+++ b/src/include/parser/gramparse.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: gramparse.h,v 1.26 2003/04/27 20:09:44 tgl Exp $
+ * $Id: gramparse.h,v 1.27 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,9 +17,8 @@
 
 #include "nodes/parsenodes.h"
 
+
 /* from parser.c */
-extern void parser_param_set(Oid *typev, int nargs);
-extern Oid	param_type(int t);
 extern int	yylex(void);
 
 /* from scan.l */
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index b21a83601bf..de2ea95db85 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_coerce.h,v 1.50 2003/04/08 23:20:04 tgl Exp $
+ * $Id: parse_coerce.h,v 1.51 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "catalog/pg_type.h"
 #include "parser/parse_node.h"
 
+
 typedef enum CATEGORY
 {
 	INVALID_TYPE,
@@ -38,22 +39,26 @@ extern bool IsBinaryCoercible(Oid srctype, Oid targettype);
 extern bool IsPreferredType(CATEGORY category, Oid type);
 extern CATEGORY TypeCategory(Oid type);
 
-extern Node *coerce_to_target_type(Node *expr, Oid exprtype,
+extern Node *coerce_to_target_type(ParseState *pstate,
+								   Node *expr, Oid exprtype,
 								   Oid targettype, int32 targettypmod,
 								   CoercionContext ccontext,
 								   CoercionForm cformat);
 extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
 							CoercionContext ccontext);
-extern Node *coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId,
+extern Node *coerce_type(ParseState *pstate, Node *node,
+						 Oid inputTypeId, Oid targetTypeId,
 						 CoercionContext ccontext, CoercionForm cformat);
 extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
 							  CoercionForm cformat);
 
-extern Node *coerce_to_boolean(Node *node, const char *constructName);
+extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
+							   const char *constructName);
 
 extern Oid	select_common_type(List *typeids, const char *context);
-extern Node *coerce_to_common_type(Node *node, Oid targetTypeId,
-					  const char *context);
+extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
+								   Oid targetTypeId,
+								   const char *context);
 
 extern bool check_generic_type_consistency(Oid *actual_arg_types,
 										   Oid *declared_arg_types,
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 3bd369e3c70..2b1a1fad4b9 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.44 2003/04/08 23:20:04 tgl Exp $
+ * $Id: parse_func.h,v 1.45 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 #include "parser/parse_node.h"
 
+
 /*
  *	This structure is used to explore the inheritance hierarchy above
  *	nodes in the type tree in order to disambiguate among polymorphic
@@ -49,7 +50,8 @@ extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
 
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
 
-extern void make_fn_arguments(List *fargs,
+extern void make_fn_arguments(ParseState *pstate,
+							  List *fargs,
 							  Oid *actual_arg_types,
 							  Oid *declared_arg_types);
 
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 12dcaccbefd..1f52963e5c0 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -1,12 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * parse_node.h
+ *		Internal definitions for parser
  *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_node.h,v 1.34 2003/04/08 23:20:04 tgl Exp $
+ * $Id: parse_node.h,v 1.35 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +33,15 @@
  * joinlist.  Note that an RTE that is present in p_namespace, but does not
  * have its inFromCl flag set, is accessible only with an explicit qualifier;
  * lookups of unqualified column names should ignore it.
+ *
+ * p_paramtypes: an array of p_numparams type OIDs for $n parameter symbols
+ * (zeroth entry in array corresponds to $1).  If p_variableparams is true, the
+ * set of param types is not predetermined; in that case, a zero array entry
+ * means that parameter number hasn't been seen, and UNKNOWNOID means the
+ * parameter has been used but its type is not yet known.  NOTE: in a stack
+ * of ParseStates, only the topmost ParseState contains paramtype info; but
+ * we copy the p_variableparams flag down to the child nodes for speed in
+ * coerce_type.
  */
 typedef struct ParseState
 {
@@ -40,9 +50,12 @@ typedef struct ParseState
 	List	   *p_joinlist;		/* join items so far (will become FromExpr
 								 * node's fromlist) */
 	List	   *p_namespace;	/* current lookup namespace (join items) */
-	int			p_last_resno;	/* last targetlist resno assigned */
+	Oid		   *p_paramtypes;	/* OIDs of types for $n parameter symbols */
+	int			p_numparams;	/* allocated size of p_paramtypes[] */
+	int			p_next_resno;	/* next targetlist resno to assign */
 	List	   *p_forUpdate;	/* FOR UPDATE clause, if any (see gram.y) */
 	Node	   *p_value_substitute;	/* what to replace VALUE with, if any */
+	bool		p_variableparams;
 	bool		p_hasAggs;
 	bool		p_hasSubLinks;
 	bool		p_is_insert;
diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h
index 2cde7189dda..6d2268bf557 100644
--- a/src/include/parser/parse_oper.h
+++ b/src/include/parser/parse_oper.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_oper.h,v 1.24 2003/04/08 23:20:04 tgl Exp $
+ * $Id: parse_oper.h,v 1.25 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,8 @@
 #define PARSE_OPER_H
 
 #include "access/htup.h"
-#include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
+
 
 typedef HeapTuple Operator;
 
@@ -50,8 +51,10 @@ extern Oid	oprid(Operator op);
 extern Oid	oprfuncid(Operator op);
 
 /* Build expression tree for an operator invocation */
-extern Expr *make_op(List *opname, Node *ltree, Node *rtree);
-extern Expr *make_op_expr(Operator op, Node *ltree, Node *rtree,
+extern Expr *make_op(ParseState *pstate, List *opname,
+					 Node *ltree, Node *rtree);
+extern Expr *make_op_expr(ParseState *pstate, Operator op,
+						  Node *ltree, Node *rtree,
 						  Oid ltypeId, Oid rtypeId);
 
 #endif   /* PARSE_OPER_H */
diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h
index 42d82869f08..e5e5bddfafc 100644
--- a/src/include/parser/parser.h
+++ b/src/include/parser/parser.h
@@ -1,21 +1,19 @@
 /*-------------------------------------------------------------------------
  *
  * parser.h
- *
+ *		Definitions for the "raw" parser (lex and yacc phases only)
  *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parser.h,v 1.14 2003/04/27 20:09:44 tgl Exp $
+ * $Id: parser.h,v 1.15 2003/04/29 22:13:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PARSER_H
 #define PARSER_H
 
-#include "parser/parse_node.h"
-
-extern List *parser(const char *str, Oid *typev, int nargs);
+extern List *raw_parser(const char *str);
 
 #endif   /* PARSER_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 4235d6d257f..b81df05320c 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tcopprot.h,v 1.54 2003/04/27 20:09:44 tgl Exp $
+ * $Id: tcopprot.h,v 1.55 2003/04/29 22:13:11 tgl Exp $
  *
  * OLD COMMENTS
  *	  This file was created so that other c files could get the two
@@ -35,14 +35,12 @@ extern DLLIMPORT const char *debug_query_string;
 
 #ifndef BOOTSTRAP_INCLUDE
 
-extern List *pg_parse_query(const char *query_string, Oid *typev, int nargs);
-extern List *pg_analyze_and_rewrite(Node *parsetree);
+extern List *pg_parse_query(const char *query_string);
+extern List *pg_analyze_and_rewrite(Node *parsetree,
+									Oid *paramTypes, int numParams);
 extern List *pg_parse_and_rewrite(const char *query_string,
-					 Oid *typev, int nargs);
+					 Oid *paramTypes, int numParams);
 extern Plan *pg_plan_query(Query *querytree);
-extern void pg_exec_query_string(const char *query_string,
-					 CommandDest dest,
-					 MemoryContext parse_context);
 
 #endif   /* BOOTSTRAP_INCLUDE */
 
-- 
GitLab