diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 8eac671fabfeb67d9266644775cc68b58907ac7f..1065eddc74efb52c270bee0a89c0e5b9d4e9045f 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.127 2008/04/06 23:43:29 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.128 2008/05/03 00:11:36 tgl Exp $ -->
 
 <chapter id="plpgsql">
   <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
@@ -1467,6 +1467,7 @@ RETURN <replaceable>expression</replaceable>;
 <synopsis>
 RETURN NEXT <replaceable>expression</replaceable>;
 RETURN QUERY <replaceable>query</replaceable>;
+RETURN QUERY EXECUTE <replaceable class="command">command-string</replaceable> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional>;
 </synopsis>
 
      <para>
@@ -1500,6 +1501,14 @@ RETURN QUERY <replaceable>query</replaceable>;
       let control reach the end of the function).
      </para>
 
+     <para>
+      <command>RETURN QUERY</command> has a variant
+      <command>RETURN QUERY EXECUTE</command>, which specifies the
+      query to be executed dynamically.  Parameter expressions can
+      be inserted into the computed query string via <literal>USING</>,
+      in just the same way as in the <command>EXECUTE</> command.
+     </para>
+
      <para>
       If you declared the function with output parameters, write just
       <command>RETURN NEXT</command> with no expression.  On each
@@ -1544,7 +1553,6 @@ SELECT * FROM getallfoo();
       Note that functions using <command>RETURN NEXT</command> or
       <command>RETURN QUERY</command> must be called as a table source in
       a <literal>FROM</literal> clause.
-
      </para>
 
      <note>
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 495b625a450835d4cfa0f8f1fcbc611f281547f8..979ebee3ca330a5de962c0ebedc0540db67d6346 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.110 2008/04/06 23:43:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.111 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2239,6 +2239,7 @@ static PLpgSQL_stmt *
 make_return_query_stmt(int lineno)
 {
 	PLpgSQL_stmt_return_query *new;
+	int			tok;
 
 	if (!plpgsql_curr_compile->fn_retset)
 		yyerror("cannot use RETURN QUERY in a non-SETOF function");
@@ -2246,7 +2247,32 @@ make_return_query_stmt(int lineno)
 	new = palloc0(sizeof(PLpgSQL_stmt_return_query));
 	new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
 	new->lineno = lineno;
-	new->query = read_sql_stmt("");
+
+	/* check for RETURN QUERY EXECUTE */
+	if ((tok = yylex()) != K_EXECUTE)
+	{
+		/* ordinary static query */
+		plpgsql_push_back_token(tok);
+		new->query = read_sql_stmt("");
+	}
+	else
+	{
+		/* dynamic SQL */
+		int		term;
+
+		new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
+											 &term);
+		if (term == K_USING)
+		{
+			do
+			{
+				PLpgSQL_expr *expr;
+
+				expr = read_sql_expression2(',', ';', ", or ;", &term);
+				new->params = lappend(new->params, expr);
+			} while (term == ',');
+		}
+	}
 
 	return (PLpgSQL_stmt *) new;
 }
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index a1774bf50340bb43a204823fd4c5963f56c36839..64845f296564285964228144abc416219cf4c5c7 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.210 2008/04/17 21:37:28 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.211 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -197,6 +197,8 @@ static void assign_text_var(PLpgSQL_var *var, const char *str);
 static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
 												  List *params);
 static void free_params_data(PreparedParamsData *ppd);
+static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
+										PLpgSQL_expr *query, List *params);
 
 
 /* ----------
@@ -1968,7 +1970,7 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
 					PLpgSQL_row *row = (PLpgSQL_row *) retvar;
 
 					Assert(row->rowtupdesc);
-					estate->retval = 
+					estate->retval =
 						PointerGetDatum(make_tuple_from_row(estate, row,
 															row->rowtupdesc));
 					if (DatumGetPointer(estate->retval) == NULL) /* should not happen */
