Thank you.

Here is my latest attempt, with actual syntax error handling.

Also,  the syntax is updated to what Tom Lane suggested in other
thread (with another variant of the same thing, from Julien Demoor)
NOTIFY [ ( option [, ...] ) ] channel [ , payload ]

Still no hash table fallback is implemented, so this is *not* a
performance improvement. Only a little more flexibility.












On Sat, Mar 9, 2019 at 3:31 AM Thomas Munro <thomas.mu...@gmail.com> wrote:
>
> On Fri, Mar 8, 2019 at 1:37 PM Filip Rembiałkowski
> <filip.rembialkow...@gmail.com> wrote:
> > See attached patch... I'm ready to work on so it can get merged in the next 
> > CF.
>
> Hi Filip,
>
> Seen on Travis:
>
>      async                        ... FAILED      126 ms
>
> Looks like the new error isn't being raised for invalid send mode?
> (What kind of error message is "?" anyway? :-))
>
>  ERROR:  channel name too long
>  -- Should fail. Invalid 3rd parameter
>  NOTIFY notify_async2, 'test', 'invalid';
> -ERROR:  ?
>  NOTIFY notify_async2, 'test', true;
> -ERROR:  ?
>  --Should work. Valid NOTIFY/LISTEN/UNLISTEN commands
>  NOTIFY notify_async2;
>  NOTIFY notify_async2, '';
>
> --
> Thomas Munro
> https://enterprisedb.com
 contrib/tcn/tcn.c                   |  2 +-
 doc/src/sgml/ref/notify.sgml        | 64 +++++++++++++++++++------
 src/backend/commands/async.c        | 93 +++++++++++++++++++++++++++++++------
 src/backend/nodes/copyfuncs.c       |  7 +--
 src/backend/nodes/equalfuncs.c      |  7 +--
 src/backend/nodes/outfuncs.c        |  3 +-
 src/backend/nodes/readfuncs.c       |  3 +-
 src/backend/parser/gram.y           | 78 ++++++++++++++++++++++++++-----
 src/backend/tcop/utility.c          |  8 ++--
 src/backend/utils/adt/ruleutils.c   |  2 +-
 src/include/catalog/pg_proc.dat     |  4 ++
 src/include/commands/async.h        |  4 +-
 src/include/nodes/parsenodes.h      | 12 +++--
 src/test/regress/expected/async.out | 21 +++++++++
 src/test/regress/sql/async.sql      | 10 ++++
 15 files changed, 257 insertions(+), 61 deletions(-)

diff --git a/contrib/tcn/tcn.c b/contrib/tcn/tcn.c
index 5355a64c5e..b80337a5ce 100644
--- a/contrib/tcn/tcn.c
+++ b/contrib/tcn/tcn.c
@@ -161,7 +161,7 @@ triggered_change_notification(PG_FUNCTION_ARGS)
 					strcpy_quoted(payload, SPI_getvalue(trigtuple, tupdesc, colno), '\'');
 				}
 
-				Async_Notify(channel, payload->data);
+				Async_Notify(channel, payload->data, true);
 			}
 			ReleaseSysCache(indexTuple);
 			break;
