/* ------------------------------------------------------------------------- * * 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 #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); } }