File serial.c¶
File List > cubrid > src > query > serial.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* serial.c - Serial number handling routine
*/
#ident "$Id$"
#include "config.h"
#include <assert.h>
#include <errno.h>
#include "serial.h"
#include "memory_hash.h"
#include "storage_common.h"
#include "heap_file.h"
#include "log_append.hpp"
#include "numeric_opfunc.h"
#include "object_primitive.h"
#include "record_descriptor.hpp"
#include "server_interface.h"
#include "xserver_interface.h"
#include "slotted_page.h"
#include "dbtype.h"
#include "xasl_cache.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#if !defined(SERVER_MODE)
#define pthread_mutex_init(a, b)
#define pthread_mutex_destroy(a)
#define pthread_mutex_lock(a) 0
#define pthread_mutex_unlock(a)
static int rv;
static int rc;
#endif /* !SERVER_MODE */
/* attribute of _db_serial class */
typedef enum
{
SERIAL_ATTR_UNIQUE_NAME_INDEX,
SERIAL_ATTR_NAME_INDEX,
SERIAL_ATTR_OWNER_INDEX,
SERIAL_ATTR_CURRENT_VAL_INDEX,
SERIAL_ATTR_INCREMENT_VAL_INDEX,
SERIAL_ATTR_MAX_VAL_INDEX,
SERIAL_ATTR_MIN_VAL_INDEX,
SERIAL_ATTR_CYCLIC_INDEX,
SERIAL_ATTR_STARTED_INDEX,
SERIAL_ATTR_CLASS_NAME_INDEX,
SERIAL_ATTR_ATTR_NAME_INDEX,
SERIAL_ATTR_CACHED_NUM_INDEX,
SERIAL_ATTR_MAX_INDEX
} SR_ATTRIBUTES;
#define NCACHE_OBJECTS 100
#define NOT_FOUND -1
typedef struct serial_entry SERIAL_CACHE_ENTRY;
struct serial_entry
{
OID oid; /* serial object identifier */
/* serial object values */
DB_VALUE cur_val;
DB_VALUE inc_val;
DB_VALUE max_val;
DB_VALUE min_val;
DB_VALUE cyclic;
DB_VALUE started;
int cached_num;
/* last cached value */
DB_VALUE last_cached_val;
/* free list */
struct serial_entry *next;
};
typedef struct serial_cache_area SERIAL_CACHE_AREA;
struct serial_cache_area
{
SERIAL_CACHE_ENTRY *obj_area;
struct serial_cache_area *next;
};
typedef struct serial_cache_pool SERIAL_CACHE_POOL;
struct serial_cache_pool
{
MHT_TABLE *ht; /* hash table of serial cache pool */
SERIAL_CACHE_ENTRY *free_list;
SERIAL_CACHE_AREA *area;
OID db_serial_class_oid;
pthread_mutex_t cache_pool_mutex;
};
SERIAL_CACHE_POOL serial_Cache_pool = { NULL, NULL, NULL,
{NULL_PAGEID, NULL_SLOTID, NULL_VOLID}, PTHREAD_MUTEX_INITIALIZER
};
#if defined (SERVER_MODE)
BTID serial_Cached_btid = BTID_INITIALIZER;
#endif /* SERVER_MODE */
ATTR_ID serial_Attrs_id[SERIAL_ATTR_MAX_INDEX];
int serial_Num_attrs = -1;
static int xserial_get_current_value_internal (THREAD_ENTRY * thread_p, DB_VALUE * result_num, const OID * serial_oidp);
static int xserial_get_next_value_internal (THREAD_ENTRY * thread_p, DB_VALUE * result_num, const OID * serial_oidp,
int num_alloc);
static int serial_get_next_cached_value (THREAD_ENTRY * thread_p, SERIAL_CACHE_ENTRY * entry, int num_alloc);
static int serial_update_cur_val_of_serial (THREAD_ENTRY * thread_p, SERIAL_CACHE_ENTRY * entry, int num_alloc);
static int serial_update_serial_object (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, RECDES * recdesc,
HEAP_CACHE_ATTRINFO * attr_info, const OID * serial_class_oidp,
const OID * serial_oidp, DB_VALUE * key_val);
static int serial_get_nth_value (DB_VALUE * inc_val, DB_VALUE * cur_val, DB_VALUE * min_val, DB_VALUE * max_val,
DB_VALUE * cyclic, int nth, DB_VALUE * result_val);
static void serial_set_cache_entry (SERIAL_CACHE_ENTRY * entry, DB_VALUE * inc_val, DB_VALUE * cur_val,
DB_VALUE * min_val, DB_VALUE * max_val, DB_VALUE * started, DB_VALUE * cyclic,
DB_VALUE * last_val, int cached_num);
static void serial_clear_value (SERIAL_CACHE_ENTRY * entry);
static SERIAL_CACHE_ENTRY *serial_alloc_cache_entry (void);
static SERIAL_CACHE_AREA *serial_alloc_cache_area (int num);
static int serial_load_attribute_info_of_db_serial (THREAD_ENTRY * thread_p);
// *INDENT-OFF*
static int serial_get_attrid (THREAD_ENTRY * thread_p, int attr_index, ATTR_ID &attrid);
// *INDENT-ON*
/*
* xserial_get_current_value () -
* return: NO_ERROR, or ER_code
* result_num(out) :
* oid_p(in) :
* cached_num(in) :
*/
int
xserial_get_current_value (THREAD_ENTRY * thread_p, DB_VALUE * result_num, const OID * oid_p, int cached_num)
{
int ret = NO_ERROR;
SERIAL_CACHE_ENTRY *entry;
#if defined(SERVER_MODE)
int rc;
#endif /* SERVER_MODE */
assert (oid_p != NULL);
assert (result_num != NULL);
if (cached_num <= 1)
{
/* not used serial cache */
ret = xserial_get_current_value_internal (thread_p, result_num, oid_p);
}
else
{
/* used serial cache */
rc = pthread_mutex_lock (&serial_Cache_pool.cache_pool_mutex);
entry = (SERIAL_CACHE_ENTRY *) mht_get (serial_Cache_pool.ht, oid_p);
if (entry != NULL)
{
pr_clone_value (&entry->cur_val, result_num);
}
else
{
ret = xserial_get_current_value_internal (thread_p, result_num, oid_p);
}
pthread_mutex_unlock (&serial_Cache_pool.cache_pool_mutex);
}
return ret;
}
/*
* xserial_get_current_value_internal () -
* return: NO_ERROR, or ER_code
* result_num(out) :
* serial_oidp(in) :
*/
static int
xserial_get_current_value_internal (THREAD_ENTRY * thread_p, DB_VALUE * result_num, const OID * serial_oidp)
{
int ret = NO_ERROR;
HEAP_SCANCACHE scan_cache;
SCAN_CODE scan;
RECDES recdesc = RECDES_INITIALIZER;
HEAP_CACHE_ATTRINFO attr_info, *attr_info_p = NULL;
ATTR_ID attrid;
DB_VALUE *cur_val;
OID serial_class_oid;
oid_get_serial_oid (&serial_class_oid);
heap_scancache_quick_start_with_class_oid (thread_p, &scan_cache, &serial_class_oid);
/* get record into record desc */
scan = heap_get_visible_version (thread_p, serial_oidp, &serial_class_oid, &recdesc, &scan_cache, PEEK, NULL_CHN);
if (scan != S_SUCCESS)
{
if (er_errid () == ER_PB_BAD_PAGEID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, serial_oidp->volid, serial_oidp->pageid,
serial_oidp->slotid);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_CANNOT_FETCH_SERIAL, 0);
}
goto exit_on_error;
}
/* retrieve attribute */
if (serial_get_attrid (thread_p, SERIAL_ATTR_CURRENT_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
ret = heap_attrinfo_start (thread_p, oid_Serial_class_oid, 1, &attrid, &attr_info);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
attr_info_p = &attr_info;
ret = heap_attrinfo_read_dbvalues (thread_p, serial_oidp, &recdesc, attr_info_p);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
cur_val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (cur_val, result_num);
heap_attrinfo_end (thread_p, attr_info_p);
heap_scancache_end (thread_p, &scan_cache);
return NO_ERROR;
exit_on_error:
if (attr_info_p != NULL)
{
heap_attrinfo_end (thread_p, attr_info_p);
}
heap_scancache_end (thread_p, &scan_cache);
ret = (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
return ret;
}
/*
* xserial_get_next_value () -
* return: NO_ERROR, or ER_status
* result_num(out) :
* oid_p(in) :
* cached_num(in) :
* num_alloc(in) :
* is_auto_increment(in) :
* force_set_last_insert_id(in):
*/
int
xserial_get_next_value (THREAD_ENTRY * thread_p, DB_VALUE * result_num, const OID * oid_p, int cached_num,
int num_alloc, int is_auto_increment, bool force_set_last_insert_id)
{
int ret = NO_ERROR, granted;
SERIAL_CACHE_ENTRY *entry;
bool is_cache_mutex_locked = false;
bool is_oid_locked = false;
#if defined (SERVER_MODE)
int rc;
#endif /* SERVER_MODE */
assert (oid_p != NULL);
assert (result_num != NULL);
CHECK_MODIFICATION_NO_RETURN (thread_p, ret);
if (ret != NO_ERROR)
{
return ret;
}
if (num_alloc < 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_PARAMETER, 0);
return ER_FAILED;
}
if (cached_num <= 1)
{
/* not used serial cache */
ret = xserial_get_next_value_internal (thread_p, result_num, oid_p, num_alloc);
}
else
{
/* used serial cache */
granted = LK_NOTGRANTED;
try_again:
rc = pthread_mutex_lock (&serial_Cache_pool.cache_pool_mutex);
is_cache_mutex_locked = true;
entry = (SERIAL_CACHE_ENTRY *) mht_get (serial_Cache_pool.ht, oid_p);
if (entry != NULL)
{
ret = serial_get_next_cached_value (thread_p, entry, num_alloc);
if (ret != NO_ERROR)
{
goto exit;
}
pr_clone_value (&entry->cur_val, result_num);
}
else
{
if (OID_ISNULL (&serial_Cache_pool.db_serial_class_oid))
{
ret = serial_load_attribute_info_of_db_serial (thread_p);
}
if (ret == NO_ERROR)
{
if (is_oid_locked == false)
{
granted = lock_object (thread_p, oid_p, &serial_Cache_pool.db_serial_class_oid, X_LOCK, LK_COND_LOCK);
if (granted != LK_GRANTED)
{
/* release mutex, get OID lock, and restart */
pthread_mutex_unlock (&serial_Cache_pool.cache_pool_mutex);
is_cache_mutex_locked = false;
granted =
lock_object (thread_p, oid_p, &serial_Cache_pool.db_serial_class_oid, X_LOCK, LK_UNCOND_LOCK);
if (granted == LK_GRANTED)
{
is_oid_locked = true;
goto try_again;
}
}
else
{
is_oid_locked = true;
}
}
if (granted != LK_GRANTED)
{
assert (er_errid () != NO_ERROR);
ret = er_errid ();
}
else
{
ret = xserial_get_next_value_internal (thread_p, result_num, oid_p, num_alloc);
assert (is_oid_locked == true);
(void) lock_unlock_object (thread_p, oid_p, &serial_Cache_pool.db_serial_class_oid, X_LOCK, true);
is_oid_locked = false;
}
}
}
if (is_cache_mutex_locked == true)
{
pthread_mutex_unlock (&serial_Cache_pool.cache_pool_mutex);
is_cache_mutex_locked = false;
}
}
if (ret == NO_ERROR && is_auto_increment == GENERATE_AUTO_INCREMENT)
{
/* we update current insert id for this session here */
/* Note that we ignore an error during updating current insert id. */
(void) xsession_set_cur_insert_id (thread_p, result_num, force_set_last_insert_id);
}
exit:
if (is_cache_mutex_locked)
{
pthread_mutex_unlock (&serial_Cache_pool.cache_pool_mutex);
is_cache_mutex_locked = false;
}
if (is_oid_locked)
{
(void) lock_unlock_object (thread_p, oid_p, &serial_Cache_pool.db_serial_class_oid, X_LOCK, true);
is_oid_locked = false;
}
return ret;
}
/*
* serial_get_next_cached_value () -
* return: NO_ERROR, or ER_status
* entry(in/out) :
* num_alloc(in) :
*/
static int
serial_get_next_cached_value (THREAD_ENTRY * thread_p, SERIAL_CACHE_ENTRY * entry, int num_alloc)
{
DB_VALUE cmp_result;
DB_VALUE next_val;
int error, nturns;
bool exhausted = false;
assert (1 <= num_alloc);
/* check if cached numbers were already exhausted */
if (num_alloc == 1)
{
error = numeric_db_value_compare (&entry->cur_val, &entry->last_cached_val, &cmp_result);
if (error != NO_ERROR)
{
return error;
}
if (db_get_int (&cmp_result) == 0)
{
/* entry is cached to number of cached_num */
exhausted = true;
}
}
else
{
error =
serial_get_nth_value (&entry->inc_val, &entry->cur_val, &entry->min_val, &entry->max_val, &entry->cyclic,
num_alloc, &next_val);
error = numeric_db_value_compare (&next_val, &entry->last_cached_val, &cmp_result);
if (error != NO_ERROR)
{
return error;
}
if (db_get_int (&cmp_result) >= 0)
{
exhausted = true;
}
}
/* consumed all cached value */
if (exhausted == true)
{
nturns = CEIL_PTVDIV (num_alloc, entry->cached_num);
error =
serial_get_nth_value (&entry->inc_val, &entry->last_cached_val, &entry->min_val, &entry->max_val,
&entry->cyclic, (nturns * entry->cached_num), &entry->last_cached_val);
if (error != NO_ERROR)
{
return error;
}
/* cur_val of _db_serial is updated to last_cached_val of entry */
error = serial_update_cur_val_of_serial (thread_p, entry, num_alloc);
if (error != NO_ERROR)
{
return error;
}
}
/* get next value */
if (num_alloc == 1)
{
error =
serial_get_nth_value (&entry->inc_val, &entry->cur_val, &entry->min_val, &entry->max_val, &entry->cyclic,
num_alloc, &next_val);
if (error != NO_ERROR)
{
return error;
}
}
pr_clone_value (&next_val, &entry->cur_val);
return NO_ERROR;
}
/*
* serial_update_cur_val_of_serial () -
* cur_val of _db_serial is updated to last_cached_val of entry
* return: NO_ERROR, or ER_status
* entry(in) :
*/
static int
serial_update_cur_val_of_serial (THREAD_ENTRY * thread_p, SERIAL_CACHE_ENTRY * entry, int num_alloc)
{
int ret = NO_ERROR;
HEAP_SCANCACHE scan_cache;
SCAN_CODE scan;
RECDES recdesc = RECDES_INITIALIZER;
HEAP_CACHE_ATTRINFO attr_info, *attr_info_p = NULL;
DB_VALUE *val;
DB_VALUE key_val;
ATTR_ID attrid;
OID serial_class_oid;
db_make_null (&key_val);
CHECK_MODIFICATION_NO_RETURN (thread_p, ret);
if (ret != NO_ERROR)
{
return ret;
}
oid_get_serial_oid (&serial_class_oid);
heap_scancache_quick_start_modify_with_class_oid (thread_p, &scan_cache, &serial_class_oid);
scan = heap_get_visible_version (thread_p, &entry->oid, &serial_class_oid, &recdesc, &scan_cache, PEEK, NULL_CHN);
if (scan != S_SUCCESS)
{
if (er_errid () == ER_PB_BAD_PAGEID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, entry->oid.volid, entry->oid.pageid,
entry->oid.slotid);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_CANNOT_FETCH_SERIAL, 0);
}
goto exit_on_error;
}
/* retrieve attribute */
ret = heap_attrinfo_start (thread_p, oid_Serial_class_oid, -1, NULL, &attr_info);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
ret = heap_attrinfo_read_dbvalues (thread_p, &entry->oid, &recdesc, &attr_info);
if (ret != NO_ERROR)
{
heap_attrinfo_end (thread_p, &attr_info);
goto exit_on_error;
}
attr_info_p = &attr_info;
if (serial_get_attrid (thread_p, SERIAL_ATTR_UNIQUE_NAME_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_clone_value (val, &key_val);
if (serial_get_attrid (thread_p, SERIAL_ATTR_CURRENT_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
ret = heap_attrinfo_set (&entry->oid, attrid, &entry->last_cached_val, attr_info_p);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
ret =
serial_update_serial_object (thread_p, scan_cache.page_watcher.pgptr, &recdesc, attr_info_p, oid_Serial_class_oid,
&entry->oid, &key_val);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) > 0 && thread_p->no_supplemental_log == false)
{
LOG_TDES *tdes = LOG_FIND_CURRENT_TDES (thread_p);
if (tdes)
{
if (!tdes->has_supplemental_log)
{
log_append_supplemental_info (thread_p, LOG_SUPPLEMENT_TRAN_USER, strlen (tdes->client.get_db_user ()),
tdes->client.get_db_user ());
tdes->has_supplemental_log = true;
}
}
(void) log_append_supplemental_serial (thread_p, db_get_string (&key_val), num_alloc, NULL, &entry->oid);
}
pr_clear_value (&key_val);
heap_attrinfo_end (thread_p, attr_info_p);
heap_scancache_end (thread_p, &scan_cache);
return NO_ERROR;
exit_on_error:
pr_clear_value (&key_val);
if (attr_info_p != NULL)
{
heap_attrinfo_end (thread_p, attr_info_p);
}
heap_scancache_end (thread_p, &scan_cache);
return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}
/*
* xserial_get_next_value_internal () -
* return: NO_ERROR, or ER_status
* result_num(out) :
* serial_oidp(in) :
*/
static int
xserial_get_next_value_internal (THREAD_ENTRY * thread_p, DB_VALUE * result_num, const OID * serial_oidp, int num_alloc)
{
int ret = NO_ERROR;
HEAP_SCANCACHE scan_cache;
SCAN_CODE scan;
RECDES recdesc = RECDES_INITIALIZER;
HEAP_CACHE_ATTRINFO attr_info, *attr_info_p = NULL;
DB_VALUE *val = NULL;
DB_VALUE cur_val;
DB_VALUE inc_val;
DB_VALUE max_val;
DB_VALUE min_val;
DB_VALUE cyclic;
DB_VALUE started;
DB_VALUE next_val;
DB_VALUE key_val;
DB_VALUE last_val;
int cached_num, nturns;
SERIAL_CACHE_ENTRY *entry = NULL;
ATTR_ID attrid;
OID serial_class_oid;
bool is_started;
db_make_null (&key_val);
oid_get_serial_oid (&serial_class_oid);
heap_scancache_quick_start_modify_with_class_oid (thread_p, &scan_cache, &serial_class_oid);
scan = heap_get_visible_version (thread_p, serial_oidp, &serial_class_oid, &recdesc, &scan_cache, PEEK, NULL_CHN);
if (scan != S_SUCCESS)
{
if (er_errid () == ER_PB_BAD_PAGEID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, serial_oidp->volid, serial_oidp->pageid,
serial_oidp->slotid);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_CANNOT_FETCH_SERIAL, 0);
}
goto exit_on_error;
}
/* retrieve attribute */
ret = heap_attrinfo_start (thread_p, oid_Serial_class_oid, -1, NULL, &attr_info);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
ret = heap_attrinfo_read_dbvalues (thread_p, serial_oidp, &recdesc, &attr_info);
attr_info_p = &attr_info;
if (serial_get_attrid (thread_p, SERIAL_ATTR_CACHED_NUM_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
if (attrid == NOT_FOUND)
{
cached_num = 0;
}
else
{
val = heap_attrinfo_access (attrid, attr_info_p);
cached_num = db_get_int (val);
}
if (serial_get_attrid (thread_p, SERIAL_ATTR_UNIQUE_NAME_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_clone_value (val, &key_val);
if (serial_get_attrid (thread_p, SERIAL_ATTR_CURRENT_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (val, &cur_val);
if (serial_get_attrid (thread_p, SERIAL_ATTR_INCREMENT_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (val, &inc_val);
if (serial_get_attrid (thread_p, SERIAL_ATTR_MAX_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (val, &max_val);
if (serial_get_attrid (thread_p, SERIAL_ATTR_MIN_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (val, &min_val);
if (serial_get_attrid (thread_p, SERIAL_ATTR_CYCLIC_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (val, &cyclic);
if (serial_get_attrid (thread_p, SERIAL_ATTR_STARTED_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
val = heap_attrinfo_access (attrid, attr_info_p);
pr_share_value (val, &started);
db_make_null (&last_val);
is_started = db_get_int (&started);
if (db_get_int (&started) == 0)
{
/* This is the first time to generate the serial value. */
db_make_int (&started, 1);
if (serial_get_attrid (thread_p, SERIAL_ATTR_STARTED_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
ret = heap_attrinfo_set (serial_oidp, attrid, &started, attr_info_p);
if (ret == NO_ERROR)
{
pr_share_value (&cur_val, &next_val);
if (cached_num > 1)
{
assert (1 <= num_alloc);
nturns = CEIL_PTVDIV (num_alloc, cached_num);
ret =
serial_get_nth_value (&inc_val, &cur_val, &min_val, &max_val, &cyclic, (nturns * (cached_num - 1)),
&last_val);
}
num_alloc--;
}
}
if (num_alloc > 0)
{
if (cached_num > 1)
{
nturns = CEIL_PTVDIV (num_alloc, cached_num);
ret =
serial_get_nth_value (&inc_val, &cur_val, &min_val, &max_val, &cyclic, (nturns * cached_num), &last_val);
}
if (ret == NO_ERROR)
{
ret = serial_get_nth_value (&inc_val, &cur_val, &min_val, &max_val, &cyclic, num_alloc, &next_val);
}
}
if (ret != NO_ERROR)
{
goto exit_on_error;
}
/* Now update record */
if (serial_get_attrid (thread_p, SERIAL_ATTR_CURRENT_VAL_INDEX, attrid) != NO_ERROR)
{
goto exit_on_error;
}
assert (attrid != NOT_FOUND);
if (cached_num > 1 && !DB_IS_NULL (&last_val))
{
ret = heap_attrinfo_set (serial_oidp, attrid, &last_val, attr_info_p);
}
else
{
ret = heap_attrinfo_set (serial_oidp, attrid, &next_val, attr_info_p);
}
if (ret != NO_ERROR)
{
goto exit_on_error;
}
ret =
serial_update_serial_object (thread_p, scan_cache.page_watcher.pgptr, &recdesc, attr_info_p, oid_Serial_class_oid,
serial_oidp, &key_val);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) > 0 && thread_p->no_supplemental_log == false)
{
LOG_TDES *tdes = LOG_FIND_CURRENT_TDES (thread_p);
if (tdes)
{
if (!tdes->has_supplemental_log)
{
log_append_supplemental_info (thread_p, LOG_SUPPLEMENT_TRAN_USER, strlen (tdes->client.get_db_user ()),
tdes->client.get_db_user ());
tdes->has_supplemental_log = true;
}
}
(void) log_append_supplemental_serial (thread_p, db_get_string (&key_val), is_started ? num_alloc : num_alloc + 1,
NULL, serial_oidp);
}
/* copy result value */
pr_share_value (&next_val, result_num);
pr_clear_value (&key_val);
heap_attrinfo_end (thread_p, attr_info_p);
heap_scancache_end (thread_p, &scan_cache);
if (cached_num > 1)
{
entry = serial_alloc_cache_entry ();
if (entry != NULL)
{
COPY_OID (&entry->oid, serial_oidp);
assert (mht_get (serial_Cache_pool.ht, &entry->oid) == NULL);
if (mht_put (serial_Cache_pool.ht, &entry->oid, entry) == NULL)
{
OID_SET_NULL (&entry->oid);
entry->next = serial_Cache_pool.free_list;
serial_Cache_pool.free_list = entry;
entry = NULL;
}
else
{
pr_share_value (&next_val, &cur_val);
serial_set_cache_entry (entry, &inc_val, &cur_val, &min_val, &max_val, &started, &cyclic, &last_val,
cached_num);
}
}
}
return NO_ERROR;
exit_on_error:
pr_clear_value (&key_val);
if (attr_info_p != NULL)
{
heap_attrinfo_end (thread_p, attr_info_p);
}
heap_scancache_end (thread_p, &scan_cache);
return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}
/*
* serial_update_serial_object () -
* return: NO_ERROR or error status
* pgptr(in) :
* recdesc(in) :
* attr_info(in) :
* serial_class_oidp(in) :
* serial_oidp(in) :
* key_val(in) :
*/
static int
serial_update_serial_object (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, RECDES * recdesc, HEAP_CACHE_ATTRINFO * attr_info,
const OID * serial_class_oidp, const OID * serial_oidp, DB_VALUE * key_val)
{
// *INDENT-OFF*
cubmem::stack_block<IO_MAX_PAGE_SIZE> copyarea;
// *INDENT-ON*
record_descriptor new_recdesc;
SCAN_CODE scan;
LOG_DATA_ADDR addr;
int ret;
int sp_success;
int tran_index;
LOCK lock_mode = NULL_LOCK;
assert_release (serial_class_oidp != NULL && !OID_ISNULL (serial_class_oidp));
tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
lock_mode = lock_get_object_lock (serial_oidp, serial_class_oidp);
/* need to start topop for replication Replication will recognize and realize a special type of update for serial by
* this top operation log record. If lock_mode is X_LOCK means we created or altered the serial obj in an
* uncommitted trans For this case, topop and flush mark are not used, since these may cause problem with replication
* log. */
if (lock_mode != X_LOCK)
{
log_sysop_start (thread_p);
}
if (lock_mode != X_LOCK && !LOG_CHECK_LOG_APPLIER (thread_p) && log_does_allow_replication () == true)
{
repl_start_flush_mark (thread_p);
}
new_recdesc.set_external_buffer (copyarea);
scan = heap_attrinfo_transform_to_disk (thread_p, attr_info, recdesc, &new_recdesc);
if (scan != S_SUCCESS)
{
assert (false);
goto exit_on_error;
}
/* Log the changes */
new_recdesc.set_type (recdesc->type);
addr.offset = serial_oidp->slotid;
addr.pgptr = pgptr;
assert (spage_is_updatable (thread_p, addr.pgptr, serial_oidp->slotid, (int) new_recdesc.get_size ()));
log_append_redo_recdes (thread_p, RVHF_UPDATE, &addr, &new_recdesc.get_recdes ());
/* Now really update */
sp_success = spage_update (thread_p, addr.pgptr, serial_oidp->slotid, &new_recdesc.get_recdes ());
if (sp_success != SP_SUCCESS)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_CANNOT_UPDATE_SERIAL, 0);
goto exit_on_error;
}
/* make replication log for the special type of update for serial */
if (!LOG_CHECK_LOG_APPLIER (thread_p) && log_does_allow_replication () == true)
{
repl_log_insert (thread_p, serial_class_oidp, serial_oidp, LOG_REPLICATION_DATA, RVREPL_DATA_UPDATE, key_val,
REPL_INFO_TYPE_RBR_NORMAL);
repl_add_update_lsa (thread_p, serial_oidp);
if (lock_mode != X_LOCK)
{
repl_end_flush_mark (thread_p, false);
}
}
if (lock_mode != X_LOCK)
{
log_sysop_commit (thread_p);
}
return NO_ERROR;
exit_on_error:
if (lock_mode != X_LOCK)
{
log_sysop_abort (thread_p);
}
ASSERT_ERROR_AND_SET (ret);
return ret;
}
/*
* serial_get_nth_value () - get Nth next_value
* return: NO_ERROR, or ER_status
* inc_val(in) :
* cur_val(in) :
* min_val(in) :
* max_val(in) :
* cyclic(in) :
* nth(in) :
* result_val(out) :
*/
static int
serial_get_nth_value (DB_VALUE * inc_val, DB_VALUE * cur_val, DB_VALUE * min_val, DB_VALUE * max_val, DB_VALUE * cyclic,
int nth, DB_VALUE * result_val)
{
DB_VALUE tmp_val, cmp_result, add_val;
unsigned char num[DB_NUMERIC_BUF_SIZE];
int inc_val_flag;
int ret;
inc_val_flag = numeric_db_value_is_positive (inc_val);
if (inc_val_flag < 0)
{
return ER_FAILED;
}
/* Now calculate next value */
if (nth > 1)
{
numeric_coerce_int_to_num (nth, num);
db_make_numeric (&tmp_val, num, DB_MAX_NUMERIC_PRECISION, 0);
numeric_db_value_mul (inc_val, &tmp_val, &add_val);
}
else
{
pr_share_value (inc_val, &add_val);
}
/* inc_val_flag (1 or 0) */
if (inc_val_flag > 0)
{
ret = numeric_db_value_sub (max_val, &add_val, &tmp_val);
if (ret != NO_ERROR)
{
return ret;
}
ret = numeric_db_value_compare (cur_val, &tmp_val, &cmp_result);
if (ret != NO_ERROR)
{
return ret;
}
/* cur_val + inc_val * cached_num > max_val */
if (db_get_int (&cmp_result) > 0)
{
if (db_get_int (cyclic))
{
pr_share_value (min_val, result_val);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_SERIAL_RANGE_OVERFLOW, 0);
return ER_QPROC_SERIAL_RANGE_OVERFLOW;
}
}
else
{
(void) numeric_db_value_add (cur_val, &add_val, result_val);
}
}
else
{
ret = numeric_db_value_sub (min_val, &add_val, &tmp_val);
if (ret != NO_ERROR)
{
return ret;
}
ret = numeric_db_value_compare (cur_val, &tmp_val, &cmp_result);
if (ret != NO_ERROR)
{
return ret;
}
/* cur_val + inc_val * cached_num < min_val */
if (db_get_int (&cmp_result) < 0)
{
if (db_get_int (cyclic))
{
pr_share_value (max_val, result_val);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_SERIAL_RANGE_OVERFLOW, 0);
return ER_QPROC_SERIAL_RANGE_OVERFLOW;
}
}
else
{
(void) numeric_db_value_add (cur_val, &add_val, result_val);
}
}
return NO_ERROR;
}
/*
* serial_initialize_cache_pool () -
* return: NO_ERROR, or ER_status
*/
int
serial_initialize_cache_pool (THREAD_ENTRY * thread_p)
{
unsigned int i;
if (serial_Cache_pool.ht != NULL)
{
serial_finalize_cache_pool ();
}
serial_Cache_pool.area = serial_alloc_cache_area (NCACHE_OBJECTS);
if (serial_Cache_pool.area == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
serial_Cache_pool.free_list = serial_Cache_pool.area->obj_area;
pthread_mutex_init (&serial_Cache_pool.cache_pool_mutex, NULL);
serial_Cache_pool.ht = mht_create ("Serial cache pool hash table", NCACHE_OBJECTS * 8, oid_hash, oid_compare_equals);
if (serial_Cache_pool.ht == NULL)
{
serial_finalize_cache_pool ();
return ER_FAILED;
}
for (i = 0; i < sizeof (serial_Attrs_id) / sizeof (ATTR_ID); i++)
{
serial_Attrs_id[i] = -1;
}
return NO_ERROR;
}
/*
* serial_finalize_cache_pool () -
* return:
*/
void
serial_finalize_cache_pool (void)
{
SERIAL_CACHE_AREA *tmp_area;
serial_Cache_pool.free_list = NULL;
if (serial_Cache_pool.ht != NULL)
{
mht_destroy (serial_Cache_pool.ht);
serial_Cache_pool.ht = NULL;
}
while (serial_Cache_pool.area)
{
tmp_area = serial_Cache_pool.area;
serial_Cache_pool.area = serial_Cache_pool.area->next;
free_and_init (tmp_area->obj_area);
free_and_init (tmp_area);
}
pthread_mutex_destroy (&serial_Cache_pool.cache_pool_mutex);
serial_Num_attrs = -1;
}
/*
* serial_get_attrid () -
* return: attribute id or NOT_FOUND
*
* attr_index(in) :
*/
// *INDENT-OFF*
static int
serial_get_attrid (THREAD_ENTRY * thread_p, int attr_index, ATTR_ID &attrid)
{
attrid = NOT_FOUND;
if (serial_Num_attrs < 0)
{
int error = serial_load_attribute_info_of_db_serial (thread_p);
if (error != NO_ERROR)
{
ASSERT_ERROR ();
return error;
}
}
if (attr_index >= 0 && attr_index <= serial_Num_attrs)
{
attrid = serial_Attrs_id[attr_index];
}
return NO_ERROR;
}
// *INDENT-ON*
/*
* serial_load_attribute_info_of_db_serial () -
* return: NO_ERROR, or ER_status
*/
static int
serial_load_attribute_info_of_db_serial (THREAD_ENTRY * thread_p)
{
HEAP_SCANCACHE scan;
RECDES class_record;
HEAP_CACHE_ATTRINFO attr_info;
int i, error = NO_ERROR;
char *attr_name_p, *string = NULL;
int alloced_string = 0;
serial_Num_attrs = -1;
oid_get_serial_oid (&serial_Cache_pool.db_serial_class_oid);
if (heap_scancache_quick_start_with_class_oid (thread_p, &scan, &serial_Cache_pool.db_serial_class_oid) != NO_ERROR)
{
return ER_FAILED;
}
if (heap_get_class_record (thread_p, &serial_Cache_pool.db_serial_class_oid, &class_record, &scan, PEEK) != S_SUCCESS)
{
heap_scancache_end (thread_p, &scan);
return ER_FAILED;
}
error = heap_attrinfo_start (thread_p, &serial_Cache_pool.db_serial_class_oid, -1, NULL, &attr_info);
if (error != NO_ERROR)
{
(void) heap_scancache_end (thread_p, &scan);
return error;
}
for (i = 0; i < attr_info.num_values; i++)
{
string = NULL;
alloced_string = 0;
error = or_get_attrname (&class_record, i, &string, &alloced_string);
if (error != NO_ERROR)
{
ASSERT_ERROR ();
goto exit_on_error;
}
attr_name_p = string;
if (attr_name_p == NULL)
{
error = ER_FAILED;
goto exit_on_error;
}
if (strcmp (attr_name_p, SERIAL_ATTR_UNIQUE_NAME) == 0)
{
serial_Attrs_id[SERIAL_ATTR_UNIQUE_NAME_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_NAME) == 0)
{
serial_Attrs_id[SERIAL_ATTR_NAME_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_OWNER) == 0)
{
serial_Attrs_id[SERIAL_ATTR_OWNER_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_CURRENT_VAL) == 0)
{
serial_Attrs_id[SERIAL_ATTR_CURRENT_VAL_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_INCREMENT_VAL) == 0)
{
serial_Attrs_id[SERIAL_ATTR_INCREMENT_VAL_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_MAX_VAL) == 0)
{
serial_Attrs_id[SERIAL_ATTR_MAX_VAL_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_MIN_VAL) == 0)
{
serial_Attrs_id[SERIAL_ATTR_MIN_VAL_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_CYCLIC) == 0)
{
serial_Attrs_id[SERIAL_ATTR_CYCLIC_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_STARTED) == 0)
{
serial_Attrs_id[SERIAL_ATTR_STARTED_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_CLASS_NAME) == 0)
{
serial_Attrs_id[SERIAL_ATTR_CLASS_NAME_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_ATTR_NAME) == 0)
{
serial_Attrs_id[SERIAL_ATTR_ATTR_NAME_INDEX] = i;
}
else if (strcmp (attr_name_p, SERIAL_ATTR_CACHED_NUM) == 0)
{
serial_Attrs_id[SERIAL_ATTR_CACHED_NUM_INDEX] = i;
}
if (string != NULL && alloced_string)
{
db_private_free_and_init (NULL, string);
}
}
serial_Num_attrs = attr_info.num_values;
heap_attrinfo_end (thread_p, &attr_info);
error = heap_scancache_end (thread_p, &scan);
return error;
exit_on_error:
heap_attrinfo_end (thread_p, &attr_info);
(void) heap_scancache_end (thread_p, &scan);
return error;
}
/*
* serial_set_cache_entry () -
* return:
* entry(out) :
* inc_val(in) :
* cur_val(in) :
* min_val(in) :
* max_val(in) :
* started(in) :
* cyclic(in) :
* last_val(in):
* cached_num(in):
*/
static void
serial_set_cache_entry (SERIAL_CACHE_ENTRY * entry, DB_VALUE * inc_val, DB_VALUE * cur_val, DB_VALUE * min_val,
DB_VALUE * max_val, DB_VALUE * started, DB_VALUE * cyclic, DB_VALUE * last_val, int cached_num)
{
pr_clone_value (cur_val, &entry->cur_val);
pr_clone_value (inc_val, &entry->inc_val);
pr_clone_value (max_val, &entry->max_val);
pr_clone_value (min_val, &entry->min_val);
pr_clone_value (cyclic, &entry->cyclic);
pr_clone_value (last_val, &entry->last_cached_val);
pr_clone_value (started, &entry->started);
entry->cached_num = cached_num;
}
/*
* serial_clear_value () - clear all value of cache entry
* return:
* entry(in/out) :
*/
static void
serial_clear_value (SERIAL_CACHE_ENTRY * entry)
{
pr_clear_value (&entry->cur_val);
pr_clear_value (&entry->inc_val);
pr_clear_value (&entry->max_val);
pr_clear_value (&entry->min_val);
pr_clear_value (&entry->cyclic);
pr_clear_value (&entry->started);
entry->cached_num = 0;
}
/*
* serial_alloc_cache_entry () -
* return:
*/
static SERIAL_CACHE_ENTRY *
serial_alloc_cache_entry (void)
{
SERIAL_CACHE_ENTRY *entry;
SERIAL_CACHE_AREA *tmp_area;
if (serial_Cache_pool.free_list == NULL)
{
tmp_area = serial_alloc_cache_area (NCACHE_OBJECTS);
if (tmp_area == NULL)
{
return NULL;
}
tmp_area->next = serial_Cache_pool.area;
serial_Cache_pool.area = tmp_area;
serial_Cache_pool.free_list = tmp_area->obj_area;
}
entry = serial_Cache_pool.free_list;
serial_Cache_pool.free_list = serial_Cache_pool.free_list->next;
return entry;
}
/*
* xserial_decache () - decache cache entry of cache pool
* return:
* oidp(in) :
*/
void
xserial_decache (THREAD_ENTRY * thread_p, OID * oidp)
{
SERIAL_CACHE_ENTRY *entry;
#if defined (SERVER_MODE)
int rc;
#endif /* SERVER_MODE */
xcache_remove_by_oid (thread_p, oidp);
rc = pthread_mutex_lock (&serial_Cache_pool.cache_pool_mutex);
entry = (SERIAL_CACHE_ENTRY *) mht_get (serial_Cache_pool.ht, oidp);
if (entry != NULL)
{
mht_rem (serial_Cache_pool.ht, oidp, NULL, NULL);
OID_SET_NULL (&entry->oid);
serial_clear_value (entry);
entry->next = serial_Cache_pool.free_list;
serial_Cache_pool.free_list = entry;
}
pthread_mutex_unlock (&serial_Cache_pool.cache_pool_mutex);
}
/*
* serial_alloc_cache_area () -
* return:
* num(in) :
*/
static SERIAL_CACHE_AREA *
serial_alloc_cache_area (int num)
{
SERIAL_CACHE_AREA *tmp_area;
int i;
tmp_area = (SERIAL_CACHE_AREA *) malloc (sizeof (SERIAL_CACHE_AREA));
if (tmp_area == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SERIAL_CACHE_AREA));
return NULL;
}
tmp_area->next = NULL;
tmp_area->obj_area = ((SERIAL_CACHE_ENTRY *) malloc (sizeof (SERIAL_CACHE_ENTRY) * num));
if (tmp_area->obj_area == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SERIAL_CACHE_ENTRY) * num);
free_and_init (tmp_area);
return NULL;
}
/* make free list */
for (i = 0; i < num - 1; i++)
{
tmp_area->obj_area[i].next = &tmp_area->obj_area[i + 1];
}
tmp_area->obj_area[i].next = NULL;
return tmp_area;
}
#if defined (SERVER_MODE)
/*
* serial_cache_index_btid () - Cache serial index BTID.
*
* return : Error Code.
* thread_p (in) : Thread entry.
*
* NOTE that workspace manager is unavailable when restarting from backup.
* It is possible to allow SA_MODE executables except restoredb to use the function,
* however, it is better not to use it in SA_MODE for clarity.
*/
int
serial_cache_index_btid (THREAD_ENTRY * thread_p)
{
int error_code = NO_ERROR;
OID serial_oid = OID_INITIALIZER;
/* Get serial class OID. */
oid_get_serial_oid (&serial_oid);
assert (!OID_ISNULL (&serial_oid));
/* Now try to get index BTID. */
error_code = heap_get_btid_from_index_name (thread_p, &serial_oid, "pk_db_serial_unique_name", &serial_Cached_btid);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
/* Safe guard: successfully read a non-NULL BTID. */
assert (!BTID_IS_NULL (&serial_Cached_btid));
return NO_ERROR;
}
/*
* serial_get_index_btid () - Get serial index BTID.
*
* return : Void.
* output (in) : Serial index btid.
*
* NOTE that workspace manager is unavailable when restarting from backup.
* It is possible to allow SA_MODE executables except restoredb to use the function,
* however, it is better not to use it in SA_MODE for clarity.
*/
void
serial_get_index_btid (BTID * output)
{
/* Safe guard: a non-NULL serial index BTID is cached. */
assert (!BTID_IS_NULL (&serial_Cached_btid));
/* Safe guard: output parameter for index BTID is not NULL. */
assert (output != NULL);
BTID_COPY (output, &serial_Cached_btid);
}
#endif /* SERVER_MODE */