diff --git a/doc/src/sgml/ref/notify.sgml b/doc/src/sgml/ref/notify.sgml
index e0e125a2a2..e06b00f1f3 100644
--- a/doc/src/sgml/ref/notify.sgml
+++ b/doc/src/sgml/ref/notify.sgml
@@ -21,7 +21,11 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-NOTIFY <replaceable class="parameter">channel</replaceable> [ , <replaceable class="parameter">payload</replaceable> ]
+NOTIFY [ ( option [, ...] ) ] <replaceable class="parameter">channel</replaceable> [ , <replaceable class="parameter">payload</replaceable> ]
+
+<phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
+
+COLLAPSE [ boolean ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -47,10 +51,10 @@ NOTIFY <replaceable class="parameter">channel</replaceable> [ , <replaceable cla
   </para>
 
   <para>
-   The information passed to the client for a notification event includes the
-   notification channel
-   name, the notifying session's server process <acronym>PID</acronym>, and the
-   payload string, which is an empty string if it has not been specified.
+   The information passed to the client for a notification event includes
+   the notification channel name, the notifying session's server process
+   <acronym>PID</acronym>, and the payload string, which is an empty string
+   if it has not been specified.
   </para>
 
   <para>
@@ -92,21 +96,13 @@ NOTIFY <replaceable class="parameter">channel</replaceable> [ , <replaceable cla
    is that applications using <command>NOTIFY</command> for real-time signaling
    should try to keep their transactions short.
   </para>
-
   <para>
-   If the same channel name is signaled multiple times from the same
-   transaction with identical payload strings, the
-   database server can decide to deliver a single notification only.
-   On the other hand, notifications with distinct payload strings will
-   always be delivered as distinct notifications. Similarly, notifications from
-   different transactions will never get folded into one notification.
    Except for dropping later instances of duplicate notifications,
    <command>NOTIFY</command> guarantees that notifications from the same
    transaction get delivered in the order they were sent.  It is also
-   guaranteed that messages from different transactions are delivered in
-   the order in which the transactions committed.
+   guaranteed that messages from different transactions are delivered
+   in the order in which the transactions committed.
   </para>
-
   <para>
    It is common for a client that executes <command>NOTIFY</command>
    to be listening on the same notification channel itself.  In that case
@@ -147,6 +143,21 @@ NOTIFY <replaceable class="parameter">channel</replaceable> [ , <replaceable cla
      </para>
     </listitem>
    </varlistentry>
+   <varlistentry>
+    <term><replaceable class="parameter">COLLAPSE</replaceable></term>
+    <listitem>
+     <para>
+      Controls collapsing of repeated messages.
+
+      When set to <literal>on</literal>, notification will be skipped if
+      the same channel was already signaled with identical payload in the same
+      transaction block (finished with <literal>COMMIT</literal>,
+      <literal>END</literal> or <literal>SAVEPOINT</literal>).
+      
+      When set to <literal>off</literal>, duplicates will not be collapsed.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
@@ -190,6 +201,13 @@ NOTIFY <replaceable class="parameter">channel</replaceable> [ , <replaceable cla
     to use than the <command>NOTIFY</command> command if you need to work with
     non-constant channel names and payloads.
    </para>
+   <para>
+    There is a three-argument version,
+    <literal><function>pg_notify</function>(<type>text</type>,
+    <type>text</type>, <type>boolean</type>)</literal>. The third argument
+    corresponds to the <replaceable class="parameter">COLLAPSE</replaceable>
+    option of the <literal>NOTIFY</literal> command. 
+   </para>
   </refsect2>
  </refsect1>
 
@@ -210,6 +228,22 @@ Asynchronous notification "virtual" with payload "This is the payload" received
 LISTEN foo;
 SELECT pg_notify('fo' || 'o', 'pay' || 'load');
 Asynchronous notification "foo" with payload "payload" received from server process with PID 14728.
+
+/* Identical messages from same (sub-) transaction will be eliminated,
+   unless you use COLLAPSE off */
+LISTEN bar;
+BEGIN;
+NOTIFY bar, 'Coffee please';
+NOTIFY bar, 'Coffee please';
+NOTIFY bar, 'Milk please';
+NOTIFY (COLLAPSE off) bar, 'Milk please';
+SAVEPOINT s;
+NOTIFY bar, 'Coffee please';
+COMMIT;
+Asynchronous notification "bar" with payload "Coffee please" received from server process with PID 31517.
+Asynchronous notification "bar" with payload "Milk please" received from server process with PID 31517.
+Asynchronous notification "bar" with payload "Milk please" received from server process with PID 31517.
+Asynchronous notification "bar" with payload "Coffee please" received from server process with PID 31517.
 </programlisting></para>
  </refsect1>
 
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 5a7ee0de4c..0f1280b3f5 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -49,12 +49,14 @@
  * 4. The NOTIFY statement (routine Async_Notify) stores the notification in
  *	  a backend-local list which will not be processed until transaction end.
  *
- *	  Duplicate notifications from the same transaction are sent out as one
- *	  notification only. This is done to save work when for example a trigger
- *	  on a 2 million row table fires a notification for each row that has been
- *	  changed. If the application needs to receive every single notification
- *	  that has been sent, it can easily add some unique string into the extra
- *	  payload parameter.
+ *	  By default (COLLAPSE on), duplicate notifications from the
+ *	  same transaction are sent out as one notification only. This is done to
+ *	  save work when for example a trigger on a 2 million row table fires a
+ *	  notification for each row that has been changed. If the application needs
+ *	  to receive every single notification that has been sent, it can easily
+ *	  add some unique string into the extra payload parameter.
+ *
+ *	  If COLLAPSE off, de-duplication is not performed.
  *
  *	  When the transaction is ready to commit, PreCommit_Notify() adds the
  *	  pending notifications to the head of the queue. The head pointer of the
@@ -123,6 +125,7 @@
 #include "access/xact.h"
 #include "catalog/pg_database.h"
 #include "commands/async.h"
+#include "commands/defrem.h"
 #include "funcapi.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
@@ -523,7 +526,42 @@ pg_notify(PG_FUNCTION_ARGS)
 	/* For NOTIFY as a statement, this is checked in ProcessUtility */
 	PreventCommandDuringRecovery("NOTIFY");
 
-	Async_Notify(channel, payload);
+	Async_Notify(channel, payload, true);
+
+	PG_RETURN_VOID();
+}
+
+
+/*
+ * pg_notify_3args
+ *    SQL function to send a notification event, 3-argument version
+ */
+Datum
+pg_notify_3args(PG_FUNCTION_ARGS)
+{
+	const char		*channel;
+	const char		*payload;
+	bool			 collapse;
+
+	if (PG_ARGISNULL(0))
+		channel = "";
+	else
+		channel = text_to_cstring(PG_GETARG_TEXT_PP(0));
+
+	if (PG_ARGISNULL(1))
+		payload = "";
+	else
+		payload = text_to_cstring(PG_GETARG_TEXT_PP(1));
+
+	if (PG_ARGISNULL(2))
+		collapse = true;
+	else
+		collapse = PG_GETARG_BOOL(2);
+
+	/* For NOTIFY as a statement, this is checked in ProcessUtility */
+	PreventCommandDuringRecovery("NOTIFY");
+
+	Async_Notify(channel, payload, collapse);
 
 	PG_RETURN_VOID();
 }
@@ -539,10 +577,10 @@ pg_notify(PG_FUNCTION_ARGS)
  *		^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  */
 void
-Async_Notify(const char *channel, const char *payload)
+Async_Notify(const char *channel, const char *payload, bool collapse)
 {
-	Notification *n;
-	MemoryContext oldcontext;
+	Notification	*n;
+	MemoryContext	 oldcontext;
 
 	if (IsParallelWorker())
 		elog(ERROR, "cannot send notifications from a parallel worker");
@@ -550,7 +588,7 @@ Async_Notify(const char *channel, const char *payload)
 	if (Trace_notify)
 		elog(DEBUG1, "Async_Notify(%s)", channel);
 
-	/* a channel name must be specified */
+	/* channel name must be specified */
 	if (!channel || !strlen(channel))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -569,9 +607,10 @@ Async_Notify(const char *channel, const char *payload)
 					 errmsg("payload string too long")));
 	}
 
-	/* no point in making duplicate entries in the list ... */
-	if (AsyncExistsPendingNotify(channel, payload))
-		return;
+	if (collapse)
+		/* remove duplicate entries in the list */
+		if (AsyncExistsPendingNotify(channel, payload))
+			return;
 
 	/*
 	 * The notification list needs to live until end of transaction, so store
@@ -595,6 +634,32 @@ Async_Notify(const char *channel, const char *payload)
 	MemoryContextSwitchTo(oldcontext);
 }
 
+void
+Async_Notify_WithOptions(const char *channel,
+		const char *payload, List *options)
+{
+	ListCell		*lc;
+
+	bool			 collapse;
+
+	collapse = true;
+
+	/* Parse options list. */
+	foreach(lc, options)
+	{
+		DefElem *opt = (DefElem *) lfirst(lc);
+
+		if (strcmp(opt->defname, "collapse") == 0)
+			collapse = defGetBoolean(opt);
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("unrecognized NOTIFY option \"%s\"",
+							opt->defname)));
+	}
+	Async_Notify(channel, payload, collapse);
+}
+
 /*
  * queue_listen
  *		Common code for listen, unlisten, unlisten all commands.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a8a735c247..df91526429 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3623,8 +3623,9 @@ _copyNotifyStmt(const NotifyStmt *from)
 {
 	NotifyStmt *newnode = makeNode(NotifyStmt);
 
-	COPY_STRING_FIELD(conditionname);
+	COPY_STRING_FIELD(channel);
 	COPY_STRING_FIELD(payload);
+	COPY_NODE_FIELD(options);
 
 	return newnode;
 }
@@ -3634,7 +3635,7 @@ _copyListenStmt(const ListenStmt *from)
 {
 	ListenStmt *newnode = makeNode(ListenStmt);
 
-	COPY_STRING_FIELD(conditionname);
+	COPY_STRING_FIELD(channel);
 
 	return newnode;
 }
@@ -3644,7 +3645,7 @@ _copyUnlistenStmt(const UnlistenStmt *from)
 {
 	UnlistenStmt *newnode = makeNode(UnlistenStmt);
 
-	COPY_STRING_FIELD(conditionname);
+	COPY_STRING_FIELD(channel);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3cab90e9f8..3db593e63c 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1480,8 +1480,9 @@ _equalRuleStmt(const RuleStmt *a, const RuleStmt *b)
 static bool
 _equalNotifyStmt(const NotifyStmt *a, const NotifyStmt *b)
 {
-	COMPARE_STRING_FIELD(conditionname);
+	COMPARE_STRING_FIELD(channel);
 	COMPARE_STRING_FIELD(payload);
+	COMPARE_NODE_FIELD(options);
 
 	return true;
 }
@@ -1489,7 +1490,7 @@ _equalNotifyStmt(const NotifyStmt *a, const NotifyStmt *b)
 static bool
 _equalListenStmt(const ListenStmt *a, const ListenStmt *b)
 {
-	COMPARE_STRING_FIELD(conditionname);
+	COMPARE_STRING_FIELD(channel);
 
 	return true;
 }
@@ -1497,7 +1498,7 @@ _equalListenStmt(const ListenStmt *a, const ListenStmt *b)
 static bool
 _equalUnlistenStmt(const UnlistenStmt *a, const UnlistenStmt *b)
 {
-	COMPARE_STRING_FIELD(conditionname);
+	COMPARE_STRING_FIELD(channel);
 
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 69179a07c3..bebdd0a9e6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2661,8 +2661,9 @@ _outNotifyStmt(StringInfo str, const NotifyStmt *node)
 {
 	WRITE_NODE_TYPE("NOTIFY");
 
-	WRITE_STRING_FIELD(conditionname);
+	WRITE_STRING_FIELD(channel);
 	WRITE_STRING_FIELD(payload);
+	WRITE_NODE_FIELD(options);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 4b845b1bb7..dfe64b5fec 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -296,8 +296,9 @@ _readNotifyStmt(void)
 {
 	READ_LOCALS(NotifyStmt);
 
-	READ_STRING_FIELD(conditionname);
+	READ_STRING_FIELD(channel);
 	READ_STRING_FIELD(payload);
+	READ_NODE_FIELD(options);
 
 	READ_DONE();
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e23e68fdb3..582c2ef692 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -524,7 +524,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <boolean> opt_varying opt_timezone opt_no_inherit
 
 %type <ival>	Iconst SignedIconst
-%type <str>		Sconst comment_text notify_payload
+%type <str>		Sconst comment_text
+%type <str>		notify_option_name
+%type <node>	notify_option_arg
+%type <defelt>	notify_option_elem
+%type <list>	notify_option_list
+
 %type <str>		RoleId opt_boolean_or_string
 %type <list>	var_list
 %type <str>		ColId ColLabel var_name type_function_name param_name
@@ -9741,24 +9746,73 @@ opt_instead:
  *
  *****************************************************************************/
 
