Add some views and more data
This commit is contained in:
parent
834e646b0e
commit
34cf4770c0
@ -9,15 +9,57 @@ RETURNS TABLE (
|
|||||||
max_dsize INT,
|
max_dsize INT,
|
||||||
ssize INT,
|
ssize INT,
|
||||||
sshift INT,
|
sshift INT,
|
||||||
nelem_alloc INT
|
nelem_alloc INT,
|
||||||
|
nfree INT,
|
||||||
|
nlockents INT,
|
||||||
|
low_mask INT,
|
||||||
|
high_mask INT
|
||||||
) AS 'pg_lock_hash_info', 'pg_lock_hash_info'
|
) AS 'pg_lock_hash_info', 'pg_lock_hash_info'
|
||||||
LANGUAGE C STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_lock_hash_freelist_info()
|
||||||
|
RETURNS TABLE (
|
||||||
|
freelist INT,
|
||||||
|
nentries INT,
|
||||||
|
count INT
|
||||||
|
) AS 'pg_lock_hash_info', 'pg_lock_hash_freelist_info'
|
||||||
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
CREATE FUNCTION pg_lock_hash_bucket_info()
|
CREATE FUNCTION pg_lock_hash_bucket_info()
|
||||||
RETURNS TABLE (
|
RETURNS TABLE (
|
||||||
directory INT,
|
segment INT,
|
||||||
bucket INT,
|
bucket INT,
|
||||||
bucket_hash INT,
|
bucket_hash INT,
|
||||||
|
bucket_hash_low INT,
|
||||||
|
bucket_hash_high INT,
|
||||||
entries INT
|
entries INT
|
||||||
) AS 'pg_lock_hash_info', 'pg_lock_hash_bucket_info'
|
) AS 'pg_lock_hash_info', 'pg_lock_hash_bucket_info'
|
||||||
LANGUAGE C STRICT;
|
LANGUAGE C STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE VIEW pg_lock_hash_info AS
|
||||||
|
SELECT * from pg_lock_hash_info();
|
||||||
|
|
||||||
|
CREATE VIEW pg_lock_hash_freelist_info AS
|
||||||
|
SELECT * FROM pg_lock_hash_freelist_info();
|
||||||
|
|
||||||
|
CREATE VIEW pg_lock_hash_bucket_info AS
|
||||||
|
SELECT * from pg_lock_hash_bucket_info();
|
||||||
|
|
||||||
|
CREATE VIEW pg_lock_hash_freelist_summary AS
|
||||||
|
SELECT count(*) AS freelist_total,
|
||||||
|
sum(nentries) AS nentries_total,
|
||||||
|
avg(nentries) AS nentries_avg,
|
||||||
|
stddev_pop(nentries) nentries_stddev,
|
||||||
|
sum(count) AS count_total,
|
||||||
|
avg(count) AS count_avg,
|
||||||
|
stddev_pop(count) AS count_stddev,
|
||||||
|
sum(nentries+count) AS total
|
||||||
|
FROM pg_lock_hash_freelist_info();
|
||||||
|
|
||||||
|
CREATE VIEW pg_lock_hash_bucket_summary AS
|
||||||
|
SELECT count(*) AS buckets_total,
|
||||||
|
sum(entries) AS total_active_locks,
|
||||||
|
avg(entries) AS avg_bucket_size,
|
||||||
|
stddev_pop(entries) AS stddev_bucket_size
|
||||||
|
FROM pg_lock_hash_bucket_info();
|
||||||
|
|||||||
@ -3,9 +3,13 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
|
#include "miscadmin.h" // Access to MaxBackends and MaxConnections
|
||||||
|
#include "access/twophase.h" // Access to max_prepared_xacts
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
|
#include "storage/lock.h"
|
||||||
#include "storage/lwlock.h"
|
#include "storage/lwlock.h"
|
||||||
#include "storage/shmem.h"
|
#include "storage/shmem.h"
|
||||||
|
#include "storage/spin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/hsearch.h"
|
#include "utils/hsearch.h"
|
||||||
#include "utils/dynahash.h"
|
#include "utils/dynahash.h"
|
||||||
@ -14,8 +18,13 @@ PG_MODULE_MAGIC;
|
|||||||
|
|
||||||
HTAB *lock_hash = NULL;
|
HTAB *lock_hash = NULL;
|
||||||
|
|
||||||
|
void get_lock_hash();
|
||||||
|
|
||||||
// BEGIN Copied from dynahash.c
|
// BEGIN Copied from dynahash.c
|
||||||
|
|
||||||
|
#define NLOCKENTS() \
|
||||||
|
mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
|
||||||
|
|
||||||
#define DEF_SEGSIZE 256
|
#define DEF_SEGSIZE 256
|
||||||
#define DEF_SEGSIZE_SHIFT 8 /* must be log2(DEF_SEGSIZE) */
|
#define DEF_SEGSIZE_SHIFT 8 /* must be log2(DEF_SEGSIZE) */
|
||||||
#define DEF_DIRSIZE 256
|
#define DEF_DIRSIZE 256
|
||||||
@ -72,27 +81,69 @@ struct HTAB
|
|||||||
|
|
||||||
// END Copied from dynahash.c
|
// END Copied from dynahash.c
|
||||||
|
|
||||||
void get_lock_hash();
|
|
||||||
|
|
||||||
void
|
void
|
||||||
get_lock_hash()
|
get_lock_hash()
|
||||||
{
|
{
|
||||||
HASHCTL info;
|
HASHCTL info;
|
||||||
lock_hash = ShmemInitHash("LOCK hash", 0, 0, &info, HASH_ATTACH);
|
long init_table_size, max_table_size;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
max_table_size = NLOCKENTS();
|
||||||
|
init_table_size = max_table_size / 2;
|
||||||
|
|
||||||
|
info.keysize = sizeof(LOCKTAG);
|
||||||
|
info.entrysize = sizeof(LOCK);
|
||||||
|
info.num_partitions = NUM_LOCK_PARTITIONS;
|
||||||
|
|
||||||
|
lock_hash = ShmemInitHash("LOCK hash", init_table_size, max_table_size, &info, HASH_ELEM | HASH_BLOBS | HASH_PARTITION);
|
||||||
|
|
||||||
if (!lock_hash)
|
if (!lock_hash)
|
||||||
{
|
{
|
||||||
elog(ERROR, "Failed to find lock hash");
|
elog(ERROR, "Failed to find lock hash");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
get_free_count(FreeListData *lst)
|
||||||
|
{
|
||||||
|
HASHELEMENT *elem;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
SpinLockAcquire(&(lst->mutex));
|
||||||
|
elem = lst->freeList;
|
||||||
|
while (elem != NULL)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
elem = elem->link;
|
||||||
|
}
|
||||||
|
SpinLockRelease(&(lst->mutex));
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
get_total_free_count(HASHHDR *hctl)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (i=0; i<NUM_FREELISTS; i++)
|
||||||
|
{
|
||||||
|
count += get_free_count(&(hctl->freeList[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(pg_lock_hash_info);
|
PG_FUNCTION_INFO_V1(pg_lock_hash_info);
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
pg_lock_hash_info(PG_FUNCTION_ARGS)
|
pg_lock_hash_info(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
Datum values[10];
|
Datum values[14];
|
||||||
bool nulls[10];
|
bool nulls[14];
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
|
|
||||||
// Get a pointer to the lock hash
|
// Get a pointer to the lock hash
|
||||||
@ -104,9 +155,11 @@ pg_lock_hash_info(PG_FUNCTION_ARGS)
|
|||||||
errmsg("function returning record called in context that cannot accept type record")));
|
errmsg("function returning record called in context that cannot accept type record")));
|
||||||
|
|
||||||
values[0] = Int32GetDatum(lock_hash->hctl->dsize);
|
values[0] = Int32GetDatum(lock_hash->hctl->dsize);
|
||||||
|
//values[0] = Int32GetDatum(MaxBackends);
|
||||||
nulls[0] = false;
|
nulls[0] = false;
|
||||||
|
|
||||||
values[1] = Int32GetDatum(lock_hash->hctl->nsegs);
|
values[1] = Int32GetDatum(lock_hash->hctl->nsegs);
|
||||||
|
//values[1] = Int32GetDatum(MaxConnections);
|
||||||
nulls[1] = false;
|
nulls[1] = false;
|
||||||
|
|
||||||
values[2] = UInt32GetDatum(lock_hash->hctl->max_bucket);
|
values[2] = UInt32GetDatum(lock_hash->hctl->max_bucket);
|
||||||
@ -133,19 +186,102 @@ pg_lock_hash_info(PG_FUNCTION_ARGS)
|
|||||||
values[9] = Int32GetDatum(lock_hash->hctl->nelem_alloc);
|
values[9] = Int32GetDatum(lock_hash->hctl->nelem_alloc);
|
||||||
nulls[9] = false;
|
nulls[9] = false;
|
||||||
|
|
||||||
|
values[10] = Int32GetDatum(get_total_free_count(lock_hash->hctl));
|
||||||
|
nulls[10] = false;
|
||||||
|
|
||||||
|
values[11] = Int32GetDatum(NLOCKENTS());
|
||||||
|
nulls[11] = false;
|
||||||
|
|
||||||
|
values[12] = Int32GetDatum(lock_hash->hctl->low_mask);
|
||||||
|
nulls[12] = false;
|
||||||
|
|
||||||
|
values[13] = Int32GetDatum(lock_hash->hctl->high_mask);
|
||||||
|
nulls[13] = false;
|
||||||
|
|
||||||
tuple = heap_form_tuple(tupdesc, values, nulls);
|
tuple = heap_form_tuple(tupdesc, values, nulls);
|
||||||
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
|
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int count_list(HASHELEMENT *root);
|
PG_FUNCTION_INFO_V1(pg_lock_hash_freelist_info);
|
||||||
|
|
||||||
|
Datum
|
||||||
|
pg_lock_hash_freelist_info(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
FuncCallContext *funcctx;
|
||||||
|
int *list_id;
|
||||||
|
|
||||||
|
if (SRF_IS_FIRSTCALL())
|
||||||
|
{
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
|
funcctx = SRF_FIRSTCALL_INIT();
|
||||||
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
|
// Get a pointer to the lock hash
|
||||||
|
get_lock_hash();
|
||||||
|
|
||||||
|
tupdesc = CreateTemplateTupleDesc(3);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "freelist", INT4OID, -1, 0);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "nentries", INT4OID, -1, 0);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "count", INT4OID, -1, 0);
|
||||||
|
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
|
||||||
|
|
||||||
|
/* Allocate and initialize the position */
|
||||||
|
funcctx->user_fctx = palloc(sizeof(int));
|
||||||
|
list_id = (int *) funcctx->user_fctx;
|
||||||
|
*list_id = 0;
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
funcctx = SRF_PERCALL_SETUP();
|
||||||
|
list_id = (int *) funcctx->user_fctx;
|
||||||
|
|
||||||
|
if (*list_id >= NUM_FREELISTS)
|
||||||
|
{
|
||||||
|
SRF_RETURN_DONE(funcctx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Datum values[4];
|
||||||
|
bool nulls[4];
|
||||||
|
HeapTuple tuple;
|
||||||
|
FreeListData *freelist = &(lock_hash->hctl->freeList[*list_id]);
|
||||||
|
|
||||||
|
values[0] = Int32GetDatum(*list_id);
|
||||||
|
nulls[0] = false;
|
||||||
|
|
||||||
|
values[1] = Int32GetDatum(freelist->nentries);
|
||||||
|
nulls[1] = false;
|
||||||
|
|
||||||
|
values[2] = Int32GetDatum(get_free_count(freelist));
|
||||||
|
nulls[2] = false;
|
||||||
|
|
||||||
|
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
||||||
|
|
||||||
|
// Increment the position, handling segment wraparound
|
||||||
|
(*list_id)++;
|
||||||
|
|
||||||
|
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
count_list(HASHELEMENT *root)
|
count_list(HASHBUCKET root)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
HASHELEMENT *elem = root;
|
HASHELEMENT *elem;
|
||||||
|
|
||||||
|
if (!root)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = root->link;
|
||||||
|
|
||||||
while (elem != NULL) {
|
while (elem != NULL) {
|
||||||
count++;
|
count++;
|
||||||
@ -155,90 +291,113 @@ count_list(HASHELEMENT *root)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int directory;
|
|
||||||
int bucket;
|
|
||||||
int bucket_hash;
|
|
||||||
int entries;
|
|
||||||
} LockHashEntry;
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(pg_lock_hash_bucket_info);
|
PG_FUNCTION_INFO_V1(pg_lock_hash_bucket_info);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int segment_id;
|
||||||
|
int bucket_id;
|
||||||
|
} LockHashBucketInfoPosition;
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
pg_lock_hash_bucket_info(PG_FUNCTION_ARGS)
|
pg_lock_hash_bucket_info(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
FuncCallContext *funcctx;
|
FuncCallContext *funcctx;
|
||||||
LockHashEntry *entries;
|
LockHashBucketInfoPosition *pos;
|
||||||
|
LWLock *part_lock;
|
||||||
|
|
||||||
if (SRF_IS_FIRSTCALL())
|
if (SRF_IS_FIRSTCALL())
|
||||||
{
|
{
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
int i, j;
|
|
||||||
//HASHSEGMENT segp;
|
|
||||||
HASHBUCKET bucketp;
|
|
||||||
|
|
||||||
// Get a pointer to the lock hash
|
|
||||||
get_lock_hash();
|
|
||||||
|
|
||||||
funcctx = SRF_FIRSTCALL_INIT();
|
funcctx = SRF_FIRSTCALL_INIT();
|
||||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
tupdesc = CreateTemplateTupleDesc(4);
|
// Get a pointer to the lock hash
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "directory", INT4OID, -1, 0);
|
get_lock_hash();
|
||||||
|
|
||||||
|
tupdesc = CreateTemplateTupleDesc(6);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "segment", INT4OID, -1, 0);
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "bucket", INT4OID, -1, 0);
|
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "bucket", INT4OID, -1, 0);
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "bucket_hash", INT4OID, -1, 0);
|
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "bucket_hash", INT4OID, -1, 0);
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "entries", INT4OID, -1, 0);
|
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "bucket_hash_low", INT4OID, -1, 0);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bucket_hash_high", INT4OID, -1, 0);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "entries", INT4OID, -1, 0);
|
||||||
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
|
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
|
||||||
|
|
||||||
/* Allocate and initialize the entries */
|
/* Allocate and initialize the position */
|
||||||
entries = (LockHashEntry *) palloc(sizeof(LockHashEntry) * lock_hash->hctl->nsegs * lock_hash->hctl->max_bucket);
|
funcctx->user_fctx = palloc(sizeof(LockHashBucketInfoPosition));
|
||||||
for (i = 0; i < lock_hash->hctl->nsegs; i++)
|
pos = (LockHashBucketInfoPosition *) funcctx->user_fctx;
|
||||||
{
|
pos->segment_id = 0;
|
||||||
for (j = 0; j < lock_hash->hctl->max_bucket; j++)
|
pos->bucket_id = 0;
|
||||||
{
|
|
||||||
bucketp = lock_hash->dir[i][j];
|
|
||||||
entries[i].directory = i;
|
|
||||||
entries[i].bucket = j;
|
|
||||||
entries[i].bucket_hash = bucketp ? bucketp->hashvalue : 'na';
|
|
||||||
entries[i].entries = count_list(bucketp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
funcctx->user_fctx = entries;
|
|
||||||
funcctx->max_calls = lock_hash->hctl->nsegs * 10; // * lock_hash->hctl->max_bucket;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
|
|
||||||
funcctx = SRF_PERCALL_SETUP();
|
funcctx = SRF_PERCALL_SETUP();
|
||||||
entries = (LockHashEntry *) funcctx->user_fctx;
|
pos = (LockHashBucketInfoPosition *) funcctx->user_fctx;
|
||||||
|
|
||||||
if (funcctx->call_cntr < funcctx->max_calls)
|
if (pos->segment_id >= lock_hash->hctl->nsegs)
|
||||||
{
|
|
||||||
Datum values[4];
|
|
||||||
bool nulls[4];
|
|
||||||
HeapTuple tuple;
|
|
||||||
LockHashEntry *entry = &entries[funcctx->call_cntr];
|
|
||||||
|
|
||||||
values[0] = Int32GetDatum(entry->directory);
|
|
||||||
nulls[0] = false;
|
|
||||||
|
|
||||||
values[1] = Int32GetDatum(entry->bucket);
|
|
||||||
nulls[1] = false;
|
|
||||||
|
|
||||||
values[2] = Int32GetDatum(entry->bucket_hash);
|
|
||||||
nulls[2] = false;
|
|
||||||
|
|
||||||
values[3] = Int32GetDatum(entry->entries);
|
|
||||||
nulls[3] = false;
|
|
||||||
|
|
||||||
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
|
||||||
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
SRF_RETURN_DONE(funcctx);
|
SRF_RETURN_DONE(funcctx);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Datum values[6];
|
||||||
|
bool nulls[6];
|
||||||
|
HeapTuple tuple;
|
||||||
|
HASHBUCKET bucketp = lock_hash->dir[pos->segment_id][pos->bucket_id];
|
||||||
|
|
||||||
|
values[0] = Int32GetDatum(pos->segment_id);
|
||||||
|
nulls[0] = false;
|
||||||
|
|
||||||
|
values[1] = Int32GetDatum(pos->bucket_id);
|
||||||
|
nulls[1] = false;
|
||||||
|
|
||||||
|
if (bucketp)
|
||||||
|
{
|
||||||
|
values[2] = Int32GetDatum(bucketp->hashvalue);
|
||||||
|
nulls[2] = false;
|
||||||
|
|
||||||
|
values[3] = Int32GetDatum(bucketp->hashvalue & lock_hash->hctl->low_mask);
|
||||||
|
nulls[3] = false;
|
||||||
|
|
||||||
|
values[4] = Int32GetDatum(bucketp->hashvalue & lock_hash->hctl->high_mask);
|
||||||
|
nulls[4] = false;
|
||||||
|
|
||||||
|
part_lock = LockHashPartitionLock(bucketp->hashvalue);
|
||||||
|
LWLockAcquire(part_lock, LW_SHARED);
|
||||||
|
values[5] = UInt32GetDatum(count_list(bucketp));
|
||||||
|
nulls[5] = false;
|
||||||
|
LWLockRelease(part_lock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
values[2] = Int32GetDatum(0);
|
||||||
|
nulls[2] = true;
|
||||||
|
|
||||||
|
values[3] = Int32GetDatum(0);
|
||||||
|
nulls[3] = true;
|
||||||
|
|
||||||
|
values[4] = Int32GetDatum(0);
|
||||||
|
nulls[4] = true;
|
||||||
|
|
||||||
|
values[5] = Int32GetDatum(0);
|
||||||
|
nulls[5] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
||||||
|
|
||||||
|
// Increment the position, handling segment wraparound
|
||||||
|
pos->bucket_id++;
|
||||||
|
while (pos->bucket_id >= lock_hash->hctl->ssize)
|
||||||
|
{
|
||||||
|
pos->bucket_id -= lock_hash->hctl->ssize;
|
||||||
|
pos->segment_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user