Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 0cca3ab

Browse files
author
Commitfest Bot
committed
[PATCH]: ./listen-pattern.patch
1 parent 0f21db3 commit 0cca3ab

File tree

11 files changed

+194
-42
lines changed

11 files changed

+194
-42
lines changed

doc/src/sgml/ref/listen.sgml

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
LISTEN <replaceable class="parameter">channel</replaceable>
24+
LISTEN { <replaceable class="parameter">channel</replaceable> | <replaceable>pattern</replaceable> }
2525
</synopsis>
2626
</refsynopsisdiv>
2727

@@ -30,17 +30,18 @@ LISTEN <replaceable class="parameter">channel</replaceable>
3030

3131
<para>
3232
<command>LISTEN</command> registers the current session as a
33-
listener on the notification channel named <replaceable
34-
class="parameter">channel</replaceable>.
33+
listener on the notification channels named <replaceable
34+
class="parameter">channel</replaceable> or whose name match
35+
the <replaceable class="parameter">pattern</replaceable>.
3536
If the current session is already registered as a listener for
36-
this notification channel, nothing is done.
37+
these notification channels, nothing is done.
3738
</para>
3839

3940
<para>
4041
Whenever the command <command>NOTIFY <replaceable
4142
class="parameter">channel</replaceable></command> is invoked, either
4243
by this session or another one connected to the same database, all
43-
the sessions currently listening on that notification channel are
44+
the sessions currently listening on those notification channels are
4445
notified, and each will in turn notify its connected client
4546
application.
4647
</para>
@@ -77,6 +78,15 @@ LISTEN <replaceable class="parameter">channel</replaceable>
7778
</para>
7879
</listitem>
7980
</varlistentry>
81+
82+
<varlistentry>
83+
<term><replaceable class="parameter">pattern</replaceable></term>
84+
<listitem>
85+
<para>
86+
Pattern of notification channel names (<xref linkend="functions-like"/> expression).
87+
</para>
88+
</listitem>
89+
</varlistentry>
8090
</variablelist>
8191
</refsect1>
8292

@@ -130,6 +140,17 @@ LISTEN <replaceable class="parameter">channel</replaceable>
130140
LISTEN virtual;
131141
NOTIFY virtual;
132142
Asynchronous notification "virtual" received from server process with PID 8448.
143+
</programlisting></para>
144+
<para>
145+
Configure and execute a listen pattern from
146+
<application>psql</application>:
147+
148+
<programlisting>
149+
LISTEN 'virtual%';
150+
NOTIFY virtual0;
151+
Asynchronous notification "virtual0" received from server process with PID 8448.
152+
NOTIFY virtual1;
153+
Asynchronous notification "virtual1" received from server process with PID 8448.
133154
</programlisting></para>
134155
</refsect1>
135156

doc/src/sgml/ref/unlisten.sgml

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
UNLISTEN { <replaceable class="parameter">channel</replaceable> | * }
24+
UNLISTEN { <replaceable class="parameter">channel</replaceable> | <replaceable class="parameter">pattern</replaceable> | * }
2525
</synopsis>
2626
</refsynopsisdiv>
2727

@@ -33,10 +33,11 @@ UNLISTEN { <replaceable class="parameter">channel</replaceable> | * }
3333
registration for <command>NOTIFY</command> events.
3434
<command>UNLISTEN</command> cancels any existing registration of
3535
the current <productname>PostgreSQL</productname> session as a
36-
listener on the notification channel named <replaceable
37-
class="parameter">channel</replaceable>. The special wildcard
38-
<literal>*</literal> cancels all listener registrations for the
39-
current session.
36+
listener on the notification channels named <replaceable
37+
class="parameter">channel</replaceable> or whose name match
38+
the <replaceable class="parameter">pattern</replaceable>.
39+
The special wildcard <literal>*</literal> cancels all listener
40+
registrations for the current session.
4041
</para>
4142

4243
<para>
@@ -60,6 +61,15 @@ UNLISTEN { <replaceable class="parameter">channel</replaceable> | * }
6061
</listitem>
6162
</varlistentry>
6263

