1737 lines
54 KiB
C++
1737 lines
54 KiB
C++
/* -------------------------------------------------------------------------
|
|
*
|
|
* gtm_seq.c
|
|
* Sequence handling on GTM
|
|
*
|
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
* Portions Copyright (c) 2010-2012 Postgres-XC Development Group
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL$
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include <unistd.h>
|
|
|
|
#include "gtm/assert.h"
|
|
#include "gtm/elog.h"
|
|
#include "gtm/gtm.h"
|
|
#include "gtm/gtm_client.h"
|
|
#include "gtm/gtm_seq.h"
|
|
#include "gtm/gtm_serialize.h"
|
|
#include "gtm/gtm_standby.h"
|
|
#include "gtm/standby_utils.h"
|
|
#include "gtm/libpq.h"
|
|
#include "gtm/libpq-int.h"
|
|
#include "gtm/pqformat.h"
|
|
#include "gtm/gtm_backup.h"
|
|
|
|
extern bool Backup_synchronously;
|
|
|
|
typedef struct GTM_SeqInfoHashBucket {
|
|
gtm_List* shb_list;
|
|
GTM_RWLock shb_lock;
|
|
} GTM_SeqInfoHashBucket;
|
|
|
|
#define SEQ_HASH_TABLE_SIZE 1024
|
|
static GTM_SeqInfoHashBucket GTMSequences[SEQ_HASH_TABLE_SIZE];
|
|
|
|
static uint32 seq_gethash(GTM_SequenceKey key);
|
|
static bool seq_keys_equal(GTM_SequenceKey key1, GTM_SequenceKey key2);
|
|
static bool seq_key_dbname_equal(GTM_SequenceKey nsp, GTM_SequenceKey seq);
|
|
static GTM_SeqInfo* seq_find_seqinfo(GTM_SequenceKey seqkey);
|
|
static int seq_release_seqinfo(GTM_SeqInfo* seqinfo);
|
|
static int seq_add_seqinfo(GTM_SeqInfo* seqinfo);
|
|
static int seq_remove_seqinfo(GTM_SeqInfo* seqinfo);
|
|
static GTM_SequenceKey seq_copy_key(GTM_SequenceKey key);
|
|
static int seq_drop_with_dbkey(GTM_SequenceKey nsp);
|
|
static bool GTM_NeedSeqRestoreUpdateInternal(GTM_SeqInfo* seqinfo);
|
|
|
|
/*
|
|
* Get the hash value given the sequence key
|
|
*
|
|
* XXX This should probably be replaced by a better hash function.
|
|
*/
|
|
static uint32 seq_gethash(GTM_SequenceKey key)
|
|
{
|
|
uint32 total = 0;
|
|
int ii;
|
|
|
|
for (ii = 0; ii < key->gsk_keylen; ii++) {
|
|
total += key->gsk_key[ii];
|
|
}
|
|
return (total % SEQ_HASH_TABLE_SIZE);
|
|
}
|
|
|
|
/*
|
|
* Return true if both keys are equal, else return false
|
|
*/
|
|
static bool seq_keys_equal(GTM_SequenceKey key1, GTM_SequenceKey key2)
|
|
{
|
|
Assert(key1);
|
|
Assert(key2);
|
|
|
|
if (key1->gsk_keylen != key2->gsk_keylen) {
|
|
return false;
|
|
}
|
|
|
|
return (memcmp(key1->gsk_key, key2->gsk_key, Min(key1->gsk_keylen, key2->gsk_keylen)) == 0);
|
|
}
|
|
|
|
/*
|
|
* Find the seqinfo structure for the given key. The reference count is
|
|
* incremented before structure is returned. The caller must release the
|
|
* reference to the structure when done with it
|
|
*/
|
|
static GTM_SeqInfo* seq_find_seqinfo(GTM_SequenceKey seqkey)
|
|
{
|
|
uint32 hash = seq_gethash(seqkey);
|
|
GTM_SeqInfoHashBucket* bucket;
|
|
gtm_ListCell* elem;
|
|
GTM_SeqInfo* curr_seqinfo = NULL;
|
|
|
|
bucket = >MSequences[hash];
|
|
|
|
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
|
|
|
|
gtm_foreach(elem, bucket->shb_list)
|
|
{
|
|
curr_seqinfo = (GTM_SeqInfo*)gtm_lfirst(elem);
|
|
if (seq_keys_equal(curr_seqinfo->gs_key, seqkey)) {
|
|
break;
|
|
}
|
|
curr_seqinfo = NULL;
|
|
}
|
|
|
|
if (curr_seqinfo != NULL) {
|
|
GTM_RWLockAcquire(&curr_seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
if (curr_seqinfo->gs_state != SEQ_STATE_ACTIVE) {
|
|
elog(LOG, "Sequence not active");
|
|
GTM_RWLockRelease(&curr_seqinfo->gs_lock);
|
|
return NULL;
|
|
}
|
|
Assert(curr_seqinfo->gs_ref_count != SEQ_MAX_REFCOUNT);
|
|
curr_seqinfo->gs_ref_count++;
|
|
GTM_RWLockRelease(&curr_seqinfo->gs_lock);
|
|
}
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
|
|
return curr_seqinfo;
|
|
}
|
|
|
|
/*
|
|
* Release previously grabbed reference to the structure. If the structure is
|
|
* marked for deletion, it will be removed from the global array and released
|
|
*/
|
|
static int seq_release_seqinfo(GTM_SeqInfo* seqinfo)
|
|
{
|
|
bool remove = false;
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
Assert(seqinfo->gs_ref_count > 0);
|
|
seqinfo->gs_ref_count--;
|
|
|
|
if ((seqinfo->gs_state == SEQ_STATE_DELETED) && (seqinfo->gs_ref_count == 0)) {
|
|
remove = true;
|
|
}
|
|
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
/*
|
|
* Remove the structure from the global hash table
|
|
*/
|
|
if (remove) {
|
|
seq_remove_seqinfo(seqinfo);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Add a seqinfo structure to the global hash table.
|
|
*/
|
|
static int seq_add_seqinfo(GTM_SeqInfo* seqinfo)
|
|
{
|
|
uint32 hash = seq_gethash(seqinfo->gs_key);
|
|
GTM_SeqInfoHashBucket* bucket;
|
|
gtm_ListCell* elem;
|
|
|
|
bucket = >MSequences[hash];
|
|
|
|
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_WRITE);
|
|
|
|
gtm_foreach(elem, bucket->shb_list)
|
|
{
|
|
GTM_SeqInfo* curr_seqinfo = NULL;
|
|
curr_seqinfo = (GTM_SeqInfo*)gtm_lfirst(elem);
|
|
|
|
if (seq_keys_equal(curr_seqinfo->gs_key, seqinfo->gs_key)) {
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
ereport(LOG, (EEXIST, errmsg("Sequence with the given key already exists")));
|
|
return EEXIST;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Safe to add the structure to the list
|
|
*/
|
|
bucket->shb_list = gtm_lappend(bucket->shb_list, seqinfo);
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Remove the seqinfo structure from the global hash table. If the structure is
|
|
* currently referenced by some other thread, just mark the structure for
|
|
* deletion and it will be deleted by the final reference is released.
|
|
*/
|
|
static int seq_remove_seqinfo(GTM_SeqInfo* seqinfo)
|
|
{
|
|
uint32 hash = seq_gethash(seqinfo->gs_key);
|
|
GTM_SeqInfoHashBucket* bucket;
|
|
|
|
bucket = >MSequences[hash];
|
|
|
|
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_WRITE);
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
|
|
if (seqinfo->gs_ref_count > 1) {
|
|
seqinfo->gs_state = SEQ_STATE_DELETED;
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
return EBUSY;
|
|
}
|
|
|
|
bucket->shb_list = gtm_list_delete(bucket->shb_list, seqinfo);
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static GTM_SequenceKey seq_copy_key(GTM_SequenceKey key)
|
|
{
|
|
GTM_SequenceKey retkey = NULL;
|
|
|
|
/*
|
|
* We must use the TopMostMemoryContext because the sequence information is
|
|
* not bound to a thread and can outlive any of the thread specific
|
|
* contextes.
|
|
*/
|
|
retkey = (GTM_SequenceKey)MemoryContextAlloc(TopMostMemoryContext, sizeof(GTM_SequenceKeyData) + key->gsk_keylen);
|
|
|
|
if (retkey == NULL) {
|
|
ereport(ERROR, (ENOMEM, errmsg("Out of memory")));
|
|
}
|
|
|
|
retkey->gsk_keylen = key->gsk_keylen;
|
|
retkey->gsk_key = (char*)((char*)retkey + sizeof(GTM_SequenceKeyData));
|
|
|
|
errno_t rc = 0;
|
|
rc = memcpy_s(retkey->gsk_key, key->gsk_keylen, key->gsk_key, key->gsk_keylen);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
return retkey;
|
|
}
|
|
|
|
/*
|
|
* Initialize a new sequence. Optionally set the initial value of the sequence.
|
|
*/
|
|
int GTM_SeqOpen(GTM_SequenceKey seqkey, GTM_Sequence increment_by, GTM_Sequence minval, GTM_Sequence maxval,
|
|
GTM_Sequence startval, bool cycle)
|
|
{
|
|
GTM_SeqInfo* seqinfo = NULL;
|
|
int errcode = 0;
|
|
seqinfo = (GTM_SeqInfo*)palloc(sizeof(GTM_SeqInfo));
|
|
|
|
if (seqinfo == NULL) {
|
|
ereport(ERROR, (ENOMEM, errmsg("Out of memory")));
|
|
}
|
|
|
|
GTM_RWLockInit(&seqinfo->gs_lock);
|
|
|
|
seqinfo->gs_ref_count = 0;
|
|
seqinfo->gs_key = seq_copy_key(seqkey);
|
|
seqinfo->gs_state = SEQ_STATE_ACTIVE;
|
|
seqinfo->gs_called = false;
|
|
|
|
/*
|
|
* Set the increment. Default is 1
|
|
*/
|
|
if (SEQVAL_IS_VALID(increment_by)) {
|
|
seqinfo->gs_increment_by = increment_by;
|
|
} else {
|
|
seqinfo->gs_increment_by = 1;
|
|
}
|
|
|
|
/*
|
|
* If minval is specified, set the minvalue to the given minval,
|
|
* otherwise set to the defaults
|
|
*/
|
|
if (SEQVAL_IS_VALID(minval)) {
|
|
seqinfo->gs_min_value = minval;
|
|
}
|
|
else if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
seqinfo->gs_min_value = SEQ_DEF_MIN_SEQVAL_ASCEND;
|
|
} else {
|
|
seqinfo->gs_min_value = SEQ_DEF_MIN_SEQVAL_DESCEND;
|
|
}
|
|
|
|
/*
|
|
* If maxval is specfied, set the maxvalue to the given maxval, otherwise
|
|
* set to the defaults depending on whether the seqeunce is ascending or
|
|
* descending. Also do some basic contraint checks
|
|
*/
|
|
if (SEQVAL_IS_VALID(maxval)) {
|
|
if (maxval < seqinfo->gs_min_value) {
|
|
ereport(ERROR, (ERANGE, errmsg("Max value must be greater than min value")));
|
|
}
|
|
seqinfo->gs_max_value = maxval;
|
|
} else if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
seqinfo->gs_max_value = SEQ_DEF_MAX_SEQVAL_ASCEND;
|
|
} else {
|
|
seqinfo->gs_max_value = SEQ_DEF_MAX_SEQVAL_DESCEND;
|
|
}
|
|
|
|
/*
|
|
* Set the startval if specified. Do some basic checks like startval must
|
|
* be in-between min and max values
|
|
*/
|
|
if (SEQVAL_IS_VALID(startval)) {
|
|
if (startval < seqinfo->gs_min_value) {
|
|
ereport(ERROR, (ERANGE, errmsg("Start value must be greater than or equal to the min value")));
|
|
}
|
|
|
|
if (startval > seqinfo->gs_max_value) {
|
|
ereport(ERROR, (ERANGE, errmsg("Start value must be less than or equal to the max value")));
|
|
}
|
|
|
|
seqinfo->gs_init_value = seqinfo->gs_value = startval;
|
|
} else if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
seqinfo->gs_init_value = seqinfo->gs_value = SEQ_DEF_MIN_SEQVAL_ASCEND;
|
|
} else {
|
|
seqinfo->gs_init_value = seqinfo->gs_value = SEQ_DEF_MIN_SEQVAL_DESCEND;
|
|
}
|
|
|
|
/*
|
|
* Should we wrap around ?
|
|
*/
|
|
seqinfo->gs_cycle = cycle;
|
|
|
|
/* Set the last value in case of a future restart */
|
|
seqinfo->gs_last_value = seqinfo->gs_init_value;
|
|
|
|
seqinfo->gs_backedUpValue = seqinfo->gs_value;
|
|
|
|
if ((errcode = seq_add_seqinfo(seqinfo))) {
|
|
GTM_RWLockDestroy(&seqinfo->gs_lock);
|
|
pfree(seqinfo->gs_key);
|
|
pfree(seqinfo);
|
|
}
|
|
GTM_SetNeedBackup();
|
|
|
|
return errcode;
|
|
}
|
|
|
|
/*
|
|
* Alter a sequence
|
|
*/
|
|
int GTM_SeqAlter(GTM_SequenceKey seqkey, GTM_Sequence increment_by, GTM_Sequence minval, GTM_Sequence maxval,
|
|
GTM_Sequence startval, GTM_Sequence lastval, bool cycle, bool is_restart)
|
|
{
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
|
|
if (seqinfo == NULL) {
|
|
ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist")));
|
|
return EINVAL;
|
|
}
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
|
|
/* Modify the data if necessary */
|
|
if (seqinfo->gs_cycle != cycle) {
|
|
seqinfo->gs_cycle = cycle;
|
|
}
|
|
if (seqinfo->gs_min_value != minval) {
|
|
seqinfo->gs_min_value = minval;
|
|
}
|
|
if (seqinfo->gs_max_value != maxval) {
|
|
seqinfo->gs_max_value = maxval;
|
|
}
|
|
if (seqinfo->gs_increment_by != increment_by) {
|
|
seqinfo->gs_increment_by = increment_by;
|
|
}
|
|
|
|
/*
|
|
* Check start/restart processes.
|
|
* Check first if restart is necessary and reset sequence in that case.
|
|
* If not, check if a simple start is necessary and update sequence.
|
|
*/
|
|
if (is_restart) {
|
|
/* Restart command has been used, reset the sequence */
|
|
seqinfo->gs_called = false;
|
|
seqinfo->gs_init_value = seqinfo->gs_last_value = lastval;
|
|
} else {
|
|
/* Start has been used, reinitialize init value */
|
|
if (seqinfo->gs_init_value != startval) {
|
|
seqinfo->gs_init_value = seqinfo->gs_last_value = startval;
|
|
}
|
|
}
|
|
|
|
/* Remove the old key with the old name */
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
seq_release_seqinfo(seqinfo);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Restore a sequence.
|
|
*/
|
|
int GTM_SeqRestore(GTM_SequenceKey seqkey, GTM_Sequence increment_by, GTM_Sequence minval, GTM_Sequence maxval,
|
|
GTM_Sequence startval, GTM_Sequence curval, int32 state, bool cycle, bool called)
|
|
{
|
|
GTM_SeqInfo* seqinfo = NULL;
|
|
int errcode = 0;
|
|
seqinfo = (GTM_SeqInfo*)palloc(sizeof(GTM_SeqInfo));
|
|
|
|
if (seqinfo == NULL) {
|
|
ereport(ERROR, (ENOMEM, errmsg("Out of memory")));
|
|
}
|
|
|
|
GTM_RWLockInit(&seqinfo->gs_lock);
|
|
|
|
seqinfo->gs_ref_count = 0;
|
|
seqinfo->gs_key = seq_copy_key(seqkey);
|
|
seqinfo->gs_state = state;
|
|
seqinfo->gs_called = called;
|
|
|
|
seqinfo->gs_increment_by = increment_by;
|
|
seqinfo->gs_min_value = minval;
|
|
seqinfo->gs_max_value = maxval;
|
|
|
|
seqinfo->gs_init_value = seqinfo->gs_last_value = startval;
|
|
seqinfo->gs_value = curval;
|
|
seqinfo->gs_backedUpValue = seqinfo->gs_value;
|
|
|
|
/*
|
|
* Should we wrap around ?
|
|
*/
|
|
seqinfo->gs_cycle = cycle;
|
|
|
|
if ((errcode = seq_add_seqinfo(seqinfo))) {
|
|
GTM_RWLockDestroy(&seqinfo->gs_lock);
|
|
pfree(seqinfo->gs_key);
|
|
pfree(seqinfo);
|
|
}
|
|
return errcode;
|
|
}
|
|
|
|
/*
|
|
* Destroy the given sequence depending on type of given key
|
|
*/
|
|
int GTM_SeqClose(GTM_SequenceKey seqkey)
|
|
{
|
|
int res;
|
|
|
|
switch (seqkey->gsk_type) {
|
|
case GTM_SEQ_FULL_NAME: {
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
if (seqinfo != NULL) {
|
|
seq_remove_seqinfo(seqinfo);
|
|
pfree(seqinfo->gs_key);
|
|
pfree(seqinfo);
|
|
res = 0;
|
|
} else {
|
|
res = EINVAL;
|
|
}
|
|
break;
|
|
}
|
|
case GTM_SEQ_DB_NAME:
|
|
res = seq_drop_with_dbkey(seqkey);
|
|
break;
|
|
|
|
default:
|
|
res = EINVAL;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* Check if sequence key contains only Database name */
|
|
static bool seq_key_dbname_equal(GTM_SequenceKey nsp, GTM_SequenceKey seq)
|
|
{
|
|
Assert(nsp);
|
|
Assert(seq);
|
|
|
|
/*
|
|
* Sequence key of GTM_SEQ_DB_NAME type has to be shorter
|
|
* than given sequence key.
|
|
*/
|
|
if (nsp->gsk_keylen >= seq->gsk_keylen) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Check also if first part of sequence key name has a dot at the right place.
|
|
* This accelerates process instead of making numerous memcmp.
|
|
*/
|
|
if (seq->gsk_key[nsp->gsk_keylen - 1] != '.') {
|
|
return false;
|
|
}
|
|
|
|
/* Then Check the strings */
|
|
return (memcmp(nsp->gsk_key, seq->gsk_key, nsp->gsk_keylen - 1) == 0);
|
|
}
|
|
|
|
/*
|
|
* Remove all sequences with given key depending on its type.
|
|
*/
|
|
static int seq_drop_with_dbkey(GTM_SequenceKey nsp)
|
|
{
|
|
int ii = 0;
|
|
GTM_SeqInfoHashBucket* bucket;
|
|
gtm_ListCell *cell, *prev;
|
|
GTM_SeqInfo* curr_seqinfo = NULL;
|
|
int res = 0;
|
|
bool deleted;
|
|
|
|
for (ii = 0; ii < SEQ_HASH_TABLE_SIZE; ii++) {
|
|
bucket = >MSequences[ii];
|
|
|
|
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
|
|
|
|
prev = NULL;
|
|
cell = gtm_list_head(bucket->shb_list);
|
|
while (cell != NULL) {
|
|
curr_seqinfo = (GTM_SeqInfo*)gtm_lfirst(cell);
|
|
deleted = false;
|
|
|
|
if (seq_key_dbname_equal(nsp, curr_seqinfo->gs_key)) {
|
|
GTM_RWLockAcquire(&curr_seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
|
|
if (curr_seqinfo->gs_ref_count > 1) {
|
|
curr_seqinfo->gs_state = SEQ_STATE_DELETED;
|
|
|
|
/* can not happen, be checked before called */
|
|
elog(LOG, "Sequence %s is in use, mark for deletion only", curr_seqinfo->gs_key->gsk_key);
|
|
|
|
/*
|
|
* Continue to delete other sequences linked to this dbname,
|
|
* sequences in use are deleted later.
|
|
*/
|
|
res = EBUSY;
|
|
} else {
|
|
/* Sequence is not is busy state, it can be deleted safely */
|
|
|
|
bucket->shb_list = gtm_list_delete_cell(bucket->shb_list, cell, prev);
|
|
elog(LOG, "Sequence %s was deleted from GTM", curr_seqinfo->gs_key->gsk_key);
|
|
|
|
deleted = true;
|
|
}
|
|
GTM_RWLockRelease(&curr_seqinfo->gs_lock);
|
|
}
|
|
if (deleted) {
|
|
if (prev)
|
|
cell = gtm_lnext(prev);
|
|
else
|
|
cell = gtm_list_head(bucket->shb_list);
|
|
} else {
|
|
prev = cell;
|
|
cell = gtm_lnext(cell);
|
|
}
|
|
}
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
/*
|
|
* Rename an existing sequence with a new name
|
|
*/
|
|
int GTM_SeqRename(GTM_SequenceKey seqkey, GTM_SequenceKey newseqkey)
|
|
{
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
GTM_SeqInfo* newseqinfo = NULL;
|
|
int errcode = 0;
|
|
|
|
/* replace old key by new key */
|
|
if (seqinfo == NULL) {
|
|
ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist")));
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Now create the new sequence info */
|
|
newseqinfo = (GTM_SeqInfo*)palloc(sizeof(GTM_SeqInfo));
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
GTM_RWLockInit(&newseqinfo->gs_lock);
|
|
|
|
newseqinfo->gs_ref_count = 0;
|
|
newseqinfo->gs_key = seq_copy_key(newseqkey);
|
|
newseqinfo->gs_state = seqinfo->gs_state;
|
|
newseqinfo->gs_called = seqinfo->gs_called;
|
|
|
|
newseqinfo->gs_increment_by = seqinfo->gs_increment_by;
|
|
newseqinfo->gs_min_value = seqinfo->gs_min_value;
|
|
newseqinfo->gs_max_value = seqinfo->gs_max_value;
|
|
|
|
newseqinfo->gs_init_value = seqinfo->gs_init_value;
|
|
newseqinfo->gs_value = seqinfo->gs_value;
|
|
newseqinfo->gs_backedUpValue = seqinfo->gs_backedUpValue;
|
|
newseqinfo->gs_cycle = seqinfo->gs_cycle;
|
|
|
|
newseqinfo->gs_state = seqinfo->gs_state;
|
|
newseqinfo->gs_last_value = seqinfo->gs_last_value;
|
|
|
|
/* Add the copy to the list */
|
|
if ((errcode = seq_add_seqinfo(newseqinfo))) /* a lock is taken here for the new sequence */
|
|
{
|
|
GTM_RWLockDestroy(&newseqinfo->gs_lock);
|
|
pfree(newseqinfo->gs_key);
|
|
pfree(newseqinfo);
|
|
return errcode;
|
|
}
|
|
|
|
/* Remove the old key with the old name */
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
/* Release first the structure as it has been taken previously */
|
|
seq_release_seqinfo(seqinfo);
|
|
|
|
/* Close sequence properly, full name is here */
|
|
seqkey->gsk_type = GTM_SEQ_FULL_NAME;
|
|
/* Then close properly the old sequence */
|
|
GTM_SeqClose(seqkey);
|
|
return errcode;
|
|
}
|
|
|
|
/*
|
|
* Set values for the sequence
|
|
*/
|
|
int GTM_SeqSetVal(GTM_SequenceKey seqkey, GTM_Sequence nextval, bool iscalled)
|
|
{
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
|
|
if (seqinfo == NULL) {
|
|
ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist")));
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
|
|
seqinfo->gs_last_value = seqinfo->gs_value;
|
|
|
|
if (seqinfo->gs_value != nextval)
|
|
seqinfo->gs_value = nextval;
|
|
|
|
seqinfo->gs_called = iscalled;
|
|
|
|
/* If sequence is not called, reset the init value to the value set */
|
|
if (!iscalled)
|
|
seqinfo->gs_init_value = nextval;
|
|
|
|
/* Remove the old key with the old name */
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
GTM_SetNeedBackup();
|
|
seq_release_seqinfo(seqinfo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get next value for the sequence
|
|
*/
|
|
GTM_Sequence GTM_SeqGetNext(GTM_SequenceKey seqkey)
|
|
{
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
GTM_Sequence value;
|
|
|
|
if (seqinfo == NULL) {
|
|
ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist")));
|
|
return InvalidSequenceValue;
|
|
}
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
|
|
/*
|
|
* If the sequence is called for the first time, initialize the value and
|
|
* return the start value
|
|
*/
|
|
if (!SEQ_IS_CALLED(seqinfo)) {
|
|
value = seqinfo->gs_last_value = seqinfo->gs_value = seqinfo->gs_init_value;
|
|
seqinfo->gs_called = true;
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
seq_release_seqinfo(seqinfo);
|
|
return value;
|
|
}
|
|
|
|
if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
/*
|
|
* Check if the sequence is about to wrap-around. If the sequence does
|
|
* not support wrap-around, throw an error and return
|
|
* InvalidSequenceValue
|
|
*/
|
|
if (seqinfo->gs_max_value - seqinfo->gs_increment_by >= seqinfo->gs_value)
|
|
value = seqinfo->gs_last_value = seqinfo->gs_value = seqinfo->gs_value + seqinfo->gs_increment_by;
|
|
else if (SEQ_IS_CYCLE(seqinfo))
|
|
value = seqinfo->gs_last_value = seqinfo->gs_value = seqinfo->gs_min_value;
|
|
else {
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
seq_release_seqinfo(seqinfo);
|
|
ereport(LOG, (ERANGE, errmsg("Sequence reached maximum value")));
|
|
return InvalidSequenceValue;
|
|
}
|
|
} else {
|
|
/*
|
|
* Check if the sequence is about to wrap-around. If the sequence does
|
|
* not support wrap-around, throw an error and return
|
|
* InvalidSequenceValue, otherwise wrap around the sequence and reset
|
|
* it to the max value.
|
|
*
|
|
* Note: The gs_increment_by is a signed integer and is negative for
|
|
* descending sequences. So we don't need special handling below
|
|
*/
|
|
if (seqinfo->gs_min_value - seqinfo->gs_increment_by <= seqinfo->gs_value)
|
|
value = seqinfo->gs_value = seqinfo->gs_last_value = seqinfo->gs_value + seqinfo->gs_increment_by;
|
|
else if (SEQ_IS_CYCLE(seqinfo))
|
|
value = seqinfo->gs_value = seqinfo->gs_last_value = seqinfo->gs_max_value;
|
|
else {
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
seq_release_seqinfo(seqinfo);
|
|
ereport(LOG, (ERANGE, errmsg("Sequence reached minimum value")));
|
|
return InvalidSequenceValue;
|
|
}
|
|
}
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
if (GTM_NeedSeqRestoreUpdateInternal(seqinfo))
|
|
GTM_SetNeedBackup();
|
|
seq_release_seqinfo(seqinfo);
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* Reset the sequence
|
|
*/
|
|
int GTM_SeqReset(GTM_SequenceKey seqkey)
|
|
{
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
|
|
if (seqinfo == NULL) {
|
|
ereport(LOG, (EINVAL, errmsg("The sequence with the given key does not exist")));
|
|
return EINVAL;
|
|
}
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
|
|
seqinfo->gs_value = seqinfo->gs_last_value = seqinfo->gs_backedUpValue = seqinfo->gs_init_value;
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
|
|
GTM_SetNeedBackup();
|
|
seq_release_seqinfo(seqinfo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void GTM_InitSeqManager(void)
|
|
{
|
|
int ii;
|
|
|
|
for (ii = 0; ii < SEQ_HASH_TABLE_SIZE; ii++) {
|
|
GTMSequences[ii].shb_list = gtm_NIL;
|
|
GTM_RWLockInit(>MSequences[ii].shb_lock);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_INIT/MSG_BKUP_SEQUENCE_INIT message
|
|
*
|
|
* is_backup indicates the message is MSG_BKUP_SEQUENCE_INIT
|
|
*/
|
|
void ProcessSequenceInitCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey;
|
|
GTM_Sequence increment, minval, maxval, startval;
|
|
bool cycle;
|
|
StringInfoData buf;
|
|
int errcode;
|
|
MemoryContext oldContext;
|
|
|
|
/*
|
|
* Get the sequence key
|
|
*/
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
|
|
/*
|
|
* Read various sequence parameters
|
|
*/
|
|
errno_t rc = 0;
|
|
rc = memcpy_s(&increment, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&minval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&maxval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&startval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
cycle = pq_getmsgbyte(message);
|
|
|
|
/*
|
|
* We must use the TopMostMemoryContext because the sequence information is
|
|
* not bound to a thread and can outlive any of the thread specific
|
|
* contextes.
|
|
*/
|
|
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
|
|
|
|
if ((errcode = GTM_SeqOpen(&seqkey, increment, minval, maxval, startval, cycle)))
|
|
ereport(ERROR, (errcode, errmsg("Failed to open a new sequence")));
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
elog(LOG, "Opening sequence %s", seqkey.gsk_key);
|
|
|
|
pq_getmsgend(message);
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
int rc;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling open_sequence() for standby GTM.");
|
|
|
|
retry:
|
|
rc = bkup_open_sequence(
|
|
GetMyThreadInfo->thr_conn->standby, &seqkey, increment, minval, maxval, startval, cycle);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "open_sequence() returns rc %d.", rc);
|
|
}
|
|
/*
|
|
* Send a SUCCESS message back to the client
|
|
*/
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_INIT_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, seqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, seqkey.gsk_key, seqkey.gsk_keylen);
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
/* Flush standby first */
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
}
|
|
/* FIXME: need to check errors */
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_ALTER/MSG_BKUP_SEQUENCE_ALTER message
|
|
*
|
|
* is_backup indicates the message is MSG_BKUP_SEQUENCE_ALTER
|
|
*/
|
|
void ProcessSequenceAlterCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey;
|
|
GTM_Sequence increment, minval, maxval, startval, lastval;
|
|
bool cycle, is_restart;
|
|
StringInfoData buf;
|
|
int errcode;
|
|
MemoryContext oldContext;
|
|
|
|
/*
|
|
* Get the sequence key
|
|
*/
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
|
|
/*
|
|
* Read various sequence parameters
|
|
*/
|
|
errno_t rc = 0;
|
|
rc = memcpy_s(&increment, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&minval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&maxval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&startval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
rc = memcpy_s(&lastval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
cycle = pq_getmsgbyte(message);
|
|
is_restart = pq_getmsgbyte(message);
|
|
|
|
/*
|
|
* We must use the TopMostMemoryContext because the sequence information is
|
|
* not bound to a thread and can outlive any of the thread specific
|
|
* contextes.
|
|
*/
|
|
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
|
|
|
|
elog(LOG, "Altering sequence key %s", seqkey.gsk_key);
|
|
|
|
if ((errcode = GTM_SeqAlter(&seqkey, increment, minval, maxval, startval, lastval, cycle, is_restart)))
|
|
ereport(ERROR, (errcode, errmsg("Failed to open a new sequence")));
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
pq_getmsgend(message);
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
int rc;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling alter_sequence() for standby GTM.");
|
|
|
|
retry:
|
|
rc = bkup_alter_sequence(GetMyThreadInfo->thr_conn->standby,
|
|
&seqkey,
|
|
increment,
|
|
minval,
|
|
maxval,
|
|
startval,
|
|
lastval,
|
|
cycle,
|
|
is_restart);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "alter_sequence() returns rc %d.", rc);
|
|
}
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_ALTER_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, seqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, seqkey.gsk_key, seqkey.gsk_keylen);
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
|
|
/* FIXME: need to check errors */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_LIST message
|
|
*/
|
|
void ProcessSequenceListCommand(Port* myport, StringInfo message)
|
|
{
|
|
StringInfoData buf;
|
|
int seq_count;
|
|
int seq_maxcount;
|
|
GTM_SeqInfo** seq_list;
|
|
int i;
|
|
|
|
if (Recovery_IsStandby()) {
|
|
ereport(ERROR, (EPERM, errmsg("Operation not permitted under the standby mode.")));
|
|
}
|
|
|
|
seq_count = 0;
|
|
seq_maxcount = 1024;
|
|
seq_list = (GTM_SeqInfo**)palloc(seq_maxcount * sizeof(GTM_SeqInfo*));
|
|
|
|
/*
|
|
* Store pointers to all GTM_SeqInfo in the hash buckets into an array.
|
|
*/
|
|
for (i = 0; i < SEQ_HASH_TABLE_SIZE; i++) {
|
|
GTM_SeqInfoHashBucket* b;
|
|
gtm_ListCell* elem;
|
|
|
|
b = >MSequences[i];
|
|
|
|
GTM_RWLockAcquire(&b->shb_lock, GTM_LOCKMODE_READ);
|
|
|
|
gtm_foreach(elem, b->shb_list)
|
|
{
|
|
/* Allocate larger array if required */
|
|
if (seq_count == seq_maxcount) {
|
|
int newcount;
|
|
GTM_SeqInfo** newlist;
|
|
|
|
newcount = 2 * seq_maxcount;
|
|
newlist = (GTM_SeqInfo**)repalloc(seq_list, newcount * sizeof(GTM_SeqInfo*));
|
|
/*
|
|
* If failed try to get less. It is unlikely to happen, but
|
|
* let's be safe.
|
|
*/
|
|
while (newlist == NULL) {
|
|
newcount = seq_maxcount + (newcount - seq_maxcount) / 2 - 1;
|
|
if (newcount <= seq_maxcount) {
|
|
/* give up */
|
|
ereport(ERROR, (ERANGE, errmsg("Can not list all the sequences")));
|
|
}
|
|
newlist = (GTM_SeqInfo**)repalloc(seq_list, newcount * sizeof(GTM_SeqInfo*));
|
|
}
|
|
seq_maxcount = newcount;
|
|
seq_list = newlist;
|
|
}
|
|
seq_list[seq_count] = (GTM_SeqInfo*)gtm_lfirst(elem);
|
|
seq_count++;
|
|
}
|
|
|
|
GTM_RWLockRelease(&b->shb_lock);
|
|
}
|
|
|
|
pq_getmsgend(message);
|
|
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_LIST_RESULT, 4);
|
|
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
|
|
/* Send a number of sequences */
|
|
pq_sendint(&buf, seq_count, 4);
|
|
|
|
/*
|
|
* Send sequences from the array
|
|
*/
|
|
{
|
|
/*
|
|
* TODO set initial size big enough to fit any sequence, and avoid
|
|
* reallocations.
|
|
*/
|
|
size_t seq_maxlen = 256;
|
|
char* seq_buf = (char*)palloc(seq_maxlen);
|
|
|
|
for (i = 0; i < seq_count; i++) {
|
|
size_t seq_buflen = gtm_get_sequence_size(seq_list[i]);
|
|
if (seq_buflen > seq_maxlen) {
|
|
seq_maxlen = seq_buflen;
|
|
seq_buf = (char*)repalloc(seq_buf, seq_maxlen);
|
|
}
|
|
|
|
gtm_serialize_sequence(seq_list[i], seq_buf, seq_buflen);
|
|
|
|
elog(DEBUG1, "seq_buflen = %ld", seq_buflen);
|
|
|
|
pq_sendint(&buf, seq_buflen, 4);
|
|
pq_sendbytes(&buf, seq_buf, seq_buflen);
|
|
}
|
|
}
|
|
|
|
pq_endmessage(myport, &buf);
|
|
|
|
elog(DEBUG1, "ProcessSequenceListCommand() done.");
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY)
|
|
/* Don't flush to the backup because this does not change the internal status */
|
|
pq_flush(myport);
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_GET_NEXT/MSG_BKUP_SEQUENCE_GET_NEXT message
|
|
*
|
|
* is_backup indicates the message is MSG_BKUP_SEQUENCE_GET_NEXT
|
|
*/
|
|
void ProcessSequenceGetNextCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey;
|
|
StringInfoData buf;
|
|
GTM_Sequence seqval;
|
|
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
|
|
seqval = GTM_SeqGetNext(&seqkey);
|
|
if (!SEQVAL_IS_VALID(seqval)) {
|
|
ereport(ERROR, (ERANGE, errmsg("Can not get current value of the sequence")));
|
|
}
|
|
|
|
elog(DEBUG1, "Getting next value %ld for sequence %s", seqval, seqkey.gsk_key);
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
GTM_Sequence loc_seq;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling get_next() for standby GTM.");
|
|
|
|
retry:
|
|
bkup_get_next(GetMyThreadInfo->thr_conn->standby, &seqkey, &loc_seq);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "get_next() returns GTM_Sequence %ld.", loc_seq);
|
|
}
|
|
/* Respond to the client */
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_GET_NEXT_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, seqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, seqkey.gsk_key, seqkey.gsk_keylen);
|
|
pq_sendbytes(&buf, (char*)&seqval, sizeof(GTM_Sequence));
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
/* Flush to the standby first */
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
|
|
/* FIXME: need to check errors */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_SET_VAL/MSG_BKUP_SEQUENCE_SET_VAL message
|
|
*
|
|
* is_backup indicates the message is MSG_BKUP_SEQUENCE_SET_VAL
|
|
*/
|
|
void ProcessSequenceSetValCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey;
|
|
GTM_Sequence nextval;
|
|
MemoryContext oldContext;
|
|
StringInfoData buf;
|
|
bool iscalled;
|
|
int errcode;
|
|
|
|
/*
|
|
* Get the sequence key
|
|
*/
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
|
|
/* Read parameters to be set */
|
|
errno_t rc = 0;
|
|
rc = memcpy_s(&nextval, sizeof(GTM_Sequence), pq_getmsgbytes(message, sizeof(GTM_Sequence)), sizeof(GTM_Sequence));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
iscalled = pq_getmsgbyte(message);
|
|
|
|
/*
|
|
* We must use the TopMostMemoryContext because the sequence information is
|
|
* not bound to a thread and can outlive any of the thread specific
|
|
* contextes.
|
|
*/
|
|
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
|
|
|
|
elog(DEBUG1, "Setting new value %ld for sequence %s", nextval, seqkey.gsk_key);
|
|
|
|
if ((errcode = GTM_SeqSetVal(&seqkey, nextval, iscalled)))
|
|
ereport(ERROR, (errcode, errmsg("Failed to set values of sequence")));
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
pq_getmsgend(message);
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
int rc;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling set_val() for standby GTM.");
|
|
|
|
retry:
|
|
rc = bkup_set_val(GetMyThreadInfo->thr_conn->standby, &seqkey, nextval, iscalled);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "set_val() returns rc %d.", rc);
|
|
}
|
|
/* Respond to the client */
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_SET_VAL_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, seqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, seqkey.gsk_key, seqkey.gsk_keylen);
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
/* Flush the standby first */
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
}
|
|
/* FIXME: need to check errors */
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_RESET/MSG_BKUP_SEQUENCE_RESET message
|
|
*
|
|
* is_backup indicates the cmessage is MSG_BKUP_SEQUENCE_RESULT
|
|
*/
|
|
void ProcessSequenceResetCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey;
|
|
StringInfoData buf;
|
|
int errcode;
|
|
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
|
|
elog(LOG, "Resetting sequence %s", seqkey.gsk_key);
|
|
|
|
if ((errcode = GTM_SeqReset(&seqkey))) {
|
|
ereport(ERROR, (errcode, errmsg("Can not reset the sequence")));
|
|
}
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
int rc;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling reset_sequence() for standby GTM.");
|
|
|
|
retry:
|
|
rc = bkup_reset_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "reset_sequence() returns rc %d.", rc);
|
|
}
|
|
/* Respond to the client */
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_RESET_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, seqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, seqkey.gsk_key, seqkey.gsk_keylen);
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
/* Flush the standby first */
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
}
|
|
/* FIXME: need to check errors */
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_CLOSE/MSG_BKUP_SEQUENCE_CLOSE message
|
|
*
|
|
* is_backup indicates the message is MSG_BKUP_SEQUENCE_CLOSE
|
|
*/
|
|
void ProcessSequenceCloseCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey;
|
|
StringInfoData buf;
|
|
int errcode;
|
|
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
errno_t rc = 0;
|
|
rc = memcpy_s(&seqkey.gsk_type, sizeof(GTM_SequenceKeyType), pq_getmsgbytes(message, sizeof(GTM_SequenceKeyType)), sizeof(GTM_SequenceKeyType));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
elog(DEBUG1, "Closing sequence %s", seqkey.gsk_key);
|
|
|
|
if ((errcode = GTM_SeqClose(&seqkey))) {
|
|
ereport(ERROR, (errcode, errmsg("Can not close the sequence")));
|
|
}
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
int rc;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling close_sequence() for standby GTM.");
|
|
|
|
retry:
|
|
rc = bkup_close_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "close_sequence() returns rc %d.", rc);
|
|
}
|
|
/* Respond to the client */
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_CLOSE_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, seqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, seqkey.gsk_key, seqkey.gsk_keylen);
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
/* Flush the standby first */
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
|
|
/* FIXME: need to check errors */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process MSG_SEQUENCE_RENAME/MSG_BKUP_SEQUENCE_RENAME message
|
|
*
|
|
* is_backup indicates the message is MSG_BKUP_SEQUENCE_RENAME
|
|
*/
|
|
void ProcessSequenceRenameCommand(Port* myport, StringInfo message, bool is_backup)
|
|
{
|
|
GTM_SequenceKeyData seqkey, newseqkey;
|
|
StringInfoData buf;
|
|
int errcode;
|
|
MemoryContext oldContext;
|
|
|
|
/* get the message from backend */
|
|
seqkey.gsk_keylen = pq_getmsgint(message, sizeof(seqkey.gsk_keylen));
|
|
seqkey.gsk_key = (char*)pq_getmsgbytes(message, seqkey.gsk_keylen);
|
|
|
|
/* Get the rest of the message, new name length and string with new name */
|
|
newseqkey.gsk_keylen = pq_getmsgint(message, sizeof(newseqkey.gsk_keylen));
|
|
newseqkey.gsk_key = (char*)pq_getmsgbytes(message, newseqkey.gsk_keylen);
|
|
|
|
/*
|
|
* As when creating a sequence, we must use the TopMostMemoryContext
|
|
* because the sequence information is not bound to a thread and
|
|
* can outlive any of the thread specific contextes.
|
|
*/
|
|
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
|
|
|
|
elog(LOG, "Renaming sequence %s to %s", seqkey.gsk_key, newseqkey.gsk_key);
|
|
|
|
if ((errcode = GTM_SeqRename(&seqkey, &newseqkey))) {
|
|
ereport(ERROR, (errcode, errmsg("Can not rename the sequence")));
|
|
}
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
pq_getmsgend(message);
|
|
|
|
if (!is_backup) {
|
|
/* Backup first */
|
|
if (GetMyThreadInfo->thr_conn->standby) {
|
|
int rc;
|
|
GTM_Conn* oldconn = GetMyThreadInfo->thr_conn->standby;
|
|
int count = 0;
|
|
|
|
elog(DEBUG1, "calling rename_sequence() for standby GTM.");
|
|
|
|
retry:
|
|
rc = bkup_rename_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey, &newseqkey);
|
|
|
|
if (gtm_standby_check_communication_error(&count, oldconn))
|
|
goto retry;
|
|
|
|
/* Sync */
|
|
if (Backup_synchronously && (myport->remote_type != GTM_NODE_GTM_PROXY))
|
|
gtm_sync_standby(GetMyThreadInfo->thr_conn->standby);
|
|
|
|
elog(DEBUG1, "rename_sequence() returns rc %d.", rc);
|
|
}
|
|
/* Send a SUCCESS message back to the client */
|
|
pq_beginmessage(&buf, 'S');
|
|
pq_sendint(&buf, SEQUENCE_RENAME_RESULT, 4);
|
|
if (myport->remote_type == GTM_NODE_GTM_PROXY) {
|
|
GTM_ProxyMsgHeader proxyhdr;
|
|
proxyhdr.ph_conid = myport->conn_id;
|
|
pq_sendbytes(&buf, (char*)&proxyhdr, sizeof(GTM_ProxyMsgHeader));
|
|
}
|
|
pq_sendint(&buf, newseqkey.gsk_keylen, 4);
|
|
pq_sendbytes(&buf, newseqkey.gsk_key, newseqkey.gsk_keylen);
|
|
pq_endmessage(myport, &buf);
|
|
|
|
if (myport->remote_type != GTM_NODE_GTM_PROXY) {
|
|
/* Flush the standby first */
|
|
if (GetMyThreadInfo->thr_conn->standby)
|
|
gtmpqFlush(GetMyThreadInfo->thr_conn->standby);
|
|
pq_flush(myport);
|
|
}
|
|
}
|
|
/* FIXME: need to check errors */
|
|
}
|
|
|
|
/*
|
|
* Escape whitespace and non-printable characters in the sequence name to
|
|
* store it to the control file.
|
|
*/
|
|
static void encode_seq_key(GTM_SequenceKey seqkey, char* buffer)
|
|
{
|
|
int i;
|
|
char c;
|
|
char* out;
|
|
|
|
out = buffer;
|
|
for (i = 0; i < seqkey->gsk_keylen; i++) {
|
|
c = seqkey->gsk_key[i];
|
|
|
|
if (c == '\\') /* double backslach */
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = '\\';
|
|
} else if (c > ' ') /* no need to escape */
|
|
{
|
|
*out++ = c;
|
|
} else if (c == '\n') /* below some known non-printable chars */
|
|
{
|
|
*out++ = '\\';
|
|
*out++ = 'n';
|
|
} else if (c == '\r') {
|
|
*out++ = '\\';
|
|
*out++ = 'r';
|
|
} else if (c == '\t') {
|
|
*out++ = '\\';
|
|
*out++ = 't';
|
|
} else /* other non-printable chars */
|
|
{
|
|
*out++ = '\\';
|
|
if ((int)c < 10) {
|
|
*out++ = '0';
|
|
*out++ = (char)((int)'0' + (int)c);
|
|
} else {
|
|
*out++ = (char)((int)'0' + ((int)c) / 10);
|
|
*out++ = (char)((int)'0' + ((int)c) % 10);
|
|
}
|
|
}
|
|
}
|
|
/* Add NULL terminator */
|
|
*out++ = '\0';
|
|
}
|
|
|
|
/*
|
|
* Decode the string encoded by the encode_seq_key function
|
|
*/
|
|
static void decode_seq_key(char* value, GTM_SequenceKey seqkey)
|
|
{
|
|
char* in;
|
|
char out[1024];
|
|
int len = 0;
|
|
|
|
in = value;
|
|
while (*in != '\0') {
|
|
if (*in == '\\') /* get escaped character */
|
|
{
|
|
in++; /* next value */
|
|
if (*in == '\\')
|
|
out[len++] = *in++;
|
|
else if (*in == 'n') {
|
|
out[len++] = '\n';
|
|
in++;
|
|
} else if (*in == 'r') {
|
|
out[len++] = '\r';
|
|
in++;
|
|
} else if (*in == 't') {
|
|
out[len++] = '\t';
|
|
in++;
|
|
} else /* \nn format */
|
|
{
|
|
int val;
|
|
val = ((int)*in++ - (int)'0');
|
|
val *= 10;
|
|
val += ((int)*in++ - (int)'0');
|
|
out[len++] = (char)val;
|
|
}
|
|
} else /* get plain character */
|
|
{
|
|
out[len++] = *in++;
|
|
}
|
|
}
|
|
/* copy result to palloc'ed memory */
|
|
seqkey->gsk_keylen = len;
|
|
seqkey->gsk_key = (char*)palloc(len);
|
|
errno_t rc = 0;
|
|
rc = memcpy_s(seqkey->gsk_key, len, out, len);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
|
|
static GTM_Sequence distanceToBackedUpSeqValue(GTM_SeqInfo* seqinfo)
|
|
{
|
|
if (!SEQ_IS_CALLED(seqinfo))
|
|
return (GTM_Sequence)0;
|
|
if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
if ((seqinfo->gs_backedUpValue - seqinfo->gs_value) >= 0) {
|
|
return (seqinfo->gs_backedUpValue - seqinfo->gs_value);
|
|
}
|
|
if (SEQ_IS_CYCLE(seqinfo)) {
|
|
return ((seqinfo->gs_max_value - seqinfo->gs_value) + (seqinfo->gs_backedUpValue - seqinfo->gs_min_value));
|
|
} else {
|
|
return (seqinfo->gs_backedUpValue - seqinfo->gs_value);
|
|
}
|
|
} else {
|
|
if ((seqinfo->gs_value - seqinfo->gs_backedUpValue) >= 0) {
|
|
return (seqinfo->gs_backedUpValue - seqinfo->gs_value);
|
|
}
|
|
if (SEQ_IS_CYCLE(seqinfo)) {
|
|
return ((seqinfo->gs_max_value - seqinfo->gs_backedUpValue) + (seqinfo->gs_value - seqinfo->gs_min_value));
|
|
} else {
|
|
return (seqinfo->gs_backedUpValue - seqinfo->gs_value);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool GTM_NeedSeqRestoreUpdate(GTM_SequenceKey seqkey)
|
|
{
|
|
GTM_SeqInfo* seqinfo = seq_find_seqinfo(seqkey);
|
|
if (!seqinfo) {
|
|
return FALSE;
|
|
}
|
|
return GTM_NeedSeqRestoreUpdateInternal(seqinfo);
|
|
}
|
|
|
|
static bool GTM_NeedSeqRestoreUpdateInternal(GTM_SeqInfo* seqinfo)
|
|
{
|
|
GTM_Sequence distance;
|
|
|
|
if (!SEQ_IS_CALLED(seqinfo)) {
|
|
/* The first call. Must backup */
|
|
return TRUE;
|
|
}
|
|
distance = distanceToBackedUpSeqValue(seqinfo);
|
|
if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
return (distance >= seqinfo->gs_increment_by);
|
|
} else {
|
|
return (distance <= seqinfo->gs_increment_by);
|
|
}
|
|
}
|
|
|
|
static void GTM_SaveSeqInfo2(FILE* ctlf, bool isBackup)
|
|
{
|
|
GTM_SeqInfoHashBucket* bucket;
|
|
gtm_ListCell* elem;
|
|
GTM_SeqInfo* seqinfo = NULL;
|
|
int hash;
|
|
char buffer[1024];
|
|
|
|
for (hash = 0; hash < SEQ_HASH_TABLE_SIZE; hash++) {
|
|
bucket = >MSequences[hash];
|
|
|
|
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
|
|
|
|
gtm_foreach(elem, bucket->shb_list)
|
|
{
|
|
seqinfo = (GTM_SeqInfo*)gtm_lfirst(elem);
|
|
if (seqinfo == NULL)
|
|
break;
|
|
|
|
if (seqinfo->gs_state == SEQ_STATE_DELETED)
|
|
continue;
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_READ);
|
|
|
|
encode_seq_key(seqinfo->gs_key, buffer);
|
|
fprintf(ctlf,
|
|
"%s\t%ld\t%ld\t%ld\t%ld\t%ld\t%c\t%c\t%x\n",
|
|
buffer,
|
|
isBackup ? seqinfo->gs_backedUpValue : seqinfo->gs_value,
|
|
seqinfo->gs_init_value,
|
|
seqinfo->gs_increment_by,
|
|
seqinfo->gs_min_value,
|
|
seqinfo->gs_max_value,
|
|
(seqinfo->gs_cycle ? 't' : 'f'),
|
|
(seqinfo->gs_called ? 't' : 'f'),
|
|
seqinfo->gs_state);
|
|
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
}
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
}
|
|
}
|
|
|
|
void GTM_SaveSeqInfo(FILE* ctlf)
|
|
{
|
|
GTM_SaveSeqInfo2(ctlf, FALSE);
|
|
}
|
|
|
|
static void advance_gs_value(GTM_SeqInfo* seqinfo)
|
|
{
|
|
|
|
GTM_Sequence distance;
|
|
|
|
distance = seqinfo->gs_increment_by * RestoreDuration;
|
|
if (SEQ_IS_ASCENDING(seqinfo)) {
|
|
if ((seqinfo->gs_max_value - seqinfo->gs_value) >= distance)
|
|
seqinfo->gs_backedUpValue = seqinfo->gs_value + distance;
|
|
else {
|
|
if (SEQ_IS_CYCLE(seqinfo))
|
|
seqinfo->gs_backedUpValue =
|
|
seqinfo->gs_min_value + (distance - (seqinfo->gs_max_value - seqinfo->gs_value));
|
|
else
|
|
seqinfo->gs_backedUpValue = seqinfo->gs_max_value;
|
|
}
|
|
} else {
|
|
if ((seqinfo->gs_min_value - seqinfo->gs_value) >= distance)
|
|
seqinfo->gs_backedUpValue = seqinfo->gs_value + distance;
|
|
else {
|
|
if (SEQ_IS_CYCLE(seqinfo))
|
|
seqinfo->gs_backedUpValue =
|
|
seqinfo->gs_max_value + (distance - (seqinfo->gs_min_value - seqinfo->gs_value));
|
|
else
|
|
seqinfo->gs_backedUpValue = seqinfo->gs_min_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GTM_UpdateRestorePointSeq(void)
|
|
{
|
|
GTM_SeqInfoHashBucket* bucket;
|
|
gtm_ListCell* elem;
|
|
GTM_SeqInfo* seqinfo = NULL;
|
|
int hash;
|
|
|
|
for (hash = 0; hash < SEQ_HASH_TABLE_SIZE; hash++) {
|
|
bucket = >MSequences[hash];
|
|
|
|
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
|
|
|
|
gtm_foreach(elem, bucket->shb_list)
|
|
{
|
|
seqinfo = (GTM_SeqInfo*)gtm_lfirst(elem);
|
|
if (seqinfo == NULL)
|
|
break;
|
|
|
|
if (seqinfo->gs_state == SEQ_STATE_DELETED)
|
|
continue;
|
|
|
|
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_READ);
|
|
advance_gs_value(seqinfo);
|
|
GTM_RWLockRelease(&seqinfo->gs_lock);
|
|
}
|
|
|
|
GTM_RWLockRelease(&bucket->shb_lock);
|
|
}
|
|
}
|
|
|
|
void GTM_WriteRestorePointSeq(FILE* ctlf)
|
|
{
|
|
GTM_UpdateRestorePointSeq();
|
|
GTM_SaveSeqInfo2(ctlf, TRUE);
|
|
}
|
|
|
|
void GTM_RestoreSeqInfo(FILE* ctlf)
|
|
{
|
|
char seqname[1024];
|
|
|
|
if (ctlf == NULL)
|
|
return;
|
|
|
|
while (fscanf_s(ctlf, "%s", seqname, sizeof(seqname)) == 1) {
|
|
GTM_SequenceKeyData seqkey;
|
|
GTM_Sequence increment_by;
|
|
GTM_Sequence minval;
|
|
GTM_Sequence maxval;
|
|
GTM_Sequence startval;
|
|
GTM_Sequence curval;
|
|
int32 state;
|
|
bool cycle;
|
|
bool called;
|
|
char boolval[16];
|
|
|
|
decode_seq_key(seqname, &seqkey);
|
|
|
|
if (fscanf_s(ctlf, "%ld", &curval) != 1) {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%ld", &startval) != 1) {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%ld", &increment_by) != 1) {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%ld", &minval) != 1) {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%ld", &maxval) != 1) {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%s", boolval, sizeof(boolval)) == 1) {
|
|
cycle = (*boolval == 't');
|
|
} else {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%s", boolval, sizeof(boolval)) == 1) {
|
|
called = (*boolval == 't');
|
|
} else {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
if (fscanf_s(ctlf, "%x", &state) != 1) {
|
|
elog(WARNING, "Corrupted control file");
|
|
return;
|
|
}
|
|
GTM_SeqRestore(&seqkey, increment_by, minval, maxval, startval, curval, state, cycle, called);
|
|
}
|
|
}
|