*** a/doc/src/sgml/ref/deallocate.sgml
--- b/doc/src/sgml/ref/deallocate.sgml
***************
*** 26,32 **** PostgreSQL documentation
  
   <refsynopsisdiv>
  <synopsis>
! DEALLOCATE [ PREPARE ] { <replaceable class="parameter">name</replaceable> | ALL }
  </synopsis>
   </refsynopsisdiv>
  
--- 26,33 ----
  
   <refsynopsisdiv>
  <synopsis>
! DEALLOCATE [ PREPARE ] [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
! DEALLOCATE [ PREPARE ] ALL
  </synopsis>
   </refsynopsisdiv>
  
***************
*** 59,64 **** DEALLOCATE [ PREPARE ] { <replaceable class="parameter">name</replaceable> | ALL
--- 60,75 ----
     </varlistentry>
  
     <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the prepared statement does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="parameter">name</replaceable></term>
      <listitem>
       <para>
*** a/src/backend/commands/prepare.c
--- b/src/backend/commands/prepare.c
***************
*** 556,562 **** void
  DeallocateQuery(DeallocateStmt *stmt)
  {
  	if (stmt->name)
! 		DropPreparedStatement(stmt->name, true);
  	else
  		DropAllPreparedStatements();
  }
--- 556,562 ----
  DeallocateQuery(DeallocateStmt *stmt)
  {
  	if (stmt->name)
! 		DropPreparedStatement(stmt->name, !stmt->missing_ok);
  	else
  		DropAllPreparedStatements();
  }
***************
*** 582,587 **** DropPreparedStatement(const char *stmt_name, bool showError)
--- 582,591 ----
  		/* Now we can remove the hash table entry */
  		hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
  	}
+ 	else
+ 		ereport(NOTICE,
+ 			(errmsg("prepared statement \"%s\" does not exist, skipping",
+ 				stmt_name)));
  }
  
  /*
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 8571,8582 **** DeallocateStmt: DEALLOCATE name
--- 8571,8598 ----
  					{
  						DeallocateStmt *n = makeNode(DeallocateStmt);
  						n->name = $2;
+ 						n->missing_ok = FALSE;
  						$$ = (Node *) n;
  					}
  				| DEALLOCATE PREPARE name
  					{
  						DeallocateStmt *n = makeNode(DeallocateStmt);
  						n->name = $3;
+ 						n->missing_ok = FALSE;
+ 						$$ = (Node *) n;
+ 					}
+ 				| DEALLOCATE IF_P EXISTS name
+ 					{
+ 						DeallocateStmt *n = makeNode(DeallocateStmt);
+ 						n->name = $4;
+ 						n->missing_ok = TRUE;
+ 						$$ = (Node *) n;
+ 					}
+ 				| DEALLOCATE PREPARE IF_P EXISTS name
+ 					{
+ 						DeallocateStmt *n = makeNode(DeallocateStmt);
+ 						n->name = $5;
+ 						n->missing_ok = TRUE;
  						$$ = (Node *) n;
  					}
  				| DEALLOCATE ALL
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 2575,2580 **** typedef struct DeallocateStmt
--- 2575,2581 ----
  	NodeTag		type;
  	char	   *name;			/* The name of the plan to remove */
  	/* NULL means DEALLOCATE ALL */
+ 	bool		missing_ok;		/* skip error if table missing */
  } DeallocateStmt;
  
  /*
*** a/src/test/regress/expected/prepare.out
--- b/src/test/regress/expected/prepare.out
***************
*** 178,180 **** SELECT name, statement, parameter_types FROM pg_prepared_statements
--- 178,183 ----
  ------+-----------+-----------------
  (0 rows)
  
+ -- test DEALLOCATE IF EXISTS;
+ DEALLOCATE IF EXISTS unprepared_statement;
+ NOTICE:  prepared statement "unprepared_statement" does not exist, skipping
*** a/src/test/regress/sql/prepare.sql
--- b/src/test/regress/sql/prepare.sql
***************
*** 75,77 **** SELECT name, statement, parameter_types FROM pg_prepared_statements
--- 75,80 ----
  DEALLOCATE ALL;
  SELECT name, statement, parameter_types FROM pg_prepared_statements
      ORDER BY name;
+ 
+ -- test DEALLOCATE IF EXISTS;
+ DEALLOCATE IF EXISTS unprepared_statement;