64+
<varlistentry>
65+
<term><replaceable class="parameter">pattern</replaceable></term>
66+
<listitem>
67+
<para>
68+
Pattern of notification channel names (<xref linkend="functions-like"/> expression).
69+
</para>
70+
</listitem>
71+
</varlistentry>
72+
6373
<varlistentry>
6474
<term><literal>*</literal></term>
6575
<listitem>

src/backend/commands/async.c

Lines changed: 90 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
#include "access/slru.h"
134134
#include "access/transam.h"
135135
#include "access/xact.h"
136+
#include "catalog/pg_collation.h"
136137
#include "catalog/pg_database.h"
137138
#include "commands/async.h"
138139
#include "common/hashfn.h"
@@ -312,6 +313,12 @@ static SlruCtlData NotifyCtlData;
312313

313314
#define QUEUE_FULL_WARN_INTERVAL 5000 /* warn at most once every 5s */
314315

316+
typedef struct
317+
{
318+
bool ispatt;
319+
char channel[FLEXIBLE_ARRAY_MEMBER]; /* nul-terminated string */
320+
} ListenChannel;
321+
315322
/*
316323
* listenChannels identifies the channels we are actually listening to
317324
* (ie, have committed a LISTEN on). It is a simple list of channel names,
@@ -339,6 +346,7 @@ typedef enum
339346
typedef struct
340347
{
341348
ListenActionKind action;
349+
bool ispatt;
342350
char channel[FLEXIBLE_ARRAY_MEMBER]; /* nul-terminated string */
343351
} ListenAction;
344352

