diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index cfd8b8dc0b9f7027dab67ba7ca136e1bf5d15003..624b5df96f981e9a788773ce982fcec7e1645ccc 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.115 2005/11/04 23:13:59 petere Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.116 2006/01/08 07:00:24 neilc Exp $
  -->
 
 <chapter id="catalogs">
@@ -4372,6 +4372,11 @@
       <entry>currently held locks</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-prepared-statements"><structname>pg_prepared_statements</structname></link></entry>
+      <entry>current prepared statements</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-prepared-xacts"><structname>pg_prepared_xacts</structname></link></entry>
       <entry>currently prepared transactions</entry>
@@ -4778,6 +4783,101 @@
 
  </sect1>
 
+ <sect1 id="view-pg-prepared-statements">
+  <title><structname>pg_prepared_statements</structname></title>
+
+  <indexterm zone="view-pg-prepared-statements">
+   <primary>pg_prepared_statements</primary>
+  </indexterm>
+
+  <para>
+   The <structname>pg_prepared_statements</structname> view displays
+   all the prepared statements that are available in the current
+   session. See <xref linkend="sql-prepare"
+   endterm="sql-prepare-title"> for more information about prepared
+   statements.
+  </para>
+
+  <para>
+   <structname>pg_prepared_statements</structname> contains one row
+   for each prepared statement. Rows are added to the view when a new
+   prepared statement is created, and removed when a prepared
+   statement is released (for example, via the <xref
+   linkend="sql-deallocate" endterm="sql-deallocate-title">
+   command).
+  </para>
+
+  <table>
+   <title><structname>pg_prepared_statements</> Columns</title>
+
+   <tgroup cols=4>
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+    <tbody>
+     <row>
+      <entry><structfield>name</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>
+       The identifier of the prepared statement.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>statement</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>
+       The query string submitted by the client to create this
+       prepared statement. For prepared statements created via SQL,
+       this is the <command>PREPARE</command> statement submitted by
+       the client. For prepared statements created via the
+       frontend/backend protocol, this is the text of the prepared
+       statement itself.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>prepare_time</structfield></entry>
+      <entry><type>timestamptz</type></entry>
+      <entry></entry>
+      <entry>
+       The time at which the prepared statement was created.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>parameter_types</structfield></entry>
+      <entry><type>oid[]</type></entry>
+      <entry></entry>
+      <entry>
+       The expected parameter types for the prepared statement in the form of
+       an array of type OIDs.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>from_sql</structfield></entry>
+      <entry><type>boolean</type></entry>
+      <entry></entry>
+      <entry>
+       <literal>true</literal> if the prepared statement was created
+       via the <command>PREPARE</command> SQL statement;
+       <literal>false</literal> if the statement was prepared via the
+       frontend/backend protocol.
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_prepared_statements</structname> view is read only.
+  </para>
+ </sect1>
+
  <sect1 id="view-pg-prepared-xacts">
   <title><structname>pg_prepared_xacts</structname></title>
 
diff --git a/doc/src/sgml/ref/prepare.sgml b/doc/src/sgml/ref/prepare.sgml
index d0abc49b5ce302b0e6d79bcd8a66863748b103ce..51c39851173c93233e09461c383143e4ca3e477c 100644
--- a/doc/src/sgml/ref/prepare.sgml
+++ b/doc/src/sgml/ref/prepare.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/prepare.sgml,v 1.16 2005/10/15 01:47:12 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/prepare.sgml,v 1.17 2006/01/08 07:00:25 neilc Exp $
 PostgreSQL documentation
 -->
 
