Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions doc/src/sgml/func.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -15446,6 +15446,10 @@ table2-mapping
<para>
<literal>json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]')</literal>
<returnvalue>5</returnvalue>
</para>
<para>
<literal>jsonb_array_length('[]')</literal>
<returnvalue>0</returnvalue>
</para></entry>
</row>

Expand Down Expand Up @@ -17887,10 +17891,19 @@ SELECT NULLIF(value, '(none)') ...
</para>
<para>
Returns the length of the requested array dimension.
(Produces NULL instead of 0 for empty or missing array dimensions.)
</para>
<para>
<literal>array_length(array[1,2,3], 1)</literal>
<returnvalue>3</returnvalue>
</para>
<para>
<literal>array_length(array[]::int[], 1)</literal>
<returnvalue>NULL</returnvalue>
</para>
<para>
<literal>array_length(array['text'], 2)</literal>
<returnvalue>NULL</returnvalue>
</para></entry>
</row>

Expand Down
4 changes: 2 additions & 2 deletions doc/src/sgml/json.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -701,10 +701,10 @@ UPDATE table_name SET jsonb_field[2] = '2';
assigned value can be placed.

<programlisting>
-- Where jsonb_field was {}, it is now {'a': [{'b': 1}]}
-- Where jsonb_field was {}, it is now {"a": [{"b": 1}]}
UPDATE table_name SET jsonb_field['a'][0]['b'] = '1';

-- Where jsonb_field was [], it is now [null, {'a': 1}]
-- Where jsonb_field was [], it is now [null, {"a": 1}]
UPDATE table_name SET jsonb_field[1]['a'] = '1';
</programlisting>

Expand Down
13 changes: 9 additions & 4 deletions src/backend/access/transam/transam.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,15 @@ TransactionIdDidAbortForReader(TransactionId transactionId)
*
* This does NOT look into pg_xact but merely probes our local cache
* (and so it's not named TransactionIdDidComplete, which would be the
* appropriate name for a function that worked that way). The intended
* use is just to short-circuit TransactionIdIsInProgress calls when doing
* repeated heapam_visibility.c checks for the same XID. If this isn't
* extremely fast then it will be counterproductive.
* appropriate name for a function that worked that way).
*
* NB: This is unused, and will be removed in v15. This was used to
* short-circuit TransactionIdIsInProgress, but that was wrong for a
* transaction that was known to be marked as committed in CLOG but not
* yet removed from the proc array. This is kept in backbranches just in
* case it is still used by extensions. However, extensions doing
* something similar to tuple visibility checks should also be careful to
* check the proc array first!
*
* Note:
* Assumes transaction identifier is valid.
Expand Down
58 changes: 40 additions & 18 deletions src/backend/storage/ipc/latch.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
#if defined(WAIT_USE_EPOLL) || defined(WAIT_USE_POLL) || \
defined(WAIT_USE_KQUEUE) || defined(WAIT_USE_WIN32)
/* don't overwrite manual choice */
#elif defined(HAVE_SYS_EPOLL_H) && defined(HAVE_SYS_SIGNALFD_H)
#elif defined(HAVE_SYS_EPOLL_H)
#define WAIT_USE_EPOLL
#elif defined(HAVE_KQUEUE)
#define WAIT_USE_KQUEUE
Expand All @@ -84,6 +84,22 @@
#error "no wait set implementation available"
#endif

/*
* By default, we use a self-pipe with poll() and a signalfd with epoll(), if
* available. We avoid signalfd on illumos for now based on problem reports.
* For testing the choice can also be manually specified.
*/
#if defined(WAIT_USE_POLL) || defined(WAIT_USE_EPOLL)
#if defined(WAIT_USE_SELF_PIPE) || defined(WAIT_USE_SIGNALFD)
/* don't overwrite manual choice */
#elif defined(WAIT_USE_EPOLL) && defined(HAVE_SYS_SIGNALFD_H) && \
!defined(__illumos__)
#define WAIT_USE_SIGNALFD
#else
#define WAIT_USE_SELF_PIPE
#endif
#endif