@@ -430,13 +438,13 @@ int max_notify_queue_pages = 1048576;
430438
/* local function prototypes */
431439
static inline int64 asyncQueuePageDiff(int64 p, int64 q);
432440
static inline bool asyncQueuePagePrecedes(int64 p, int64 q);
433-
static void queue_listen(ListenActionKind action, const char *channel);
441+
static void queue_listen(ListenActionKind action, const bool ispatt, const char *channel);
434442
static void Async_UnlistenOnExit(int code, Datum arg);
435443
static void Exec_ListenPreCommit(void);
436-
static void Exec_ListenCommit(const char *channel);
437-
static void Exec_UnlistenCommit(const char *channel);
444+
static void Exec_ListenCommit(const bool ispatt, const char *channel);
445+
static void Exec_UnlistenCommit(const bool ispatt, const char *channel);
438446
static void Exec_UnlistenAllCommit(void);
439-
static bool IsListeningOn(const char *channel);
447+
static bool IsListeningOn(const bool trymatch, const bool ispatt, const char *channel);
440448
static void asyncQueueUnregister(void);
441449
static bool asyncQueueIsFull(void);
442450
static bool asyncQueueAdvance(volatile QueuePosition *position, int entryLength);
@@ -687,7 +695,7 @@ Async_Notify(const char *channel, const char *payload)
687695
* commit.
688696
*/
689697
static void
690-
queue_listen(ListenActionKind action, const char *channel)
698+
queue_listen(ListenActionKind action, const bool ispatt, const char *channel)
691699
{
692700
MemoryContext oldcontext;
693701
ListenAction *actrec;
@@ -705,6 +713,7 @@ queue_listen(ListenActionKind action, const char *channel)
705713
actrec = (ListenAction *) palloc(offsetof(ListenAction, channel) +
706714
strlen(channel) + 1);
707715
actrec->action = action;
716+
actrec->ispatt = ispatt;
708717
strcpy(actrec->channel, channel);
709718

710719
if (pendingActions == NULL || my_level > pendingActions->nestingLevel)
@@ -735,12 +744,12 @@ queue_listen(ListenActionKind action, const char *channel)
735744
* This is executed by the SQL listen command.
736745
*/
737746
void
738-
Async_Listen(const char *channel)
747+
Async_Listen(const bool ispatt, const char *channel)
739748
{
740749
if (Trace_notify)
741750
elog(DEBUG1, "Async_Listen(%s,%d)", channel, MyProcPid);
742751

743-
queue_listen(LISTEN_LISTEN, channel);
752+
queue_listen(LISTEN_LISTEN, ispatt, channel);
744753
}
745754

746755
/*
@@ -749,7 +758,7 @@ Async_Listen(const char *channel)
749758
* This is executed by the SQL unlisten command.
750759
*/
751760
void
752-
Async_Unlisten(const char *channel)
761+
Async_Unlisten(const bool ispatt, const char *channel)
753762
{
754763
if (Trace_notify)
755764
elog(DEBUG1, "Async_Unlisten(%s,%d)", channel, MyProcPid);
@@ -758,7 +767,7 @@ Async_Unlisten(const char *channel)
758767
if (pendingActions == NULL && !unlistenExitRegistered)
759768
return;
760769

761-
queue_listen(LISTEN_UNLISTEN, channel);
770+
queue_listen(LISTEN_UNLISTEN, ispatt, channel);
762771
}
763772

764773
/*
@@ -776,7 +785,7 @@ Async_UnlistenAll(void)
776785
if (pendingActions == NULL && !unlistenExitRegistered)
777786
return;
778787

779-
queue_listen(LISTEN_UNLISTEN_ALL, "");
788+
queue_listen(LISTEN_UNLISTEN_ALL, false, "");
780789
}
781790

782791
/*
@@ -803,10 +812,31 @@ pg_listening_channels(PG_FUNCTION_ARGS)
803812

804813
if (funcctx->call_cntr < list_length(listenChannels))
805814
{
806-
char *channel = (char *) list_nth(listenChannels,
807-
funcctx->call_cntr);
815+
ListenChannel *chnl;
816+
817+
chnl = (ListenChannel *)list_nth(listenChannels, funcctx->call_cntr);
818+
819+
if (chnl->ispatt)
820+
{
821+
Size plen;
822+
char *result;
823+
MemoryContext oldcontext;
824+
825+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
826+
827+
plen = strlen(chnl->channel);
828+
result = (char *)palloc(plen + 3);
829+
result[0] = '\'';
830+
memcpy(result + 1, chnl->channel, plen);
831+
result[plen + 1] = '\'';
832+
result[plen + 2] = '\0';
808833

809-
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(channel));
834+
MemoryContextSwitchTo(oldcontext);
835+
836+
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(result));
837+
}
838+
else
839+
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(chnl->channel));
810840
}
811841

812842
SRF_RETURN_DONE(funcctx);
@@ -989,10 +1019,10 @@ AtCommit_Notify(void)
9891019
switch (actrec->action)
9901020
{
9911021
case LISTEN_LISTEN:
992-
Exec_ListenCommit(actrec->channel);
1022+
Exec_ListenCommit(actrec->ispatt, actrec->channel);
9931023
break;
9941024
case LISTEN_UNLISTEN:
995-
Exec_UnlistenCommit(actrec->channel);
1025+
Exec_UnlistenCommit(actrec->ispatt, actrec->channel);
9961026
break;
9971027
case LISTEN_UNLISTEN_ALL:
9981028
Exec_UnlistenAllCommit();
@@ -1133,12 +1163,13 @@ Exec_ListenPreCommit(void)
11331163
* Add the channel to the list of channels we are listening on.
11341164
*/
11351165
static void
1136-
Exec_ListenCommit(const char *channel)
1166+
Exec_ListenCommit(const bool ispatt, const char *channel)
11371167
{
1138-
MemoryContext oldcontext;
1168+
MemoryContext oldcontext;
1169+
ListenChannel *chnl;
11391170

11401171
/* Do nothing if we are already listening on this channel */
1141-
if (IsListeningOn(channel))
1172+
if (IsListeningOn(false, ispatt, channel))
11421173
return;
11431174

11441175
/*
@@ -1150,7 +1181,15 @@ Exec_ListenCommit(const char *channel)
11501181
* later.
11511182
*/
11521183
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
1153-
listenChannels = lappend(listenChannels, pstrdup(channel));
1184+
1185+
chnl = (ListenChannel *) palloc(offsetof(ListenChannel, channel) +
1186+
strlen(channel) + 1);
1187+
1188+
chnl->ispatt = ispatt;
1189+
strcpy(chnl->channel, channel);
1190+
1191+
listenChannels = lappend(listenChannels, chnl);
1192+
11541193
MemoryContextSwitchTo(oldcontext);
11551194
}
11561195

@@ -1160,7 +1199,7 @@ Exec_ListenCommit(const char *channel)
11601199
* Remove the specified channel name from listenChannels.
11611200
*/
11621201
static void
1163-
Exec_UnlistenCommit(const char *channel)
1202+
Exec_UnlistenCommit(const bool ispatt, const char *channel)
11641203
{
11651204
ListCell *q;
11661205

@@ -1169,9 +1208,12 @@ Exec_UnlistenCommit(const char *channel)
11691208

11701209
foreach(q, listenChannels)
11711210
{
1172-
char *lchan = (char *) lfirst(q);
1211+
ListenChannel *lchan = (ListenChannel *) lfirst(q);
1212+
1213+
if (lchan->ispatt != ispatt)
1214+
continue;
11731215

1174-
if (strcmp(lchan, channel) == 0)
1216+
if (strcmp(lchan->channel, channel) == 0)
11751217
{
11761218
listenChannels = foreach_delete_current(listenChannels, q);
11771219
pfree(lchan);
@@ -1209,16 +1251,37 @@ Exec_UnlistenAllCommit(void)
12091251
* fairly short, though.
12101252
*/
12111253
static bool
1212-
IsListeningOn(const char *channel)
1254+
IsListeningOn(const bool trymatch, const bool ispatt, const char *channel)
12131255
{
12141256
ListCell *p;
12151257

12161258
foreach(p, listenChannels)
12171259
{
1218-
char *lchan = (char *) lfirst(p);
1260+
ListenChannel *lchan = (ListenChannel *) lfirst(p);
12191261

1220-
if (strcmp(lchan, channel) == 0)
1221-
return true;
1262+
if (trymatch)
1263+
{
1264+
Assert(!ispatt);
1265+
1266+
if (lchan->ispatt)
1267+
{
1268+
Datum s = PointerGetDatum(cstring_to_text(channel));
1269+
Datum p = PointerGetDatum(cstring_to_text(lchan->channel));
1270+
1271+
if (DatumGetBool(DirectFunctionCall2Coll(textlike, DEFAULT_COLLATION_OID, s, p)))
1272+
return true;
1273+
}
1274+
else if (strcmp(lchan->channel, channel) == 0)
1275+
return true;
1276+
}
1277+
else
1278+
{
1279+
if (ispatt == lchan->ispatt)
1280+
{
1281+
if (strcmp(lchan->channel, channel) == 0)
1282+
return true;
1283+
}
1284+
}
12221285
}
12231286
return false;
12241287
}
@@ -2071,7 +2134,7 @@ asyncQueueProcessPageEntries(volatile QueuePosition *current,
20712134
/* qe->data is the null-terminated channel name */
20722135
char *channel = qe->data;
20732136

2074-
if (IsListeningOn(channel))
2137+
if (IsListeningOn(true, false, channel))
20752138
{
20762139
/* payload follows channel name */
20772140
char *payload = qe->data + strlen(channel) + 1;

src/backend/parser/gram.y

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11062,6 +11062,14 @@ ListenStmt: LISTEN ColId
1106211062
{
1106311063
ListenStmt *n = makeNode(ListenStmt);
1106411064

11065+
n->conditionname = $2;
11066+
$$ = (Node *) n;
11067+
}
11068+
| LISTEN Sconst
11069+
{
11070+
ListenStmt *n = makeNode(ListenStmt);
11071+
11072+
n->ispatt = true;
1106511073
n->conditionname = $2;
1106611074
$$ = (Node *) n;
1106711075
}
@@ -11082,6 +11090,14 @@ UnlistenStmt:
1108211090
n->conditionname = NULL;
1108311091
$$ = (Node *) n;
1108411092
}
11093+
| UNLISTEN Sconst
11094+
{
11095+
UnlistenStmt *n = makeNode(UnlistenStmt);
11096+
11097+
n->ispatt = true;
11098+
n->conditionname = $2;
11099+
$$ = (Node *) n;
11100+
}
1108511101
;
1108611102

1108711103

0 commit comments

Comments
 (0)