/* * Async_Notify * * This is executed by the SQL notify command. * * Adds the relation to the list of pending notifies. * Actual notification happens during transaction commit. * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */ void Async_Notify(const char *relname) { if (Trace_notify) elog(DEBUG1, "Async_Notify(%s)", relname); /* no point in making duplicate entries in the list ... */ if (!AsyncExistsPendingNotify(relname)) { /* * The name list needs to live until end of transaction, so store it * in the transaction context. */ MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(CurTransactionContext); /* * Ordering of the list isn't important. We choose to put new entries * on the front, as this might make duplicate-elimination a tad faster * when the same condition is signaled many times in a row. */ pendingNotifies = lcons(pstrdup(relname), pendingNotifies); MemoryContextSwitchTo(oldcontext); } }
/* *-------------------------------------------------------------- * Async_Notify * * This is executed by the SQL notify command. * * Adds the relation to the list of pending notifies. * Actual notification happens during transaction commit. * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * *-------------------------------------------------------------- */ void Async_Notify(const char *relname) { if (Trace_notify) elog(DEBUG1, "Async_Notify(%s)", relname); /* no point in making duplicate entries in the list ... */ if (!AsyncExistsPendingNotify(relname)) { /* * The name list needs to live until end of transaction, so store it * in the transaction context. */ MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(CurTransactionContext); pendingNotifies = lcons(pstrdup(relname), pendingNotifies); MemoryContextSwitchTo(oldcontext); } }
/* *-------------------------------------------------------------- * AtCommit_Notify * * This is called at transaction commit. * * If there are outbound notify requests in the pendingNotifies list, * scan pg_listener for matching tuples, and either signal the other * backend or send a message to our own frontend. * * NOTE: we are still inside the current transaction, therefore can * piggyback on its committing of changes. * * Results: * XXX * * Side effects: * Tuples in pg_listener that have matching relnames and other peoples' * listenerPIDs are updated with a nonzero notification field. * *-------------------------------------------------------------- */ void AtCommit_Notify(void) { Relation lRel; TupleDesc tdesc; HeapScanDesc scan; HeapTuple lTuple, rTuple; Datum value[Natts_pg_listener]; char repl[Natts_pg_listener], nulls[Natts_pg_listener]; if (pendingNotifies == NIL) return; /* no NOTIFY statements in this transaction */ /* * NOTIFY is disabled if not normal processing mode. This test used to be * in xact.c, but it seems cleaner to do it here. */ if (!IsNormalProcessingMode()) { ClearPendingNotifies(); return; } if (Trace_notify) elog(DEBUG1, "AtCommit_Notify"); /* preset data to update notify column to MyProcPid */ nulls[0] = nulls[1] = nulls[2] = ' '; repl[0] = repl[1] = repl[2] = ' '; repl[Anum_pg_listener_notify - 1] = 'r'; value[0] = value[1] = value[2] = (Datum) 0; value[Anum_pg_listener_notify - 1] = Int32GetDatum(MyProcPid); lRel = heap_open(ListenerRelationId, ExclusiveLock); tdesc = RelationGetDescr(lRel); scan = heap_beginscan(lRel, SnapshotNow, 0, NULL); while ((lTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(lTuple); char *relname = NameStr(listener->relname); int32 listenerPID = listener->listenerpid; if (!AsyncExistsPendingNotify(relname)) continue; if (listenerPID == MyProcPid) { /* * Self-notify: no need to bother with table update. Indeed, we * *must not* clear the notification field in this path, or we * could lose an outside notify, which'd be bad for applications * that ignore self-notify messages. */ if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: notifying self"); NotifyMyFrontEnd(relname, listenerPID); } else { if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: notifying pid %d", listenerPID); /* * If someone has already notified this listener, we don't bother * modifying the table, but we do still send a SIGUSR2 signal, * just in case that backend missed the earlier signal for some * reason. It's OK to send the signal first, because the other * guy can't read pg_listener until we unlock it. */ if (kill(listenerPID, SIGUSR2) < 0) { /* * Get rid of pg_listener entry if it refers to a PID that no * longer exists. Presumably, that backend crashed without * deleting its pg_listener entries. This code used to only * delete the entry if errno==ESRCH, but as far as I can see * we should just do it for any failure (certainly at least * for EPERM too...) */ simple_heap_delete(lRel, &lTuple->t_self); } else if (listener->notification == 0) { HTSU_Result result; ItemPointerData update_ctid; TransactionId update_xmax; rTuple = heap_modifytuple(lTuple, tdesc, value, nulls, repl); /* * We cannot use simple_heap_update here because the tuple * could have been modified by an uncommitted transaction; * specifically, since UNLISTEN releases exclusive lock on the * table before commit, the other guy could already have tried * to unlisten. There are no other cases where we should be * able to see an uncommitted update or delete. Therefore, our * response to a HeapTupleBeingUpdated result is just to * ignore it. We do *not* wait for the other guy to commit * --- that would risk deadlock, and we don't want to block * while holding the table lock anyway for performance * reasons. We also ignore HeapTupleUpdated, which could occur * if the other guy commits between our heap_getnext and * heap_update calls. */ result = heap_update(lRel, &lTuple->t_self, rTuple, &update_ctid, &update_xmax, GetCurrentCommandId(), InvalidSnapshot, false /* no wait for commit */ ); switch (result) { case HeapTupleSelfUpdated: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; case HeapTupleMayBeUpdated: /* done successfully */ #ifdef NOT_USED /* currently there are no indexes */ CatalogUpdateIndexes(lRel, rTuple); #endif break; case HeapTupleBeingUpdated: /* ignore uncommitted tuples */ break; case HeapTupleUpdated: /* ignore just-committed tuples */ break; default: elog(ERROR, "unrecognized heap_update status: %u", result); break; } } } } heap_endscan(scan); /* * We do NOT release the lock on pg_listener here; we need to hold it * until end of transaction (which is about to happen, anyway) to ensure * that notified backends see our tuple updates when they look. Else they * might disregard the signal, which would make the application programmer * very unhappy. */ heap_close(lRel, NoLock); ClearPendingNotifies(); if (Trace_notify) elog(DEBUG1, "AtCommit_Notify: done"); }