-NotifyStmt: NOTIFY ColId notify_payload
+NotifyStmt:
+		NOTIFY ColId
+			{
+				NotifyStmt *n = makeNode(NotifyStmt);
+				n->channel = $2;
+				n->payload = NULL;
+				n->options = NIL;
+				$$ = (Node *)n;
+			}
+		| NOTIFY ColId ',' Sconst
+			{
+				NotifyStmt *n = makeNode(NotifyStmt);
+				n->channel = $2;
+				n->payload = $4;
+				n->options = NIL;
+				$$ = (Node *)n;
+			}
+		| NOTIFY '(' notify_option_list ')' ColId
+			{
+				NotifyStmt *n = makeNode(NotifyStmt);
+				n->options = $3;
+				n->channel = $5;
+				n->payload = NULL;
+				$$ = (Node *)n;
+			}
+		| NOTIFY '(' notify_option_list ')' ColId ',' Sconst
+			{
+				NotifyStmt *n = makeNode(NotifyStmt);
+				n->options = $3;
+				n->channel = $5;
+				n->payload = $7;
+				$$ = (Node *)n;
+			}
+		;
+
+notify_option_list:
+			notify_option_elem
 				{
-					NotifyStmt *n = makeNode(NotifyStmt);
-					n->conditionname = $2;
-					n->payload = $3;
-					$$ = (Node *)n;
+					$$ = list_make1($1);
+				}
+			| notify_option_list ',' notify_option_elem
+				{
+					$$ = lappend($1, $3);
 				}
 		;
 