@@ -145,6 +145,11 @@ PREPARE <replaceable class="PARAMETER">plan_name</replaceable> [ (<replaceable c
    the <xref linkend="sql-analyze" endterm="sql-analyze-title">
    documentation.
   </para>
+
+  <para>
+   You can see all available prepared statements of a session by querying the
+   <structname>pg_prepared_statements</> system view.
+  </para>
  </refsect1>
 
  <refsect1 id="sql-prepare-examples">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 4ffb8ac2c5d7ab9583ac1a6e328c1209f2eecf08..a186c33b4447b3d1bf79cdf5104691c904c48d0e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.22 2005/10/06 02:29:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.23 2006/01/08 07:00:25 neilc Exp $
  */
 
 CREATE VIEW pg_roles AS 
@@ -156,6 +156,12 @@ CREATE VIEW pg_prepared_xacts AS
          LEFT JOIN pg_authid U ON P.ownerid = U.oid
          LEFT JOIN pg_database D ON P.dbid = D.oid;
 
+CREATE VIEW pg_prepared_statements AS
+    SELECT P.name, P.statement, P.prepare_time, P.parameter_types, P.from_sql
+    FROM pg_prepared_statement() AS P
+    (name text, statement text, prepare_time timestamptz,
+     parameter_types oid[], from_sql boolean);
+
 CREATE VIEW pg_settings AS 
     SELECT * 
     FROM pg_show_all_settings() AS A 
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 1ee955ebe14e8d3d6575aad5f5fb62b9e69ba1f8..ccf8c297b21fc204ee4dd5e1948adf12476c020b 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -10,21 +10,26 @@
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.44 2005/12/14 17:06:27 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.45 2006/01/08 07:00:25 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "access/heapam.h"
+#include "catalog/pg_type.h"
 #include "commands/explain.h"
 #include "commands/prepare.h"
 #include "executor/executor.h"
-#include "utils/guc.h"
+#include "funcapi.h"
+#include "parser/parsetree.h"
 #include "optimizer/planner.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 
@@ -40,6 +45,7 @@ static HTAB *prepared_queries = NULL;
 static void InitQueryHashTable(void);
 static ParamListInfo EvaluateParams(EState *estate,
 			   List *params, List *argtypes);
+static Datum build_oid_array(List *oid_list);
 
 /*
  * Implements the 'PREPARE' utility statement.
@@ -114,7 +120,8 @@ PrepareQuery(PrepareStmt *stmt)
 						   commandTag,
 						   query_list,
 						   plan_list,
-						   stmt->argtype_oids);
+						   stmt->argtype_oids,
+						   true);
 }
 
 /*
@@ -298,7 +305,8 @@ StorePreparedStatement(const char *stmt_name,
 					   const char *commandTag,
 					   List *query_list,
 					   List *plan_list,
-					   List *argtype_list)
+					   List *argtype_list,
+					   bool from_sql)
 {
 	PreparedStatement *entry;
 	MemoryContext oldcxt,
@@ -361,6 +369,8 @@ StorePreparedStatement(const char *stmt_name,
 	entry->plan_list = plan_list;
 	entry->argtype_list = argtype_list;
 	entry->context = entrycxt;
+	entry->prepare_time = GetCurrentTimestamp();
+	entry->from_sql = from_sql;
 
 	MemoryContextSwitchTo(oldcxt);
 }
@@ -383,7 +393,7 @@ FetchPreparedStatement(const char *stmt_name, bool throwError)
 	{
 		/*
 		 * We can't just use the statement name as supplied by the user: the
-		 * hash package is picky enough that it needs to be NULL-padded out to
+		 * hash package is picky enough that it needs to be NUL-padded out to
 		 * the appropriate length to work correctly.
 		 */
 		StrNCpy(key, stmt_name, sizeof(key));
@@ -661,3 +671,125 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
 	if (estate)
 		FreeExecutorState(estate);
 }
+
+/*
+ * This set returning function reads all the prepared statements and
+ * returns a set of (name, statement, prepare_time, param_types).
+ */
+Datum
+pg_prepared_statement(PG_FUNCTION_ARGS)
+{
+	FuncCallContext	   *funcctx;
+	HASH_SEQ_STATUS    *hash_seq;
+	PreparedStatement  *prep_stmt;
+
+	/* stuff done only on the first call of the function */
+	if (SRF_IS_FIRSTCALL())
+	{
+		TupleDesc		tupdesc;
+		MemoryContext	oldcontext;
+
+		/* create a function context for cross-call persistence */
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		/*
+		 * switch to memory context appropriate for multiple function
+		 * calls
+		 */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		/* allocate memory for user context */
+		if (prepared_queries)
+		{
+			hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS));
+			hash_seq_init(hash_seq, prepared_queries);
+			funcctx->user_fctx = (void *) hash_seq;
+		}
+		else
+			funcctx->user_fctx = NULL;
+
+		/*
+		 * build tupdesc for result tuples. This must match the
+		 * definition of the pg_prepared_statements view in
+		 * system_views.sql
+		 */
+		tupdesc = CreateTemplateTupleDesc(5, false);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
+						   TEXTOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
+						   TIMESTAMPTZOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
+						   OIDARRAYOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
+						   BOOLOID, -1, 0);
+
+		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+	hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx;
+
+	/* if the hash table is uninitialized, we're done */
+	if (hash_seq == NULL)
+		SRF_RETURN_DONE(funcctx);
+
+	prep_stmt = hash_seq_search(hash_seq);
+	if (prep_stmt)
+	{
+		Datum			result;
+		HeapTuple		tuple;
+		Datum			values[5];
+		bool			nulls[5];
+
+		MemSet(nulls, 0, sizeof(nulls));
+
+		values[0] = DirectFunctionCall1(textin,
+										CStringGetDatum(prep_stmt->stmt_name));
+
+		if (prep_stmt->query_string == NULL)
+			nulls[1] = true;
+		else
+			values[1] = DirectFunctionCall1(textin,
+									CStringGetDatum(prep_stmt->query_string));
+
+		values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
+		values[3] = build_oid_array(prep_stmt->argtype_list);
+		values[4] = BoolGetDatum(prep_stmt->from_sql);
+
+		tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+		result = HeapTupleGetDatum(tuple);
+		SRF_RETURN_NEXT(funcctx, result);
+	}
+
+	SRF_RETURN_DONE(funcctx);
+}
+
+/*
+ * This utility function takes a List of Oids, and returns a Datum
+ * pointing to a Postgres array containing those OIDs. The empty list
+ * is returned as a zero-element array, not NULL.
+ */
+static Datum
+build_oid_array(List *oid_list)
+{
+	ListCell *lc;
+	int len;
+	int i;
+	Datum *tmp_ary;
+	ArrayType *ary;
+
+	len = list_length(oid_list);
+	tmp_ary = (Datum *) palloc(len * sizeof(Datum));
+
+	i = 0;
+	foreach(lc, oid_list)
+		tmp_ary[i++] = ObjectIdGetDatum(lfirst_oid(lc));
+
+	/* XXX: this hardcodes assumptions about the OID type... */
+	ary = construct_array(tmp_ary, len, OIDOID, sizeof(Oid), true, 'i');
+	return PointerGetDatum(ary);
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e38e66a38cb6203475e2f4655ae4410c08240b95..0fe8ee057d5655c33f6593483c2f0b5760497476 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.477 2006/01/05 10:07:45 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.478 2006/01/08 07:00:25 neilc Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -55,6 +55,7 @@
 #include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
+#include "utils/builtins.h"
 #include "utils/flatfiles.h"
 #include "utils/guc.h"
 #include "utils/lsyscache.h"
@@ -1308,7 +1309,8 @@ exec_parse_message(const char *query_string,	/* string to execute */
 							   commandTag,
 							   querytree_list,
 							   plantree_list,
-							   param_list);
+							   param_list,
+							   false);
 	}
 	else
 	{
@@ -1322,6 +1324,7 @@ exec_parse_message(const char *query_string,	/* string to execute */
 		pstmt->query_list = querytree_list;
 		pstmt->plan_list = plantree_list;
 		pstmt->argtype_list = param_list;
+		pstmt->from_sql = false;
 		pstmt->context = unnamed_stmt_context;
 		/* Now the unnamed statement is complete and valid */
 		unnamed_stmt_pstmt = pstmt;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index a7bad3dfd932b5f78abf41f5dfabe28005c78528..6eb5f72d9fa6487f73060767d9a003be5ea92795 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.308 2005/12/28 01:30:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.309 2006/01/08 07:00:25 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200512271
+#define CATALOG_VERSION_NO	200601081
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 824f7a3fba2fa5abe7a1801cf6f635f2c0acebe3..7b78e78fcc39bcd4930034904c0ac68df48b994a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.389 2005/11/17 22:14:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.390 2006/01/08 07:00:25 neilc Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -3617,6 +3617,8 @@ DATA(insert OID = 2508 (  pg_get_constraintdef PGNSP PGUID 12 f f t f s 2 25 "26
 DESCR("constraint description with pretty-print option");
 DATA(insert OID = 2509 (  pg_get_expr		   PGNSP PGUID 12 f f t f s 3 25 "25 26 16" _null_ _null_ _null_ pg_get_expr_ext - _null_ ));
 DESCR("deparse an encoded expression with pretty-print option");
+DATA(insert OID = 2510 (  pg_prepared_statement PGNSP PGUID 12 f f t t s 0 2249 "" _null_ _null_ _null_ pg_prepared_statement - _null_ ));
+DESCR("get the prepared statements for this session");
 
 /* non-persistent series generator */
 DATA(insert OID = 1066 (  generate_series PGNSP PGUID 12 f f t t v 3 23 "23 23 23" _null_ _null_ _null_ generate_series_step_int4 - _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 4b7b7c265276f75216d4f436599902adc22c0d94..c6ca59c87e2f5aa410b6c78ddef5174c7228ec0f 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.167 2005/11/22 18:17:30 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.168 2006/01/08 07:00:26 neilc Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -406,6 +406,7 @@ DATA(insert OID = 1007 (  _int4		 PGNSP PGUID -1 f b t \054 0	23 array_in array_
 DATA(insert OID = 1008 (  _regproc	 PGNSP PGUID -1 f b t \054 0	24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1009 (  _text		 PGNSP PGUID -1 f b t \054 0	25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1028 (  _oid		 PGNSP PGUID -1 f b t \054 0	26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+#define OIDARRAYOID			1028
 DATA(insert OID = 1010 (  _tid		 PGNSP PGUID -1 f b t \054 0	27 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1011 (  _xid		 PGNSP PGUID -1 f b t \054 0	28 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1012 (  _cid		 PGNSP PGUID -1 f b t \054 0	29 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index 50767740ccbda99c05cd3458d57698cf1ac6d950..503f27e4bc3eb9cb0c6a35ea179953c85956c734 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.16 2005/12/14 17:06:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/prepare.h,v 1.17 2006/01/08 07:00:26 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,13 +30,16 @@
 typedef struct
 {
 	/* dynahash.c requires key to be first field */
-	char		stmt_name[NAMEDATALEN];
-	char	   *query_string;	/* text of query, or NULL */
-	const char *commandTag;		/* command tag (a constant!), or NULL */
-	List	   *query_list;		/* list of queries */
-	List	   *plan_list;		/* list of plans */
-	List	   *argtype_list;	/* list of parameter type OIDs */
-	MemoryContext context;		/* context containing this query */
+	char			stmt_name[NAMEDATALEN];
+	char		   *query_string;	/* text of query, or NULL */
+	const char	   *commandTag;		/* command tag (a constant!), or NULL */
+	List		   *query_list;		/* list of queries, rewritten */
+	List		   *plan_list;		/* list of plans */
+	List		   *argtype_list;	/* list of parameter type OIDs */
+	TimestampTz		prepare_time;	/* the time when the stmt was prepared */
+	bool			from_sql;		/* stmt prepared via SQL, not
+									 * FE/BE protocol? */
+	MemoryContext	context;		/* context containing this query */
 } PreparedStatement;
 
 
@@ -54,7 +57,8 @@ extern void StorePreparedStatement(const char *stmt_name,
 					   const char *commandTag,
 					   List *query_list,
 					   List *plan_list,
-					   List *argtype_list);
+					   List *argtype_list,
+					   bool from_sql);
 extern PreparedStatement *FetchPreparedStatement(const char *stmt_name,
 					   bool throwError);
 extern void DropPreparedStatement(const char *stmt_name, bool showError);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 4d1349917ca9157ef70b69da2f438af4340651b3..97fbfdc341e8e4afcc6c6b79d300c78c596edd08 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.268 2005/11/22 18:17:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.269 2006/01/08 07:00:26 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -861,4 +861,7 @@ extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);
 /* catalog/pg_conversion.c */
 extern Datum pg_convert_using(PG_FUNCTION_ARGS);
 
+/* commands/prepare.c */
+extern Datum pg_prepared_statement(PG_FUNCTION_ARGS);
+
 #endif   /* BUILTINS_H */
diff --git a/src/test/regress/expected/prepare.out b/src/test/regress/expected/prepare.out
index 43fd8ecf56c331ffec76b47c497a0df62aa8f19a..54616199b60e889f23c2b711f877125dceb336ec 100644
--- a/src/test/regress/expected/prepare.out
+++ b/src/test/regress/expected/prepare.out
@@ -1,9 +1,22 @@
--- Regression tests for prepareable statements
-PREPARE q1 AS SELECT 1;
+-- Regression tests for prepareable statements. We query the content
+-- of the pg_prepared_statements view as prepared statements are
+-- created and removed.
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name | statement | parameter_types 
+------+-----------+-----------------
+(0 rows)
+
+PREPARE q1 AS SELECT 1 AS a;
 EXECUTE q1;
- ?column? 
-----------
-        1
+ a 
+---
+ 1
+(1 row)
+
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name |          statement           | parameter_types 
+------+------------------------------+-----------------
+ q1   | PREPARE q1 AS SELECT 1 AS a; | {}
 (1 row)
 
 -- should fail
@@ -18,12 +31,41 @@ EXECUTE q1;
         2
 (1 row)
 
+PREPARE q2 AS SELECT 2 AS b;
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name |          statement           | parameter_types 
+------+------------------------------+-----------------
+ q1   | PREPARE q1 AS SELECT 2;      | {}
+ q2   | PREPARE q2 AS SELECT 2 AS b; | {}
+(2 rows)
+
 -- sql92 syntax
 DEALLOCATE PREPARE q1;
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name |          statement           | parameter_types 
+------+------------------------------+-----------------
+ q2   | PREPARE q2 AS SELECT 2 AS b; | {}
+(1 row)
+
+DEALLOCATE PREPARE q2;
+-- the view should return the empty set again
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name | statement | parameter_types 
+------+-----------+-----------------
+(0 rows)
+
 -- parameterized queries
 PREPARE q2(text) AS
 	SELECT datname, datistemplate, datallowconn
 	FROM pg_database WHERE datname = $1;
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name |                                               statement                                                | parameter_types 
+------+--------------------------------------------------------------------------------------------------------+-----------------
+ q2   | PREPARE q2(text) AS
+	SELECT datname, datistemplate, datallowconn
+	FROM pg_database WHERE datname = $1; | {25}
+(1 row)
+
 EXECUTE q2('regression');
   datname   | datistemplate | datallowconn 
 ------------+---------------+--------------
@@ -33,6 +75,17 @@ EXECUTE q2('regression');
 PREPARE q3(text, int, float, boolean, oid, smallint) AS
 	SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR
 	ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int);
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+ name |                                                                                    statement                                                                                    |   parameter_types    
+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------
+ q2   | PREPARE q2(text) AS
+	SELECT datname, datistemplate, datallowconn
+	FROM pg_database WHERE datname = $1;                                                                          | {25}
+ q3   | PREPARE q3(text, int, float, boolean, oid, smallint) AS
+	SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR
+	ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int); | {25,23,701,16,26,21}
+(2 rows)
+
 EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint);
  unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
 ---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2c8070d119586e5cc7f92d2ae95788e5e1fd8703..67616b73335ca2e7bb15b284071f3d1a8d9c0340 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1280,6 +1280,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  pg_group                 | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
  pg_indexes               | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS "tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
  pg_locks                 | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l.transactionid, l.classid, l.objid, l.objsubid, l."transaction", l.pid, l."mode", l."granted" FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, transactionid xid, classid oid, objid oid, objsubid smallint, "transaction" xid, pid integer, "mode" text, "granted" boolean);
+ pg_prepared_statements   | SELECT p.name, p."statement", p.prepare_time, p.parameter_types, p.from_sql FROM pg_prepared_statement() p(name text, "statement" text, prepare_time timestamp with time zone, parameter_types oid[], from_sql boolean);
  pg_prepared_xacts        | SELECT p."transaction", p.gid, p."prepared", u.rolname AS "owner", d.datname AS "database" FROM ((pg_prepared_xact() p("transaction" xid, gid text, "prepared" timestamp with time zone, ownerid oid, dbid oid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
  pg_roles                 | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, pg_authid.rolconfig, pg_authid.oid FROM pg_authid;
  pg_rules                 | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
@@ -1320,7 +1321,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  shoelace_obsolete        | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
  street                   | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
  toyemp                   | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp;
-(44 rows)
+(45 rows)
 
 SELECT tablename, rulename, definition FROM pg_rules 
 	ORDER BY tablename, rulename;
diff --git a/src/test/regress/sql/prepare.sql b/src/test/regress/sql/prepare.sql
index fc6924307da9f79c9fe5b52e408ff37b02ec4609..95db2a0910f8e60b4d171f46710a4df36164ed18 100644
--- a/src/test/regress/sql/prepare.sql
+++ b/src/test/regress/sql/prepare.sql
@@ -1,8 +1,14 @@
--- Regression tests for prepareable statements
+-- Regression tests for prepareable statements. We query the content
+-- of the pg_prepared_statements view as prepared statements are
+-- created and removed.
 
-PREPARE q1 AS SELECT 1;
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
+PREPARE q1 AS SELECT 1 AS a;
 EXECUTE q1;
 
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
 -- should fail
 PREPARE q1 AS SELECT 2;
 
@@ -11,19 +17,33 @@ DEALLOCATE q1;
 PREPARE q1 AS SELECT 2;
 EXECUTE q1;
 
+PREPARE q2 AS SELECT 2 AS b;
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
 -- sql92 syntax
 DEALLOCATE PREPARE q1;
 
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
+DEALLOCATE PREPARE q2;
+-- the view should return the empty set again
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
 -- parameterized queries
 PREPARE q2(text) AS
 	SELECT datname, datistemplate, datallowconn
 	FROM pg_database WHERE datname = $1;
+
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
 EXECUTE q2('regression');
 
 PREPARE q3(text, int, float, boolean, oid, smallint) AS
 	SELECT * FROM tenk1 WHERE string4 = $1 AND (four = $2 OR
 	ten = $3::bigint OR true = $4 OR oid = $5 OR odd = $6::int);
 
+SELECT name, statement, parameter_types FROM pg_prepared_statements;
+
 EXECUTE q3('AAAAxx', 5::smallint, 10.5::float, false, 500::oid, 4::bigint);
 
 -- too few params