/* typedef in latch.h */
struct WaitEventSet
{
Expand Down Expand Up @@ -146,12 +162,12 @@ static WaitEventSet *LatchWaitSet;
static volatile sig_atomic_t waiting = false;
#endif

#ifdef WAIT_USE_EPOLL
#ifdef WAIT_USE_SIGNALFD
/* On Linux, we'll receive SIGURG via a signalfd file descriptor. */
static int signal_fd = -1;
#endif

#if defined(WAIT_USE_POLL)
#ifdef WAIT_USE_SELF_PIPE
/* Read and write ends of the self-pipe */
static int selfpipe_readfd = -1;
static int selfpipe_writefd = -1;
Expand All @@ -164,7 +180,7 @@ static void latch_sigurg_handler(SIGNAL_ARGS);
static void sendSelfPipeByte(void);
#endif

#if defined(WAIT_USE_POLL) || defined(WAIT_USE_EPOLL)
#if defined(WAIT_USE_SELF_PIPE) || defined(WAIT_USE_SIGNALFD)
static void drain(void);
#endif

Expand All @@ -190,7 +206,7 @@ static inline int WaitEventSetWaitBlock(WaitEventSet *set, int cur_timeout,
void
InitializeLatchSupport(void)
{
#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)
int pipefd[2];

if (IsUnderPostmaster)
Expand Down Expand Up @@ -264,7 +280,7 @@ InitializeLatchSupport(void)
pqsignal(SIGURG, latch_sigurg_handler);
#endif

#ifdef WAIT_USE_EPOLL
#ifdef WAIT_USE_SIGNALFD
sigset_t signalfd_mask;

/* Block SIGURG, because we'll receive it through a signalfd. */
Expand Down Expand Up @@ -316,15 +332,15 @@ ShutdownLatchSupport(void)
LatchWaitSet = NULL;
}

#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)
close(selfpipe_readfd);
close(selfpipe_writefd);
selfpipe_readfd = -1;
selfpipe_writefd = -1;
selfpipe_owner_pid = InvalidPid;
#endif

#if defined(WAIT_USE_EPOLL)
#if defined(WAIT_USE_SIGNALFD)
close(signal_fd);
signal_fd = -1;
#endif
Expand All @@ -341,9 +357,12 @@ InitLatch(Latch *latch)
latch->owner_pid = MyProcPid;
latch->is_shared = false;

#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)
/* Assert InitializeLatchSupport has been called in this process */
Assert(selfpipe_readfd >= 0 && selfpipe_owner_pid == MyProcPid);
#elif defined(WAIT_USE_SIGNALFD)
/* Assert InitializeLatchSupport has been called in this process */
Assert(signal_fd >= 0);
#elif defined(WAIT_USE_WIN32)
latch->event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (latch->event == NULL)
Expand Down Expand Up @@ -405,9 +424,12 @@ OwnLatch(Latch *latch)
/* Sanity checks */
Assert(latch->is_shared);

#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)
/* Assert InitializeLatchSupport has been called in this process */
Assert(selfpipe_readfd >= 0 && selfpipe_owner_pid == MyProcPid);
#elif defined(WAIT_USE_SIGNALFD)
/* Assert InitializeLatchSupport has been called in this process */
Assert(signal_fd >= 0);
#endif

if (latch->owner_pid != 0)
Expand Down Expand Up @@ -618,7 +640,7 @@ SetLatch(Latch *latch)
return;
else if (owner_pid == MyProcPid)
{
#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)
if (waiting)
sendSelfPipeByte();
#else
Expand Down Expand Up @@ -983,9 +1005,9 @@ AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch,
{
set->latch = latch;
set->latch_pos = event->pos;
#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)
event->fd = selfpipe_readfd;
#elif defined(WAIT_USE_EPOLL)
#elif defined(WAIT_USE_SIGNALFD)
event->fd = signal_fd;
#else
event->fd = PGINVALID_SOCKET;
Expand Down Expand Up @@ -2102,7 +2124,7 @@ GetNumRegisteredWaitEvents(WaitEventSet *set)
return set->nevents;
}

#if defined(WAIT_USE_POLL)
#if defined(WAIT_USE_SELF_PIPE)

/*
* SetLatch uses SIGURG to wake up the process waiting on the latch.
Expand Down Expand Up @@ -2153,7 +2175,7 @@ sendSelfPipeByte(void)

#endif

#if defined(WAIT_USE_POLL) || defined(WAIT_USE_EPOLL)
#if defined(WAIT_USE_SELF_PIPE) || defined(WAIT_USE_SIGNALFD)

/*
* Read all available data from self-pipe or signalfd.
Expand All @@ -2169,7 +2191,7 @@ drain(void)
int rc;
int fd;

#ifdef WAIT_USE_POLL
#ifdef WAIT_USE_SELF_PIPE
fd = selfpipe_readfd;
#else
fd = signal_fd;
Expand All @@ -2187,7 +2209,7 @@ drain(void)
else
{
waiting = false;
#ifdef WAIT_USE_POLL
#ifdef WAIT_USE_SELF_PIPE
elog(ERROR, "read() on self-pipe failed: %m");
#else
elog(ERROR, "read() on signalfd failed: %m");
Expand All @@ -2197,7 +2219,7 @@ drain(void)
else if (rc == 0)
{
waiting = false;
#ifdef WAIT_USE_POLL
#ifdef WAIT_USE_SELF_PIPE
elog(ERROR, "unexpected EOF on self-pipe");
#else
elog(ERROR, "unexpected EOF on signalfd");
Expand Down
12 changes: 11 additions & 1 deletion src/backend/storage/ipc/procarray.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ static ProcArrayStruct *procArray;
static PGPROC *allProcs;
static TMGXACT *allTmGxact;

/*
* Cache to reduce overhead of repeated calls to TransactionIdIsInProgress()
*/
static TransactionId cachedXidIsNotInProgress = InvalidTransactionId;

/*
* Bookkeeping for tracking emulated transactions in recovery
*/
Expand Down Expand Up @@ -1486,7 +1491,7 @@ TransactionIdIsInProgress(TransactionId xid)
* already known to be completed, we can fall out without any access to
* shared memory.
*/
if (TransactionIdIsKnownCompleted(xid))
if (TransactionIdEquals(cachedXidIsNotInProgress, xid))
{
xc_by_known_xact_inc();
return false;
Expand Down Expand Up @@ -1644,6 +1649,7 @@ TransactionIdIsInProgress(TransactionId xid)
if (nxids == 0)
{
xc_no_overflow_inc();
cachedXidIsNotInProgress = xid;
return false;
}

Expand All @@ -1658,7 +1664,10 @@ TransactionIdIsInProgress(TransactionId xid)
xc_slow_answer_inc();

if (TransactionIdDidAbort(xid))
{
cachedXidIsNotInProgress = xid;
return false;
}

/*
* It isn't aborted, so check whether the transaction tree it belongs to
Expand All @@ -1676,6 +1685,7 @@ TransactionIdIsInProgress(TransactionId xid)
}
}

cachedXidIsNotInProgress = xid;
return false;
}

Expand Down
30 changes: 12 additions & 18 deletions src/backend/utils/adt/xid8funcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "storage/lwlock.h"
#include "storage/procarray.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
Expand Down Expand Up @@ -810,29 +811,22 @@ pg_xact_status(PG_FUNCTION_ARGS)
{
Assert(TransactionIdIsValid(xid));

if (TransactionIdIsCurrentTransactionId(xid))
/*
* Like when doing visiblity checks on a row, check whether the
* transaction is still in progress before looking into the CLOG.
* Otherwise we would incorrectly return "committed" for a transaction
* that is committing and has already updated the CLOG, but hasn't
* removed its XID from the proc array yet. (See comment on that race
* condition at the top of heapam_visibility.c)
*/
if (TransactionIdIsInProgress(xid))
status = "in progress";
else if (TransactionIdDidCommit(xid))
status = "committed";
else if (TransactionIdDidAbort(xid))
status = "aborted";
else
{
/*
* The xact is not marked as either committed or aborted in clog.
*
* It could be a transaction that ended without updating clog or
* writing an abort record due to a crash. We can safely assume
* it's aborted if it isn't committed and is older than our
* snapshot xmin.
*
* Otherwise it must be in-progress (or have been at the time we
* checked commit/abort status).
*/
if (TransactionIdPrecedes(xid, GetActiveSnapshot()->xmin))
status = "aborted";
else
status = "in progress";
/* it must have aborted or crashed */
status = "aborted";
}
}
else
Expand Down
Loading
Loading