-notify_payload:
-			',' Sconst							{ $$ = $2; }
-			| /*EMPTY*/							{ $$ = NULL; }
+notify_option_elem:
+			notify_option_name notify_option_arg
+				{
+					$$ = makeDefElem($1, $2, @1);
+				}
+		;
+
+notify_option_name:
+			NonReservedWord			{ $$ = $1; }
+		;
+
+notify_option_arg:
+			opt_boolean_or_string		{ $$ = (Node *) makeString($1); }
+			| NumericOnly				{ $$ = (Node *) $1; }
+			| /* EMPTY */				{ $$ = NULL; }
 		;
 
 ListenStmt: LISTEN ColId
 				{
 					ListenStmt *n = makeNode(ListenStmt);
-					n->conditionname = $2;
+					n->channel = $2;
 					$$ = (Node *)n;
 				}
 		;
@@ -9767,13 +9821,13 @@ UnlistenStmt:
 			UNLISTEN ColId
 				{
 					UnlistenStmt *n = makeNode(UnlistenStmt);
-					n->conditionname = $2;
+					n->channel = $2;
 					$$ = (Node *)n;
 				}
 			| UNLISTEN '*'
 				{
 					UnlistenStmt *n = makeNode(UnlistenStmt);
-					n->conditionname = NULL;
+					n->channel = NULL;
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6ec795f1b4..d6f8d09174 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -611,7 +611,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 				NotifyStmt *stmt = (NotifyStmt *) parsetree;
 
 				PreventCommandDuringRecovery("NOTIFY");
-				Async_Notify(stmt->conditionname, stmt->payload);
+				Async_Notify_WithOptions(stmt->channel, stmt->payload, stmt->options);
 			}
 			break;
 