@@ -2189,7 +2191,18 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
 	if (estate->tuple_store == NULL)
 		exec_init_tuple_store(estate);
 
-	exec_run_select(estate, stmt->query, 0, &portal);
+	if (stmt->query != NULL)
+	{
+		/* static query */
+		exec_run_select(estate, stmt->query, 0, &portal);
+	}
+	else
+	{
+		/* RETURN QUERY EXECUTE */
+		Assert(stmt->dynquery != NULL);
+		portal = exec_dynquery_with_params(estate, stmt->dynquery,
+										   stmt->params);
+	}
 
 	if (!compatible_tupdesc(estate->rettupdesc, portal->tupDesc))
 		ereport(ERROR,
@@ -2841,58 +2854,10 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
 static int
 exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
 {
-	Datum		query;
-	bool		isnull;
-	Oid			restype;
-	char	   *querystr;
 	Portal		portal;
 	int			rc;
 
-	/*
-	 * Evaluate the string expression after the EXECUTE keyword. It's result
-	 * is the querystring we have to execute.
-	 */
-	query = exec_eval_expr(estate, stmt->query, &isnull, &restype);
-	if (isnull)
-		ereport(ERROR,
-				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-				 errmsg("cannot EXECUTE a null querystring")));
-
-	/* Get the C-String representation */
-	querystr = convert_value_to_string(query, restype);
-
-	exec_eval_cleanup(estate);
-
-	/*
-	 * Open an implicit cursor for the query.  We use SPI_cursor_open_with_args
-	 * even when there are no params, because this avoids making and freeing
-	 * one copy of the plan.
-	 */
-	if (stmt->params)
-	{
-		PreparedParamsData *ppd;
-
-		ppd = exec_eval_using_params(estate, stmt->params);
-		portal = SPI_cursor_open_with_args(NULL,
-										   querystr,
-										   ppd->nargs, ppd->types,
-										   ppd->values, ppd->nulls,
-										   estate->readonly_func, 0);
-		free_params_data(ppd);
-	}
-	else
-	{
-		portal = SPI_cursor_open_with_args(NULL,
-										   querystr,
-										   0, NULL,
-										   NULL, NULL,
-										   estate->readonly_func, 0);
-	}
-
-	if (portal == NULL)
-		elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
-			 querystr, SPI_result_code_string(SPI_result));
-	pfree(querystr);
+	portal = exec_dynquery_with_params(estate, stmt->query, stmt->params);
 
 	/*
 	 * Execute the loop
@@ -5208,3 +5173,65 @@ free_params_data(PreparedParamsData *ppd)
 
 	pfree(ppd);
 }
+
+/*
+ * Open portal for dynamic query
+ */
+static Portal
+exec_dynquery_with_params(PLpgSQL_execstate *estate, PLpgSQL_expr *dynquery,
+						  List *params)
+{
+	Portal		portal;
+	Datum		query;
+	bool		isnull;
+	Oid			restype;
+	char	   *querystr;
+
+	/*
+	 * Evaluate the string expression after the EXECUTE keyword. Its result
+	 * is the querystring we have to execute.
+	 */
+	query = exec_eval_expr(estate, dynquery, &isnull, &restype);
+	if (isnull)
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 errmsg("cannot EXECUTE a null querystring")));
+
+	/* Get the C-String representation */
+	querystr = convert_value_to_string(query, restype);
+
+	exec_eval_cleanup(estate);
+
+	/*
+	 * Open an implicit cursor for the query.  We use SPI_cursor_open_with_args
+	 * even when there are no params, because this avoids making and freeing
+	 * one copy of the plan.
+	 */
+	if (params)
+	{
+		PreparedParamsData *ppd;
+
+		ppd = exec_eval_using_params(estate, params);
+		portal = SPI_cursor_open_with_args(NULL,
+										   querystr,
+										   ppd->nargs, ppd->types,
+										   ppd->values, ppd->nulls,
+										   estate->readonly_func, 0);
+		free_params_data(ppd);
+	}
+	else
+	{
+		portal = SPI_cursor_open_with_args(NULL,
+										   querystr,
+										   0, NULL,
+										   NULL, NULL,
+										   estate->readonly_func, 0);
+	}
+
+	if (portal == NULL)
+		elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
+			 querystr, SPI_result_code_string(SPI_result));
+	pfree(querystr);
+
+	return portal;
+}
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 1a68c9339f2f7bb6969af2ffa5ed2e94633bc23d..f7624a6c33d6cac1f548482d072671c9eb1afa12 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.69 2008/04/06 23:43:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.70 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -963,9 +963,37 @@ static void
 dump_return_query(PLpgSQL_stmt_return_query *stmt)
 {
 	dump_ind();
-	printf("RETURN QUERY ");
-	dump_expr(stmt->query);
-	printf("\n");
+	if (stmt->query)
+	{
+		printf("RETURN QUERY ");
+		dump_expr(stmt->query);
+		printf("\n");
+	}
+	else
+	{
+		printf("RETURN QUERY EXECUTE ");
+		dump_expr(stmt->dynquery);
+		printf("\n");
+		if (stmt->params != NIL)
+		{
+			ListCell   *lc;
+			int			i;
+
+			dump_indent += 2;
+			dump_ind();
+			printf("    USING\n");
+			dump_indent += 2;
+			i = 1;
+			foreach(lc, stmt->params)
+			{
+				dump_ind();
+				printf("    parameter $%d: ", i++);
+				dump_expr((PLpgSQL_expr *) lfirst(lc));
+				printf("\n");
+			}
+			dump_indent -= 4;
+		}
+	}
 }
 
 static void
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index a50d5721f437dc93b2e9e9cd5eb4fe61dd5b3cc7..f1206932bb5e871af6366d2b25d87831334c28a3 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.97 2008/04/06 23:43:29 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.98 2008/05/03 00:11:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -529,7 +529,9 @@ typedef struct
 {								/* RETURN QUERY statement */
 	int			cmd_type;
 	int			lineno;
-	PLpgSQL_expr *query;
+	PLpgSQL_expr *query;		/* if static query */
+	PLpgSQL_expr *dynquery;		/* if dynamic query (RETURN QUERY EXECUTE) */
+	List	   *params;			/* USING arguments for dynamic query */
 } PLpgSQL_stmt_return_query;
 
 typedef struct
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 018c8c2b50f974ade5fc0a1f409e8d9c6b7d7667..da987b22e460218f406c4ee5982d30f3449b018a 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -3267,3 +3267,21 @@ end;
 $$ language plpgsql;
 ERROR:  cursor FOR loop must use a bound cursor variable
 CONTEXT:  compile of PL/pgSQL function "forc_bad" near line 4
+-- return query execute
+create or replace function return_dquery()
+returns setof int as $$
+begin
+  return query execute 'select * from (values(10),(20)) f';
+  return query execute 'select * from (values($1),($2)) f' using 40,50;
+end;
+$$ language plpgsql;
+select * from return_dquery();
+ return_dquery 
+---------------
+            10
+            20
+            40
+            50
+(4 rows)
+
+drop function return_dquery();
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 066ccbeba639bcfebe29a4a893956e2f476cc099..b0799dcdc705d53acdd5ad99570cea7dc869a04a 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -2669,3 +2669,17 @@ begin
   end loop;
 end;
 $$ language plpgsql;
+
+-- return query execute
+
+create or replace function return_dquery()
+returns setof int as $$
+begin
+  return query execute 'select * from (values(10),(20)) f';
+  return query execute 'select * from (values($1),($2)) f' using 40,50;
+end;
+$$ language plpgsql;
+
+select * from return_dquery();
+
+drop function return_dquery();