mirror of
https://github.com/postgres/postgres.git
synced 2025-06-08 22:02:03 +03:00
From: Massimo Dal Zotto <dz@cs.unitn.it>
This commit is contained in:
parent
6f3de1bb66
commit
6c4982851a
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.37 1998/08/19 02:01:39 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.38 1998/08/30 21:04:43 scrappy Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -34,29 +34,7 @@
|
|||||||
* -- jw, 12/28/93
|
* -- jw, 12/28/93
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* The following is the old model which does not work.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Model is:
|
|
||||||
* 1. Multiple backends on same machine.
|
|
||||||
*
|
|
||||||
* 2. Query on one backend sends stuff over an asynchronous portal by
|
|
||||||
* appending to a relation, and then doing an async. notification
|
|
||||||
* (which takes place after commit) to all listeners on this relation.
|
|
||||||
*
|
|
||||||
* 3. Async. notification results in all backends listening on relation
|
|
||||||
* to be woken up, by a process signal kill(SIGUSR2), with name of relation
|
|
||||||
* passed in shared memory.
|
|
||||||
*
|
|
||||||
* 4. Each backend notifies its respective frontend over the comm
|
|
||||||
* channel using the out-of-band channel.
|
|
||||||
*
|
|
||||||
* 5. Each frontend receives this notification and processes accordingly.
|
|
||||||
*
|
|
||||||
* #4,#5 are changing soon with pending rewrite of portal/protocol.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -82,17 +60,28 @@
|
|||||||
#include "tcop/dest.h"
|
#include "tcop/dest.h"
|
||||||
#include "utils/mcxt.h"
|
#include "utils/mcxt.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
#include <utils/trace.h>
|
||||||
|
#include <utils/ps_status.h>
|
||||||
|
|
||||||
|
#define NotifyUnlock pg_options[OPT_NOTIFYUNLOCK]
|
||||||
|
#define NotifyHack pg_options[OPT_NOTIFYHACK]
|
||||||
|
|
||||||
|
extern TransactionState CurrentTransactionState;
|
||||||
|
extern CommandDest whereToSendOutput;
|
||||||
|
|
||||||
|
GlobalMemory notifyContext = NULL;
|
||||||
|
|
||||||
static int notifyFrontEndPending = 0;
|
static int notifyFrontEndPending = 0;
|
||||||
static int notifyIssued = 0;
|
static int notifyIssued = 0;
|
||||||
static Dllist *pendingNotifies = NULL;
|
static Dllist *pendingNotifies = NULL;
|
||||||
|
|
||||||
|
|
||||||
static int AsyncExistsPendingNotify(char *);
|
static int AsyncExistsPendingNotify(char *);
|
||||||
static void ClearPendingNotify(void);
|
static void ClearPendingNotify(void);
|
||||||
static void Async_NotifyFrontEnd(void);
|
static void Async_NotifyFrontEnd(void);
|
||||||
|
static void Async_NotifyFrontEnd_Aux(void);
|
||||||
void Async_Unlisten(char *relname, int pid);
|
void Async_Unlisten(char *relname, int pid);
|
||||||
static void Async_UnlistenOnExit(int code, char *relname);
|
static void Async_UnlistenOnExit(int code, char *relname);
|
||||||
|
static void Async_UnlistenAll(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------
|
*--------------------------------------------------------------
|
||||||
@ -116,33 +105,36 @@ static void Async_UnlistenOnExit(int code, char *relname);
|
|||||||
void
|
void
|
||||||
Async_NotifyHandler(SIGNAL_ARGS)
|
Async_NotifyHandler(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
extern TransactionState CurrentTransactionState;
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyHandler");
|
||||||
|
|
||||||
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
|
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
|
||||||
(CurrentTransactionState->blockState == TRANS_DEFAULT))
|
(CurrentTransactionState->blockState == TRANS_DEFAULT))
|
||||||
{
|
{
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyHandler: "
|
||||||
#ifdef ASYNC_DEBUG
|
"waking up sleeping backend process");
|
||||||
elog(DEBUG, "Waking up sleeping backend process");
|
PS_SET_STATUS("async_notify");
|
||||||
#endif
|
|
||||||
Async_NotifyFrontEnd();
|
Async_NotifyFrontEnd();
|
||||||
|
PS_SET_STATUS("idle");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef ASYNC_DEBUG
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyHandler: "
|
||||||
elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d",
|
"process in middle of transaction, state=%d, blockstate=%d",
|
||||||
CurrentTransactionState->state,
|
CurrentTransactionState->state,
|
||||||
CurrentTransactionState->blockState);
|
CurrentTransactionState->blockState);
|
||||||
#endif
|
|
||||||
notifyFrontEndPending = 1;
|
notifyFrontEndPending = 1;
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyHandler: notify frontend pending");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyHandler: done");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------
|
*--------------------------------------------------------------
|
||||||
* Async_Notify --
|
* Async_Notify --
|
||||||
*
|
*
|
||||||
|
* This is executed by the SQL notify command.
|
||||||
|
*
|
||||||
* Adds the relation to the list of pending notifies.
|
* Adds the relation to the list of pending notifies.
|
||||||
* All notification happens at end of commit.
|
* All notification happens at end of commit.
|
||||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -151,7 +143,6 @@ Async_NotifyHandler(SIGNAL_ARGS)
|
|||||||
* then each backend notifies its corresponding front end at
|
* then each backend notifies its corresponding front end at
|
||||||
* the end of commit.
|
* the end of commit.
|
||||||
*
|
*
|
||||||
* This correspond to 'notify <relname>' command
|
|
||||||
* -- jw, 12/28/93
|
* -- jw, 12/28/93
|
||||||
*
|
*
|
||||||
* Results:
|
* Results:
|
||||||
@ -180,9 +171,7 @@ Async_Notify(char *relname)
|
|||||||
|
|
||||||
char *notifyName;
|
char *notifyName;
|
||||||
|
|
||||||
#ifdef ASYNC_DEBUG
|
TPRINTF(TRACE_NOTIFY, "Async_Notify: %s", relname);
|
||||||
elog(DEBUG, "Async_Notify: %s", relname);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!pendingNotifies)
|
if (!pendingNotifies)
|
||||||
pendingNotifies = DLNewList();
|
pendingNotifies = DLNewList();
|
||||||
@ -217,18 +206,32 @@ Async_Notify(char *relname)
|
|||||||
{
|
{
|
||||||
rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl);
|
rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl);
|
||||||
heap_replace(lRel, &lTuple->t_ctid, rTuple);
|
heap_replace(lRel, &lTuple->t_ctid, rTuple);
|
||||||
|
/* notify is really issued only if a tuple has been changed */
|
||||||
|
notifyIssued = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
heap_endscan(sRel);
|
heap_endscan(sRel);
|
||||||
RelationUnsetLockForWrite(lRel);
|
|
||||||
|
/*
|
||||||
|
* Note: if the write lock is unset we can get multiple tuples
|
||||||
|
* with same oid if other backends notify the same relation.
|
||||||
|
* Use this option at your own risk.
|
||||||
|
*/
|
||||||
|
if (NotifyUnlock) {
|
||||||
|
RelationUnsetLockForWrite(lRel);
|
||||||
|
}
|
||||||
|
|
||||||
heap_close(lRel);
|
heap_close(lRel);
|
||||||
notifyIssued = 1;
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_Notify: done %s", relname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------
|
*--------------------------------------------------------------
|
||||||
* Async_NotifyAtCommit --
|
* Async_NotifyAtCommit --
|
||||||
*
|
*
|
||||||
|
* This is called at transaction commit.
|
||||||
|
*
|
||||||
* Signal our corresponding frontend process on relations that
|
* Signal our corresponding frontend process on relations that
|
||||||
* were notified. Signal all other backend process that
|
* were notified. Signal all other backend process that
|
||||||
* are listening also.
|
* are listening also.
|
||||||
@ -265,14 +268,12 @@ Async_NotifyAtCommit()
|
|||||||
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
|
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
|
||||||
(CurrentTransactionState->blockState == TRANS_DEFAULT))
|
(CurrentTransactionState->blockState == TRANS_DEFAULT))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (notifyIssued)
|
if (notifyIssued)
|
||||||
{ /* 'notify <relname>' issued by us */
|
{
|
||||||
|
/* 'notify <relname>' issued by us */
|
||||||
notifyIssued = 0;
|
notifyIssued = 0;
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
#ifdef ASYNC_DEBUG
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyAtCommit");
|
||||||
elog(DEBUG, "Async_NotifyAtCommit.");
|
|
||||||
#endif
|
|
||||||
ScanKeyEntryInitialize(&key, 0,
|
ScanKeyEntryInitialize(&key, 0,
|
||||||
Anum_pg_listener_notify,
|
Anum_pg_listener_notify,
|
||||||
F_INT4EQ,
|
F_INT4EQ,
|
||||||
@ -294,16 +295,15 @@ Async_NotifyAtCommit()
|
|||||||
|
|
||||||
if (MyProcPid == DatumGetInt32(d))
|
if (MyProcPid == DatumGetInt32(d))
|
||||||
{
|
{
|
||||||
#ifdef ASYNC_DEBUG
|
|
||||||
elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1");
|
|
||||||
#endif
|
|
||||||
notifyFrontEndPending = 1;
|
notifyFrontEndPending = 1;
|
||||||
|
TPRINTF(TRACE_NOTIFY,
|
||||||
|
"Async_NotifyAtCommit: notifying self");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef ASYNC_DEBUG
|
TPRINTF(TRACE_NOTIFY,
|
||||||
elog(DEBUG, "Notifying others");
|
"Async_NotifyAtCommit: notifying pid %d",
|
||||||
#endif
|
DatumGetInt32(d));
|
||||||
#ifdef HAVE_KILL
|
#ifdef HAVE_KILL
|
||||||
if (kill(DatumGetInt32(d), SIGUSR2) < 0)
|
if (kill(DatumGetInt32(d), SIGUSR2) < 0)
|
||||||
{
|
{
|
||||||
@ -315,19 +315,35 @@ Async_NotifyAtCommit()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
heap_endscan(sRel);
|
heap_endscan(sRel);
|
||||||
RelationUnsetLockForWrite(lRel);
|
|
||||||
heap_close(lRel);
|
heap_close(lRel);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify the frontend inside the current transaction while
|
||||||
|
* we still have a valid write lock on pg_listeners. This
|
||||||
|
* avoid waiting until all other backends have finished
|
||||||
|
* with pg_listener.
|
||||||
|
*/
|
||||||
|
if (notifyFrontEndPending) {
|
||||||
|
/* The aux version is called inside transaction */
|
||||||
|
Async_NotifyFrontEnd_Aux();
|
||||||
|
}
|
||||||
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyAtCommit: done");
|
||||||
CommitTransactionCommand();
|
CommitTransactionCommand();
|
||||||
ClearPendingNotify();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No notifies issued by us. If notifyFrontEndPending has been set
|
||||||
|
* by Async_NotifyHandler notify the frontend of pending notifies
|
||||||
|
* from other backends.
|
||||||
|
*/
|
||||||
|
if (notifyFrontEndPending) {
|
||||||
|
Async_NotifyFrontEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notifyFrontEndPending)
|
ClearPendingNotify();
|
||||||
{ /* we need to notify the frontend of all
|
|
||||||
* pending notifies. */
|
|
||||||
notifyFrontEndPending = 1;
|
|
||||||
Async_NotifyFrontEnd();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +351,8 @@ Async_NotifyAtCommit()
|
|||||||
*--------------------------------------------------------------
|
*--------------------------------------------------------------
|
||||||
* Async_NotifyAtAbort --
|
* Async_NotifyAtAbort --
|
||||||
*
|
*
|
||||||
|
* This is called at transaction commit.
|
||||||
|
*
|
||||||
* Gets rid of pending notifies. List elements are automatically
|
* Gets rid of pending notifies. List elements are automatically
|
||||||
* freed through memory context.
|
* freed through memory context.
|
||||||
*
|
*
|
||||||
@ -350,20 +368,19 @@ Async_NotifyAtCommit()
|
|||||||
void
|
void
|
||||||
Async_NotifyAtAbort()
|
Async_NotifyAtAbort()
|
||||||
{
|
{
|
||||||
extern TransactionState CurrentTransactionState;
|
if (pendingNotifies) {
|
||||||
|
|
||||||
if (notifyIssued)
|
|
||||||
ClearPendingNotify();
|
ClearPendingNotify();
|
||||||
notifyIssued = 0;
|
|
||||||
if (pendingNotifies)
|
|
||||||
DLFreeList(pendingNotifies);
|
DLFreeList(pendingNotifies);
|
||||||
|
}
|
||||||
pendingNotifies = DLNewList();
|
pendingNotifies = DLNewList();
|
||||||
|
notifyIssued = 0;
|
||||||
|
|
||||||
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
|
if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
|
||||||
(CurrentTransactionState->blockState == TRANS_DEFAULT))
|
(CurrentTransactionState->blockState == TRANS_DEFAULT))
|
||||||
{
|
{
|
||||||
|
/* don't forget to notify front end */
|
||||||
if (notifyFrontEndPending)
|
if (notifyFrontEndPending)
|
||||||
{ /* don't forget to notify front end */
|
{
|
||||||
Async_NotifyFrontEnd();
|
Async_NotifyFrontEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -373,11 +390,11 @@ Async_NotifyAtAbort()
|
|||||||
*--------------------------------------------------------------
|
*--------------------------------------------------------------
|
||||||
* Async_Listen --
|
* Async_Listen --
|
||||||
*
|
*
|
||||||
|
* This is executed by the SQL listen command.
|
||||||
|
*
|
||||||
* Register a backend (identified by its Unix PID) as listening
|
* Register a backend (identified by its Unix PID) as listening
|
||||||
* on the specified relation.
|
* on the specified relation.
|
||||||
*
|
*
|
||||||
* This corresponds to the 'listen <relation>' command in SQL
|
|
||||||
*
|
|
||||||
* One listener per relation, pg_listener relation is keyed
|
* One listener per relation, pg_listener relation is keyed
|
||||||
* on (relname,pid) to provide multiple listeners in future.
|
* on (relname,pid) to provide multiple listeners in future.
|
||||||
*
|
*
|
||||||
@ -406,9 +423,13 @@ Async_Listen(char *relname, int pid)
|
|||||||
char *relnamei;
|
char *relnamei;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
|
|
||||||
#ifdef ASYNC_DEBUG
|
if (whereToSendOutput != Remote) {
|
||||||
elog(DEBUG, "Async_Listen: %s", relname);
|
elog(NOTICE, "Async_Listen: "
|
||||||
#endif
|
"listen not available on interactive sessions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_Listen: %s", relname);
|
||||||
for (i = 0; i < Natts_pg_listener; i++)
|
for (i = 0; i < Natts_pg_listener; i++)
|
||||||
{
|
{
|
||||||
nulls[i] = ' ';
|
nulls[i] = ' ';
|
||||||
@ -438,6 +459,10 @@ Async_Listen(char *relname, int pid)
|
|||||||
if (pid == MyProcPid)
|
if (pid == MyProcPid)
|
||||||
alreadyListener = 1;
|
alreadyListener = 1;
|
||||||
}
|
}
|
||||||
|
if (alreadyListener) {
|
||||||
|
/* No need to scan the rest of the table */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
heap_endscan(scan);
|
heap_endscan(scan);
|
||||||
|
|
||||||
@ -445,15 +470,14 @@ Async_Listen(char *relname, int pid)
|
|||||||
{
|
{
|
||||||
elog(NOTICE, "Async_Listen: We are already listening on %s",
|
elog(NOTICE, "Async_Listen: We are already listening on %s",
|
||||||
relname);
|
relname);
|
||||||
|
RelationUnsetLockForWrite(lDesc);
|
||||||
|
heap_close(lDesc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tupDesc = lDesc->rd_att;
|
tupDesc = lDesc->rd_att;
|
||||||
newtup = heap_formtuple(tupDesc,
|
newtup = heap_formtuple(tupDesc, values, nulls);
|
||||||
values,
|
|
||||||
nulls);
|
|
||||||
heap_insert(lDesc, newtup);
|
heap_insert(lDesc, newtup);
|
||||||
|
|
||||||
pfree(newtup);
|
pfree(newtup);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -477,12 +501,11 @@ Async_Listen(char *relname, int pid)
|
|||||||
*--------------------------------------------------------------
|
*--------------------------------------------------------------
|
||||||
* Async_Unlisten --
|
* Async_Unlisten --
|
||||||
*
|
*
|
||||||
|
* This is executed by the SQL unlisten command.
|
||||||
|
*
|
||||||
* Remove the backend from the list of listening backends
|
* Remove the backend from the list of listening backends
|
||||||
* for the specified relation.
|
* for the specified relation.
|
||||||
*
|
*
|
||||||
* This would correspond to the 'unlisten <relation>'
|
|
||||||
* command, but there isn't one yet.
|
|
||||||
*
|
|
||||||
* Results:
|
* Results:
|
||||||
* pg_listeners is updated.
|
* pg_listeners is updated.
|
||||||
*
|
*
|
||||||
@ -497,20 +520,81 @@ Async_Unlisten(char *relname, int pid)
|
|||||||
Relation lDesc;
|
Relation lDesc;
|
||||||
HeapTuple lTuple;
|
HeapTuple lTuple;
|
||||||
|
|
||||||
lTuple = SearchSysCacheTuple(LISTENREL,
|
/* Handle specially the `unlisten "*"' command */
|
||||||
PointerGetDatum(relname),
|
if ((!relname) || (*relname == '\0') || (strcmp(relname,"*")==0)) {
|
||||||
|
Async_UnlistenAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_Unlisten %s", relname);
|
||||||
|
|
||||||
|
lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname),
|
||||||
Int32GetDatum(pid),
|
Int32GetDatum(pid),
|
||||||
0, 0);
|
0, 0);
|
||||||
lDesc = heap_openr(ListenerRelationName);
|
|
||||||
RelationSetLockForWrite(lDesc);
|
|
||||||
|
|
||||||
if (lTuple != NULL)
|
if (lTuple != NULL)
|
||||||
|
{
|
||||||
|
lDesc = heap_openr(ListenerRelationName);
|
||||||
|
RelationSetLockForWrite(lDesc);
|
||||||
heap_delete(lDesc, &lTuple->t_ctid);
|
heap_delete(lDesc, &lTuple->t_ctid);
|
||||||
|
RelationUnsetLockForWrite(lDesc);
|
||||||
RelationUnsetLockForWrite(lDesc);
|
heap_close(lDesc);
|
||||||
heap_close(lDesc);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------
|
||||||
|
* Async_UnlistenAll --
|
||||||
|
*
|
||||||
|
* Unlisten all relations for this backend.
|
||||||
|
*
|
||||||
|
* Results:
|
||||||
|
* pg_listeners is updated.
|
||||||
|
*
|
||||||
|
* Side effects:
|
||||||
|
* XXX
|
||||||
|
*
|
||||||
|
*--------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
Async_UnlistenAll()
|
||||||
|
{
|
||||||
|
HeapTuple lTuple;
|
||||||
|
Relation lRel;
|
||||||
|
HeapScanDesc sRel;
|
||||||
|
TupleDesc tdesc;
|
||||||
|
ScanKeyData key[1];
|
||||||
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_UnlistenAll");
|
||||||
|
ScanKeyEntryInitialize(&key[0], 0,
|
||||||
|
Anum_pg_listener_pid,
|
||||||
|
F_INT4EQ,
|
||||||
|
Int32GetDatum(MyProcPid));
|
||||||
|
lRel = heap_openr(ListenerRelationName);
|
||||||
|
RelationSetLockForWrite(lRel);
|
||||||
|
tdesc = RelationGetTupleDescriptor(lRel);
|
||||||
|
sRel = heap_beginscan(lRel, 0, SnapshotNow, 1, key);
|
||||||
|
|
||||||
|
while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0)))
|
||||||
|
{
|
||||||
|
heap_delete(lRel, &lTuple->t_ctid);
|
||||||
|
}
|
||||||
|
heap_endscan(sRel);
|
||||||
|
RelationUnsetLockForWrite(lRel);
|
||||||
|
heap_close(lRel);
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_UnlistenAll: done");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* --------------------------------------------------------------
|
||||||
|
* Async_UnlistenOnExit --
|
||||||
|
*
|
||||||
|
* This is called at backend exit for each registered listen.
|
||||||
|
*
|
||||||
|
* Results:
|
||||||
|
* XXX
|
||||||
|
*
|
||||||
|
* --------------------------------------------------------------
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
Async_UnlistenOnExit(int code, /* from exitpg */
|
Async_UnlistenOnExit(int code, /* from exitpg */
|
||||||
char *relname)
|
char *relname)
|
||||||
@ -522,6 +606,25 @@ Async_UnlistenOnExit(int code, /* from exitpg */
|
|||||||
* --------------------------------------------------------------
|
* --------------------------------------------------------------
|
||||||
* Async_NotifyFrontEnd --
|
* Async_NotifyFrontEnd --
|
||||||
*
|
*
|
||||||
|
* This is called outside transactions. The real work is done
|
||||||
|
* by Async_NotifyFrontEnd_Aux().
|
||||||
|
*
|
||||||
|
* --------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
Async_NotifyFrontEnd()
|
||||||
|
{
|
||||||
|
StartTransactionCommand();
|
||||||
|
Async_NotifyFrontEnd_Aux();
|
||||||
|
CommitTransactionCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* --------------------------------------------------------------
|
||||||
|
* Async_NotifyFrontEnd_Aux --
|
||||||
|
*
|
||||||
|
* This must be called inside a transaction block.
|
||||||
|
*
|
||||||
* Perform an asynchronous notification to front end over
|
* Perform an asynchronous notification to front end over
|
||||||
* portal comm channel. The name of the relation which contains the
|
* portal comm channel. The name of the relation which contains the
|
||||||
* data is sent to the front end.
|
* data is sent to the front end.
|
||||||
@ -534,12 +637,9 @@ Async_UnlistenOnExit(int code, /* from exitpg */
|
|||||||
*
|
*
|
||||||
* --------------------------------------------------------------
|
* --------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
GlobalMemory notifyContext = NULL;
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Async_NotifyFrontEnd()
|
Async_NotifyFrontEnd_Aux()
|
||||||
{
|
{
|
||||||
extern CommandDest whereToSendOutput;
|
|
||||||
HeapTuple lTuple,
|
HeapTuple lTuple,
|
||||||
rTuple;
|
rTuple;
|
||||||
Relation lRel;
|
Relation lRel;
|
||||||
@ -552,12 +652,15 @@ Async_NotifyFrontEnd()
|
|||||||
nulls[3];
|
nulls[3];
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
|
#define MAX_DONE 64
|
||||||
|
|
||||||
|
char *done[MAX_DONE];
|
||||||
|
int ndone = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
notifyFrontEndPending = 0;
|
notifyFrontEndPending = 0;
|
||||||
|
|
||||||
#ifdef ASYNC_DEBUG
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyFrontEnd");
|
||||||
elog(DEBUG, "Async_NotifyFrontEnd: notifying front end.");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
StartTransactionCommand();
|
StartTransactionCommand();
|
||||||
ScanKeyEntryInitialize(&key[0], 0,
|
ScanKeyEntryInitialize(&key[0], 0,
|
||||||
Anum_pg_listener_notify,
|
Anum_pg_listener_notify,
|
||||||
@ -580,11 +683,35 @@ Async_NotifyFrontEnd()
|
|||||||
|
|
||||||
while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0)))
|
while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0)))
|
||||||
{
|
{
|
||||||
d = heap_getattr(lTuple, Anum_pg_listener_relname, tdesc, &isnull);
|
d = heap_getattr(lTuple, Anum_pg_listener_relname, tdesc,
|
||||||
|
&isnull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This hack deletes duplicate tuples which can be left
|
||||||
|
* in the table if the NotifyUnlock option is set.
|
||||||
|
* I'm further investigating this. -- dz
|
||||||
|
*/
|
||||||
|
if (NotifyHack) {
|
||||||
|
for (i=0; i<ndone; i++) {
|
||||||
|
if (strcmp(DatumGetName(d)->data, done[i]) == 0) {
|
||||||
|
TPRINTF(TRACE_NOTIFY,
|
||||||
|
"Async_NotifyFrontEnd: duplicate %s",
|
||||||
|
DatumGetName(d)->data);
|
||||||
|
heap_delete(lRel, &lTuple->t_ctid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ndone < MAX_DONE) {
|
||||||
|
done[ndone++] = pstrdup(DatumGetName(d)->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl);
|
rTuple = heap_modifytuple(lTuple, lRel, value, nulls, repl);
|
||||||
heap_replace(lRel, &lTuple->t_ctid, rTuple);
|
heap_replace(lRel, &lTuple->t_ctid, rTuple);
|
||||||
|
|
||||||
/* notifying the front end */
|
/* notifying the front end */
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyFrontEnd: notifying %s",
|
||||||
|
DatumGetName(d)->data);
|
||||||
|
|
||||||
if (whereToSendOutput == Remote)
|
if (whereToSendOutput == Remote)
|
||||||
{
|
{
|
||||||
@ -593,12 +720,12 @@ Async_NotifyFrontEnd()
|
|||||||
pq_putstr(DatumGetName(d)->data);
|
pq_putstr(DatumGetName(d)->data);
|
||||||
pq_flush();
|
pq_flush();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions");
|
|
||||||
}
|
}
|
||||||
heap_endscan(sRel);
|
heap_endscan(sRel);
|
||||||
|
RelationUnsetLockForWrite(lRel);
|
||||||
heap_close(lRel);
|
heap_close(lRel);
|
||||||
CommitTransactionCommand();
|
|
||||||
|
TPRINTF(TRACE_NOTIFY, "Async_NotifyFrontEnd: done");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.86 1998/08/25 21:34:04 scrappy Exp $
|
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.87 1998/08/30 21:05:27 scrappy Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -443,11 +443,41 @@ pg_parse_and_plan(char *query_string, /* string to execute */
|
|||||||
|
|
||||||
querytree = querytree_list->qtrees[i];
|
querytree = querytree_list->qtrees[i];
|
||||||
|
|
||||||
if (DebugPrintQuery == true)
|
if (DebugPrintQuery)
|
||||||
{
|
{
|
||||||
printf("\n---- \tquery is:\n%s\n", query_string);
|
if (DebugPrintQuery > 3) {
|
||||||
printf("\n");
|
/* Print the query string as is if query debug level > 3 */
|
||||||
fflush(stdout);
|
TPRINTF(TRACE_QUERY, "query: %s",query_string);
|
||||||
|
} else {
|
||||||
|
/* Print condensed query string to fit in one log line */
|
||||||
|
char buff[8192+1];
|
||||||
|
char c,
|
||||||
|
*s,
|
||||||
|
*d;
|
||||||
|
int n,
|
||||||
|
is_space=1;
|
||||||
|
|
||||||
|
for (s=query_string,d=buff,n=0; (c=*s) && (n<8192); s++) {
|
||||||
|
switch (c) {
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
case '\t':
|
||||||
|
c = ' ';
|
||||||
|
/* fall through */
|
||||||
|
case ' ':
|
||||||
|
if (is_space) continue;
|
||||||
|
is_space = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
is_space = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*d++ = c;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
TPRINTF(TRACE_QUERY, "query: %s",buff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* don't rewrite utilites */
|
/* don't rewrite utilites */
|
||||||
@ -457,11 +487,10 @@ pg_parse_and_plan(char *query_string, /* string to execute */
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DebugPrintParse == true)
|
if (DebugPrintParse)
|
||||||
{
|
{
|
||||||
printf("\n---- \tparser outputs :\n");
|
TPRINTF(TRACE_PARSE, "parser outputs:");
|
||||||
nodeDisplay(querytree);
|
nodeDisplay(querytree);
|
||||||
printf("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rewrite queries (retrieve, append, delete, replace) */
|
/* rewrite queries (retrieve, append, delete, replace) */
|
||||||
@ -906,9 +935,11 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
char firstchar;
|
char firstchar;
|
||||||
char parser_input[MAX_PARSE_BUFFER];
|
char parser_input[MAX_PARSE_BUFFER];
|
||||||
char *userName;
|
char *userName;
|
||||||
char *remote_info;
|
|
||||||
char *remote_host;
|
/* Used if verbose is set, must be initialized */
|
||||||
unsigned short remote_port = 0;
|
char *remote_info = "interactive";
|
||||||
|
char *remote_host = "";
|
||||||
|
unsigned short remote_port = 0;
|
||||||
|
|
||||||
char *DBDate = NULL;
|
char *DBDate = NULL;
|
||||||
extern int optind;
|
extern int optind;
|
||||||
@ -1490,7 +1521,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
|
|||||||
if (!IsUnderPostmaster)
|
if (!IsUnderPostmaster)
|
||||||
{
|
{
|
||||||
puts("\nPOSTGRES backend interactive interface");
|
puts("\nPOSTGRES backend interactive interface");
|
||||||
puts("$Revision: 1.86 $ $Date: 1998/08/25 21:34:04 $");
|
puts("$Revision: 1.87 $ $Date: 1998/08/30 21:05:27 $");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.\" This is -*-nroff-*-
|
.\" This is -*-nroff-*-
|
||||||
.\" XXX standard disclaimer belongs here....
|
.\" XXX standard disclaimer belongs here....
|
||||||
.\" $Header: /cvsroot/pgsql/src/man/Attic/create_sequence.l,v 1.5 1998/07/14 01:45:25 momjian Exp $
|
.\" $Header: /cvsroot/pgsql/src/man/Attic/create_sequence.l,v 1.6 1998/08/30 21:03:19 scrappy Exp $
|
||||||
.TH "CREATE SEQUENCE" SQL 07/13/98 PostgreSQL PostgreSQL
|
.TH "CREATE SEQUENCE" SQL 07/13/98 PostgreSQL PostgreSQL
|
||||||
.SH NAME
|
.SH NAME
|
||||||
create sequence - create a new sequence number generator
|
create sequence - create a new sequence number generator
|
||||||
@ -82,6 +82,14 @@ given sequence in the current backend session. Also beware that it
|
|||||||
does not give the last number ever allocated, only the last one allocated
|
does not give the last number ever allocated, only the last one allocated
|
||||||
by this backend.
|
by this backend.
|
||||||
.PP
|
.PP
|
||||||
|
The function
|
||||||
|
.BR setval
|
||||||
|
('sequence_name', value)
|
||||||
|
may be used to set the current value of the specified sequence.
|
||||||
|
The next call to
|
||||||
|
.BR nextval
|
||||||
|
will return the given value + the sequence increment.
|
||||||
|
.PP
|
||||||
Use a query like
|
Use a query like
|
||||||
.nf
|
.nf
|
||||||
SELECT * FROM <sequence_name>;
|
SELECT * FROM <sequence_name>;
|
||||||
@ -134,6 +142,15 @@ select nextval ('seq');
|
|||||||
-- Use sequence in insert
|
-- Use sequence in insert
|
||||||
--
|
--
|
||||||
insert into table _table_ values (nextval ('seq'),...);
|
insert into table _table_ values (nextval ('seq'),...);
|
||||||
|
.nf
|
||||||
|
--
|
||||||
|
-- Set the sequence value after a copy in
|
||||||
|
--
|
||||||
|
create function table_id_max() returns int4
|
||||||
|
as 'select max(id) from _table_'
|
||||||
|
language 'sql';
|
||||||
|
copy _table_ from 'input_file';
|
||||||
|
select setval('seq', table_id_max());
|
||||||
.fi
|
.fi
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
drop_sequence(l).
|
drop_sequence(l).
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.\" This is -*-nroff-*-
|
.\" This is -*-nroff-*-
|
||||||
.\" XXX standard disclaimer belongs here....
|
.\" XXX standard disclaimer belongs here....
|
||||||
.\" $Header: /cvsroot/pgsql/src/man/Attic/listen.l,v 1.7 1998/07/09 03:29:09 scrappy Exp $
|
.\" $Header: /cvsroot/pgsql/src/man/Attic/listen.l,v 1.8 1998/08/30 21:03:20 scrappy Exp $
|
||||||
.TH "LISTEN" SQL 03/12/94 PostgreSQL PostgreSQL
|
.TH "LISTEN" SQL 03/12/94 PostgreSQL PostgreSQL
|
||||||
.SH NAME
|
.SH NAME
|
||||||
listen - listen for notification on a relation
|
listen - listen for notification on a relation
|
||||||
@ -27,16 +27,19 @@ in order to find out the name of the class to which a given
|
|||||||
notification corresponds. If this code is not included in
|
notification corresponds. If this code is not included in
|
||||||
the application, the event notification will be queued and
|
the application, the event notification will be queued and
|
||||||
never be processed.
|
never be processed.
|
||||||
|
.PP
|
||||||
|
Note that
|
||||||
|
.IR class_name
|
||||||
|
needs not to be a valid class name but can be any ascii string up to 32
|
||||||
|
characters long. It must however be eclosed in double-quotes if it is
|
||||||
|
not valid as class name.
|
||||||
.SH "SEE ALSO"
|
.SH "SEE ALSO"
|
||||||
create_rule(l),
|
create_rule(l),
|
||||||
notify(l),
|
notify(l),
|
||||||
select(l),
|
select(l),
|
||||||
|
unlisten(l),
|
||||||
libpq.
|
libpq.
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
There is no way to un-\c
|
|
||||||
.BR listen
|
|
||||||
except to drop the connection (i.e., restart the backend server).
|
|
||||||
.PP
|
|
||||||
The
|
The
|
||||||
.IR psql(1)
|
.IR psql(1)
|
||||||
command does not poll for asynchronous events.
|
command does not poll for asynchronous events.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user