@@ -621,7 +621,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
 				PreventCommandDuringRecovery("LISTEN");
 				CheckRestrictedOperation("LISTEN");
-				Async_Listen(stmt->conditionname);
+				Async_Listen(stmt->channel);
 			}
 			break;
 
@@ -631,8 +631,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
 				/* we allow UNLISTEN during recovery, as it's a noop */
 				CheckRestrictedOperation("UNLISTEN");
-				if (stmt->conditionname)
-					Async_Unlisten(stmt->conditionname);
+				if (stmt->channel)
+					Async_Unlisten(stmt->channel);
 				else
 					Async_UnlistenAll();
 			}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 85055bbb95..1300347387 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6581,7 +6581,7 @@ get_utility_query_def(Query *query, deparse_context *context)
 		appendContextKeyword(context, "",
 							 0, PRETTYINDENT_STD, 1);
 		appendStringInfo(buf, "NOTIFY %s",
-						 quote_identifier(stmt->conditionname));
+						 quote_identifier(stmt->channel));
 		if (stmt->payload)
 		{
 			appendStringInfoString(buf, ", ");
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 562c5408f8..94dbe5c70a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -7554,6 +7554,10 @@
   proname => 'pg_notify', proisstrict => 'f', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'text text',
   prosrc => 'pg_notify' },
+{ oid => '3998', descr => 'send a notification event',
+  proname => 'pg_notify', proisstrict => 'f', provolatile => 'v',
+  proparallel => 'r', prorettype => 'void', proargtypes => 'text text bool',
+  prosrc => 'pg_notify_3args' },
 { oid => '3296',
   descr => 'get the fraction of the asynchronous notification queue currently in use',
   proname => 'pg_notification_queue_usage', provolatile => 'v',
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index cfea78e039..36b39ff8bd 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -16,6 +16,7 @@
 #include <signal.h>
 
 #include "fmgr.h"
+#include "nodes/parsenodes.h"
 
 /*
  * The number of SLRU page buffers we use for the notification queue.
@@ -33,7 +34,8 @@ extern void NotifyMyFrontEnd(const char *channel,
 				 int32 srcPid);
 
 /* notify-related SQL statements */
-extern void Async_Notify(const char *channel, const char *payload);
+extern void Async_Notify(const char *channel, const char *payload, bool collapse);
+extern void Async_Notify_WithOptions(const char *channel, const char *payload, List *options);
 extern void Async_Listen(const char *channel);
 extern void Async_Unlisten(const char *channel);
 extern void Async_UnlistenAll(void);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fe35783359..94f24568e9 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -27,6 +27,7 @@
 #include "nodes/primnodes.h"
 #include "nodes/value.h"
 #include "partitioning/partdefs.h"
+#include "commands/async.h"
 
 
 typedef enum OverridingKind
@@ -2947,9 +2948,10 @@ typedef struct RuleStmt
  */
 typedef struct NotifyStmt
 {
-	NodeTag		type;
-	char	   *conditionname;	/* condition name to notify */
-	char	   *payload;		/* the payload string, or NULL if none */
+	NodeTag	 type;
+	char	*channel;	/* channel name to notify */
+	char	*payload;	/* the payload string, or NULL if none */
+	List	*options;	/* List of DefElem nodes */
 } NotifyStmt;
 
 /* ----------------------
@@ -2959,7 +2961,7 @@ typedef struct NotifyStmt
 typedef struct ListenStmt
 {
 	NodeTag		type;
-	char	   *conditionname;	/* condition name to listen on */
+	char	   *channel;		/* channel name to listen on */
 } ListenStmt;
 
 /* ----------------------
@@ -2969,7 +2971,7 @@ typedef struct ListenStmt
 typedef struct UnlistenStmt
 {
 	NodeTag		type;
-	char	   *conditionname;	/* name to unlisten on, or NULL for all */
+	char	   *channel;		/* channel name to unlisten, or NULL for all */
 } UnlistenStmt;
 
 /* ----------------------
diff --git a/src/test/regress/expected/async.out b/src/test/regress/expected/async.out
index 19cbe38e63..6d5726127c 100644
--- a/src/test/regress/expected/async.out
+++ b/src/test/regress/expected/async.out
@@ -8,6 +8,18 @@ SELECT pg_notify('notify_async1','sample message1');
  
 (1 row)
 
+SELECT pg_notify('notify_async1','sample_message1',true);
+ pg_notify 
+-----------
+ 
+(1 row)
+
+SELECT pg_notify('notify_async1','sample_message1',false);
+ pg_notify 
+-----------
+ 
+(1 row)
+
 SELECT pg_notify('notify_async1','');
  pg_notify 
 -----------
@@ -29,9 +41,18 @@ SELECT pg_notify('notify_async_channel_name_too_long____________________________
 ERROR:  channel name too long
 --Should work. Valid NOTIFY/LISTEN/UNLISTEN commands
 NOTIFY notify_async2;
+NOTIFY (collapse off) notify_async2;
+NOTIFY notify_async2, '';
+NOTIFY (collapse on) notify_async2, '';
+NOTIFY (collapse off) notify_async2, '';
 LISTEN notify_async2;
 UNLISTEN notify_async2;
 UNLISTEN *;
+-- Should fail. Invalid option syntax
+NOTIFY (collapse maybe) notify_async2;
+ERROR:  collapse requires a Boolean value
+NOTIFY (wrong opt) notify_async2, 'test';
+ERROR:  unrecognized NOTIFY option "wrong"
 -- Should return zero while there are no pending notifications.
 -- src/test/isolation/specs/async-notify.spec tests for actual usage.
 SELECT pg_notification_queue_usage();
diff --git a/src/test/regress/sql/async.sql b/src/test/regress/sql/async.sql
index 40f6e01538..29f9681d2a 100644
--- a/src/test/regress/sql/async.sql
+++ b/src/test/regress/sql/async.sql
@@ -4,6 +4,8 @@
 
 --Should work. Send a valid message via a valid channel name
 SELECT pg_notify('notify_async1','sample message1');
+SELECT pg_notify('notify_async1','sample_message1',true);
+SELECT pg_notify('notify_async1','sample_message1',false);
 SELECT pg_notify('notify_async1','');
 SELECT pg_notify('notify_async1',NULL);
 
@@ -14,10 +16,18 @@ SELECT pg_notify('notify_async_channel_name_too_long____________________________
 
 --Should work. Valid NOTIFY/LISTEN/UNLISTEN commands
 NOTIFY notify_async2;
+NOTIFY (collapse off) notify_async2;
+NOTIFY notify_async2, '';
+NOTIFY (collapse on) notify_async2, '';
+NOTIFY (collapse off) notify_async2, '';
 LISTEN notify_async2;
 UNLISTEN notify_async2;
 UNLISTEN *;
 
+-- Should fail. Invalid option syntax
+NOTIFY (collapse maybe) notify_async2;
+NOTIFY (wrong opt) notify_async2, 'test';
+
 -- Should return zero while there are no pending notifications.
 -- src/test/isolation/specs/async-notify.spec tests for actual usage.
 SELECT pg_notification_queue_usage();

Reply via email to