File system_catalog.c¶
File List > cubrid > src > storage > system_catalog.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.
*
*/
/*
* system_catalog.c - Catalog manager
*/
#ident "$Id$"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "system_catalog.h"
#include "error_manager.h"
#include "file_manager.h"
#include "log_append.hpp"
#include "slotted_page.h"
#include "extendible_hash.h"
#include "boot_sr.h"
#include "btree_load.h"
#include "heap_file.h"
#include "xserver_interface.h"
#include "statistics_sr.h"
#include "partition_sr.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "thread_lockfree_hash_map.hpp"
#include "thread_manager.hpp"
// 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_trylock(a) 0
#define pthread_mutex_unlock(a)
static int rv;
#endif /* not SERVER_MODE */
#define CATALOG_HEADER_SLOT 0
#define CATALOG_MAX_SLOT_ID_SIZE 12
#define CATALOG_HASH_SIZE 1000
#define CATALOG_KEY_VALUE_ARRAY_SIZE 1000
#define CATALOG_PGHEADER_OVFL_PGID_PAGEID_OFF 0
#define CATALOG_PGHEADER_OVFL_PGID_VOLID_OFF 4
#define CATALOG_PGHEADER_DIR_CNT_OFF 8
#define CATALOG_PGHEADER_PG_OVFL_OFF 12
#define CATALOG_PAGE_HEADER_SIZE 16
/* READERS for CATALOG_PAGE_HEADER related fields */
#define CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID(ptr) \
(PAGEID) OR_GET_INT ((ptr) + CATALOG_PGHEADER_OVFL_PGID_PAGEID_OFF)
#define CATALOG_GET_PGHEADER_OVFL_PGID_VOLID(ptr) \
(VOLID) OR_GET_SHORT ((ptr) + CATALOG_PGHEADER_OVFL_PGID_VOLID_OFF)
#define CATALOG_GET_PGHEADER_DIR_COUNT(ptr) \
(int) OR_GET_INT ((ptr) + CATALOG_PGHEADER_DIR_CNT_OFF)
#define CATALOG_GET_PGHEADER_PG_OVFL(ptr) \
(bool) OR_GET_INT ((ptr) + CATALOG_PGHEADER_PG_OVFL_OFF)
/* WRITERS for CATALOG_PAGE_HEADER related fields */
#define CATALOG_PUT_PGHEADER_OVFL_PGID_PAGEID(ptr,val) \
OR_PUT_INT ((ptr) + CATALOG_PGHEADER_OVFL_PGID_PAGEID_OFF, (val))
#define CATALOG_PUT_PGHEADER_OVFL_PGID_VOLID(ptr,val) \
OR_PUT_SHORT ((ptr) + CATALOG_PGHEADER_OVFL_PGID_VOLID_OFF, (val))
#define CATALOG_PUT_PGHEADER_DIR_COUNT(ptr,val) \
OR_PUT_INT ((ptr) + CATALOG_PGHEADER_DIR_CNT_OFF, (val))
#define CATALOG_PUT_PGHEADER_PG_OVFL(ptr,val) \
OR_PUT_INT ((ptr) + CATALOG_PGHEADER_PG_OVFL_OFF, (int) (val))
/* Each disk representation is aligned with MAX_ALIGNMENT */
#define CATALOG_DISK_REPR_ID_OFF 0
#define CATALOG_DISK_REPR_N_FIXED_OFF 4
#define CATALOG_DISK_REPR_FIXED_LENGTH_OFF 8
#define CATALOG_DISK_REPR_N_VARIABLE_OFF 12
#define CATALOG_DISK_REPR_RESERVED_1_OFF 16 /* reserved for future use */
#define CATALOG_DISK_REPR_SIZE 56
/* Each disk attribute is aligned with MAX_ALIGNMENT
Each disk attribute may be followed by a "value" which is of
variable size. The below constants does not consider the
optional value field following the attribute structure. */
#define CATALOG_DISK_ATTR_ID_OFF 0
#define CATALOG_DISK_ATTR_LOCATION_OFF 4
#define CATALOG_DISK_ATTR_TYPE_OFF 8
#define CATALOG_DISK_ATTR_VAL_LENGTH_OFF 12
#define CATALOG_DISK_ATTR_POSITION_OFF 16
#define CATALOG_DISK_ATTR_CLASSOID_OFF 20
#define CATALOG_DISK_ATTR_N_BTSTATS_OFF 28
#define CATALOG_DISK_ATTR_NDV_OFF 80
#define CATALOG_DISK_ATTR_SIZE 88
#define CATALOG_BT_STATS_BTID_OFF 0
#define CATALOG_BT_STATS_LEAFS_OFF OR_BTID_ALIGNED_SIZE
#define CATALOG_BT_STATS_PAGES_OFF 16
#define CATALOG_BT_STATS_HEIGHT_OFF 20
#define CATALOG_BT_STATS_KEYS_OFF 24
#define CATALOG_BT_STATS_FUNC_INDEX_OFF 28
#define CATALOG_BT_STATS_PKEYS_OFF 32
#define CATALOG_BT_STATS_RESERVED_OFF (CATALOG_BT_STATS_PKEYS_OFF + (OR_INT_SIZE * BTREE_STATS_PKEYS_NUM)) /* 64 */
#define CATALOG_BT_STATS_SIZE (CATALOG_BT_STATS_RESERVED_OFF + (OR_INT_SIZE * BTREE_STATS_RESERVED_NUM)) /* 64 + (4 * R_NUM) = 80 */
#define CATALOG_GET_BT_STATS_BTID(var, ptr) \
OR_GET_BTID((ptr) + CATALOG_BT_STATS_BTID_OFF, (var))
#define CATALOG_CLS_INFO_HFID_OFF 0
#define CATALOG_CLS_INFO_TOT_PAGES_OFF 12
#define CATALOG_CLS_INFO_TOT_OBJS_OFF 16
#define CATALOG_CLS_INFO_TIME_STAMP_OFF 20
#define CATALOG_CLS_INFO_REP_DIR_OFF 24
#define CATALOG_CLS_INFO_SIZE 56
#define CATALOG_CLS_INFO_RESERVED 24
#define CATALOG_REPR_ITEM_PAGEID_PAGEID_OFF 0
#define CATALOG_REPR_ITEM_PAGEID_VOLID_OFF 4
#define CATALOG_REPR_ITEM_REPRID_OFF 8
#define CATALOG_REPR_ITEM_SLOTID_OFF 10
#define CATALOG_REPR_ITEM_COUNT_OFF 12
#define CATALOG_REPR_ITEM_SIZE 16
#define CATALOG_GET_REPR_ITEM_PAGEID_PAGEID(ptr) \
(PAGEID) OR_GET_INT ((ptr) + CATALOG_REPR_ITEM_PAGEID_PAGEID_OFF)
#define CATALOG_GET_REPR_ITEM_PAGEID_VOLID(ptr) \
(VOLID) OR_GET_SHORT ((ptr) + CATALOG_REPR_ITEM_PAGEID_VOLID_OFF)
#define CATALOG_GET_REPR_ITEM_REPRID(ptr) \
(REPR_ID) OR_GET_SHORT ((ptr) + CATALOG_REPR_ITEM_REPRID_OFF)
#define CATALOG_GET_REPR_ITEM_SLOTID(ptr) \
(PGSLOTID) OR_GET_SHORT ((ptr) + CATALOG_REPR_ITEM_SLOTID_OFF)
#define CATALOG_GET_REPR_ITEM_COUNT(ptr) \
(PGSLOTID) OR_GET_BYTE ((ptr) + CATALOG_REPR_ITEM_COUNT_OFF)
/* catalog estimated max. space information */
typedef struct catalog_max_space CATALOG_MAX_SPACE;
struct catalog_max_space
{
VPID max_page_id; /* estimated maximum space page identifier */
PGLENGTH max_space; /* estimated maximum space */
};
/* catalog key for the hash table */
typedef struct catalog_key CATALOG_KEY;
struct catalog_key
{
/* actual key */
PAGEID page_id;
VOLID volid;
PGSLOTID slot_id;
REPR_ID repr_id;
/* these are part of the data, but we want them inserted atomically */
VPID r_page_id; /* location of representation */
PGSLOTID r_slot_id;
}; /* class identifier + representation identifier */
/* catalog value for the hash table */
typedef struct catalog_entry CATALOG_ENTRY;
struct catalog_entry
{
CATALOG_ENTRY *stack; /* used for freelist */
CATALOG_ENTRY *next; /* next in hash chain */
UINT64 del_id; /* delete transaction ID (for lock free) */
CATALOG_KEY key; /* key of catalog entry */
};
/* handling functions for catalog key and entry */
static void *catalog_entry_alloc (void);
static int catalog_entry_free (void *ent);
static int catalog_entry_init (void *ent);
static int catalog_entry_uninit (void *ent);
static int catalog_key_copy (void *src, void *dest);
static int catalog_key_compare (void *key1, void *key2);
static unsigned int catalog_key_hash (void *key, int htsize);
/* catalog entry descriptor */
static LF_ENTRY_DESCRIPTOR catalog_entry_Descriptor = {
/* offsets */
offsetof (CATALOG_ENTRY, stack),
offsetof (CATALOG_ENTRY, next),
offsetof (CATALOG_ENTRY, del_id),
offsetof (CATALOG_ENTRY, key),
0,
/* using mutex? */
LF_EM_NOT_USING_MUTEX,
LF_ENTRY_DESCRIPTOR_MAX_ALLOC,
catalog_entry_alloc,
catalog_entry_free,
catalog_entry_init,
catalog_entry_uninit,
catalog_key_copy,
catalog_key_compare,
catalog_key_hash,
NULL /* no inserts */
};
typedef struct catalog_class_id_list CATALOG_CLASS_ID_LIST;
struct catalog_class_id_list
{
OID class_id;
CATALOG_CLASS_ID_LIST *next;
};
typedef struct catalog_record CATALOG_RECORD;
struct catalog_record
{
VPID vpid; /* (volume id, page id) of the slotted page record */
PGSLOTID slotid; /* slot id of the slotted page record record */
PAGE_PTR page_p; /* pointer to the page fetched */
RECDES recdes; /* record descriptor to be fetched and copied */
int offset; /* offset in the record data */
};
typedef struct catalog_page_header CATALOG_PAGE_HEADER;
struct catalog_page_header
{
VPID overflow_page_id; /* overflow page identifier */
int dir_count; /* number of directories in page */
bool is_overflow_page; /* true if page is overflow page, false or else */
};
typedef struct catalog_repr_item CATALOG_REPR_ITEM;
struct catalog_repr_item
{
VPID page_id; /* page identifier of the representation */
INT16 repr_id; /* representation identifier */
PGSLOTID slot_id; /* page slot identifier of representation */
};
#define CATALOG_REPR_ITEM_INITIALIZER \
{ { NULL_PAGEID, NULL_VOLID }, NULL_REPRID, NULL_SLOTID }
CTID catalog_Id; /* global catalog identifier */
static PGLENGTH catalog_Max_record_size; /* Maximum Record Size */
/*
* Note: Catalog memory hash table operations are NOT done in CRITICAL
* SECTIONS, because there can not be simultaneous updaters and readers
* for the same class representation information.
*/
// *INDENT-OFF*
using catalog_hashmap_type = cubthread::lockfree_hashmap<catalog_key, catalog_entry>;
// *INDENT-ON*
static catalog_hashmap_type catalog_Hashmap;
static CATALOG_MAX_SPACE catalog_Max_space; /* Global space information */
static pthread_mutex_t catalog_Max_space_lock = PTHREAD_MUTEX_INITIALIZER;
static bool catalog_is_header_initialized = false;
typedef struct catalog_find_optimal_page_context CATALOG_FIND_OPTIMAL_PAGE_CONTEXT;
struct catalog_find_optimal_page_context
{
int size;
int size_optimal_free;
VPID vpid_optimal;
PAGE_PTR page_optimal;
};
#if defined (SA_MODE)
typedef struct catalog_page_collector CATALOG_PAGE_COLLECTOR;
struct catalog_page_collector
{
VPID *vpids;
int n_vpids;
};
#endif /* SA_MODE */
typedef struct catalog_page_dump_context CATALOG_PAGE_DUMP_CONTEXT;
struct catalog_page_dump_context
{
FILE *fp;
int page_index;
};
static void catalog_initialize_max_space (CATALOG_MAX_SPACE * header_p);
static void catalog_update_max_space (VPID * page_id, PGLENGTH space);
static int catalog_initialize_new_page (THREAD_ENTRY * thread_p, PAGE_PTR page, void *args);
static PAGE_PTR catalog_get_new_page (THREAD_ENTRY * thread_p, VPID * page_id, bool is_overflow_page);
static PAGE_PTR catalog_find_optimal_page (THREAD_ENTRY * thread_p, int size, VPID * page_id);
static int catalog_get_key_list (THREAD_ENTRY * thread_p, void *key, void *val, void *args);
static void catalog_free_key_list (CATALOG_CLASS_ID_LIST * clsid_list);
static int catalog_put_record_into_page (THREAD_ENTRY * thread_p, CATALOG_RECORD * ct_recordp, int next,
PGSLOTID * remembered_slotid);
static int catalog_store_disk_representation (THREAD_ENTRY * thread_p, DISK_REPR * disk_reprp,
CATALOG_RECORD * ct_recordp, PGSLOTID * remembered_slotid);
static int catalog_store_disk_attribute (THREAD_ENTRY * thread_p, DISK_ATTR * disk_attrp, CATALOG_RECORD * ct_recordp,
PGSLOTID * remembered_slotid);
static int catalog_store_attribute_value (THREAD_ENTRY * thread_p, void *value, int length, CATALOG_RECORD * ct_recordp,
PGSLOTID * remembered_slotid);
static int catalog_store_btree_statistics (THREAD_ENTRY * thread_p, BTREE_STATS * bt_statsp,
CATALOG_RECORD * ct_recordp, PGSLOTID * remembered_slotid);
static int catalog_get_record_from_page (THREAD_ENTRY * thread_p, CATALOG_RECORD * ct_recordp);
static int catalog_fetch_disk_representation (THREAD_ENTRY * thread_p, DISK_REPR * disk_reprp,
CATALOG_RECORD * ct_recordp);
static int catalog_fetch_disk_attribute (THREAD_ENTRY * thread_p, DISK_ATTR * disk_attrp, CATALOG_RECORD * ct_recordp);
static int catalog_fetch_attribute_value (THREAD_ENTRY * thread_p, void *value, int length,
CATALOG_RECORD * ct_recordp);
static int catalog_fetch_btree_statistics (THREAD_ENTRY * thread_p, BTREE_STATS * bt_statsp,
CATALOG_RECORD * ct_recordp);
static int catalog_drop_disk_representation_from_page (THREAD_ENTRY * thread_p, VPID * page_id, PGSLOTID slot_id);
static int catalog_drop_representation_class_from_page (THREAD_ENTRY * thread_p, VPID * dir_pgid, PAGE_PTR * dir_pgptr,
VPID * page_id, PGSLOTID slot_id);
static int catalog_get_rep_dir (THREAD_ENTRY * thread_p, OID * class_oid_p, OID * rep_dir_p, bool lookup_hash);
static PAGE_PTR catalog_get_representation_record (THREAD_ENTRY * thread_p, OID * rep_dir_p, RECDES * record_p,
PGBUF_LATCH_MODE latch, int is_peek, int *out_repr_count_p);
static PAGE_PTR catalog_get_representation_record_after_search (THREAD_ENTRY * thread_p, OID * class_id_p,
RECDES * record_p, PGBUF_LATCH_MODE latch, int is_peek,
OID * rep_dir_p, int *out_repr_count_p,
bool lookup_hash);
static int catalog_adjust_directory_count (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_p, int delta);
static void catalog_delete_key (THREAD_ENTRY * thread_p, OID * class_id_p, REPR_ID repr_id);
static char *catalog_find_representation_item_position (INT16 repr_id, int repr_cnt, char *repr_p, int *out_position_p);
static int catalog_insert_representation_item (THREAD_ENTRY * thread_p, RECDES * record_p, OID * rep_dir_p);
static int catalog_drop_directory (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_p, OID * oid_p,
OID * class_id_p);
static void catalog_copy_btree_statistic (BTREE_STATS * new_btree_stats_p, int new_btree_stats_count,
BTREE_STATS * pre_btree_stats_p, int pre_btree_stats_count);
static void catalog_copy_disk_attributes (DISK_ATTR * new_attrs_p, int new_attr_count, DISK_ATTR * pre_attrs_p,
int pre_attr_count);
static int catalog_sum_disk_attribute_size (DISK_ATTR * attrs_p, int count);
static int catalog_put_representation_item (THREAD_ENTRY * thread_p, OID * class_id, CATALOG_REPR_ITEM * repr_item,
OID * rep_dir_p);
static int catalog_get_representation_item (THREAD_ENTRY * thread_p, OID * class_id, CATALOG_REPR_ITEM * repr_item);
static int catalog_drop_representation_item (THREAD_ENTRY * thread_p, OID * class_id, CATALOG_REPR_ITEM * repr_item);
static int catalog_drop (THREAD_ENTRY * thread_p, OID * class_id, REPR_ID repr_id);
static int catalog_drop_all (THREAD_ENTRY * thread_p, OID * class_id);
static int catalog_drop_all_representation_and_class (THREAD_ENTRY * thread_p, OID * class_id);
#if defined (ENABLE_UNUSED_FUNCTION)
static int catalog_fixup_missing_disk_representation (THREAD_ENTRY * thread_p, OID * class_oid, REPR_ID reprid);
static int catalog_fixup_missing_class_info (THREAD_ENTRY * thread_p, OID * class_oid);
#endif
static DISK_ISVALID catalog_check_class_consistency (THREAD_ENTRY * thread_p, OID * class_oid);
static void catalog_dump_disk_attribute (DISK_ATTR * atr);
static void catalog_dump_representation (DISK_REPR * dr);
static void catalog_clear_hash_table (THREAD_ENTRY * thread_p);
static void catalog_put_page_header (char *rec_p, CATALOG_PAGE_HEADER * header_p);
static void catalog_get_disk_representation (DISK_REPR * disk_repr_p, char *rec_p);
static void catalog_put_disk_representation (char *rec_p, DISK_REPR * disk_repr_p);
static void catalog_get_disk_attribute (DISK_ATTR * attr_p, char *rec_p);
static void catalog_put_disk_attribute (char *rec_p, DISK_ATTR * attr_p);
static void catalog_put_btree_statistics (char *rec_p, BTREE_STATS * stat_p);
static void catalog_get_class_info_from_record (CLS_INFO * class_info_p, char *rec_p);
static void catalog_put_class_info_to_record (char *rec_p, CLS_INFO * class_info_p);
static void catalog_get_repr_item_from_record (CATALOG_REPR_ITEM * item_p, char *rec_p);
static void catalog_put_repr_item_to_record (char *rec_p, CATALOG_REPR_ITEM * item_p);
static int catalog_assign_attribute (THREAD_ENTRY * thread_p, DISK_ATTR * disk_attr_p,
CATALOG_RECORD * catalog_record_p);
#if defined (SA_MODE)
static int catalog_file_map_is_empty (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args);
#endif /* SA_MODE */
static int catalog_file_map_page_dump (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args);
static int catalog_file_map_overflow_count (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args);
static void
catalog_put_page_header (char *rec_p, CATALOG_PAGE_HEADER * header_p)
{
CATALOG_PUT_PGHEADER_OVFL_PGID_PAGEID (rec_p, header_p->overflow_page_id.pageid);
CATALOG_PUT_PGHEADER_OVFL_PGID_VOLID (rec_p, header_p->overflow_page_id.volid);
CATALOG_PUT_PGHEADER_DIR_COUNT (rec_p, header_p->dir_count);
CATALOG_PUT_PGHEADER_PG_OVFL (rec_p, header_p->is_overflow_page);
}
static void
catalog_get_disk_representation (DISK_REPR * disk_repr_p, char *rec_p)
{
disk_repr_p->id = (REPR_ID) OR_GET_INT (rec_p + CATALOG_DISK_REPR_ID_OFF);
disk_repr_p->n_fixed = OR_GET_INT (rec_p + CATALOG_DISK_REPR_N_FIXED_OFF);
disk_repr_p->fixed = NULL;
disk_repr_p->fixed_length = OR_GET_INT (rec_p + CATALOG_DISK_REPR_FIXED_LENGTH_OFF);
disk_repr_p->n_variable = OR_GET_INT (rec_p + CATALOG_DISK_REPR_N_VARIABLE_OFF);
disk_repr_p->variable = NULL;
#if 0 /* reserved for future use */
disk_repr_p->repr_reserved_1 = OR_GET_INT (rec_p + CATALOG_DISK_REPR_RESERVED_1_OFF);
#endif
}
static void
catalog_put_disk_representation (char *rec_p, DISK_REPR * disk_repr_p)
{
OR_PUT_INT (rec_p + CATALOG_DISK_REPR_ID_OFF, disk_repr_p->id);
OR_PUT_INT (rec_p + CATALOG_DISK_REPR_N_FIXED_OFF, disk_repr_p->n_fixed);
OR_PUT_INT (rec_p + CATALOG_DISK_REPR_FIXED_LENGTH_OFF, disk_repr_p->fixed_length);
OR_PUT_INT (rec_p + CATALOG_DISK_REPR_N_VARIABLE_OFF, disk_repr_p->n_variable);
/* reserved for future use */
OR_PUT_INT (rec_p + CATALOG_DISK_REPR_RESERVED_1_OFF, 0);
}
static void
catalog_get_disk_attribute (DISK_ATTR * attr_p, char *rec_p)
{
attr_p->id = OR_GET_INT (rec_p + CATALOG_DISK_ATTR_ID_OFF);
attr_p->location = OR_GET_INT (rec_p + CATALOG_DISK_ATTR_LOCATION_OFF);
attr_p->type = (DB_TYPE) OR_GET_INT (rec_p + CATALOG_DISK_ATTR_TYPE_OFF);
attr_p->value = NULL;
attr_p->val_length = OR_GET_INT (rec_p + CATALOG_DISK_ATTR_VAL_LENGTH_OFF);
attr_p->position = OR_GET_INT (rec_p + CATALOG_DISK_ATTR_POSITION_OFF);
OR_GET_OID (rec_p + CATALOG_DISK_ATTR_CLASSOID_OFF, &attr_p->classoid);
attr_p->n_btstats = OR_GET_INT (rec_p + CATALOG_DISK_ATTR_N_BTSTATS_OFF);
OR_GET_INT64 (rec_p + CATALOG_DISK_ATTR_NDV_OFF, &attr_p->ndv);
attr_p->bt_stats = NULL;
}
static void
catalog_get_btree_statistics (BTREE_STATS * stat_p, char *rec_p)
{
int i;
stat_p->leafs = OR_GET_INT (rec_p + CATALOG_BT_STATS_LEAFS_OFF);
stat_p->pages = OR_GET_INT (rec_p + CATALOG_BT_STATS_PAGES_OFF);
stat_p->height = OR_GET_INT (rec_p + CATALOG_BT_STATS_HEIGHT_OFF);
stat_p->keys = OR_GET_INT (rec_p + CATALOG_BT_STATS_KEYS_OFF);
stat_p->has_function = OR_GET_INT (rec_p + CATALOG_BT_STATS_FUNC_INDEX_OFF);
assert (stat_p->pkeys_size <= BTREE_STATS_PKEYS_NUM);
for (i = 0; i < stat_p->pkeys_size; i++)
{
stat_p->pkeys[i] = OR_GET_INT (rec_p + CATALOG_BT_STATS_PKEYS_OFF + (OR_INT_SIZE * i));
}
#if 0 /* reserved for future use */
for (i = 0; i < BTREE_STATS_RESERVED_NUM; i++)
{
stat_p->reserved[i] = OR_GET_INT (rec_p + CATALOG_BT_STATS_RESERVED_OFF + (OR_INT_SIZE * i));
}
#endif
}
static void
catalog_put_disk_attribute (char *rec_p, DISK_ATTR * attr_p)
{
OR_PUT_INT (rec_p + CATALOG_DISK_ATTR_ID_OFF, attr_p->id);
OR_PUT_INT (rec_p + CATALOG_DISK_ATTR_LOCATION_OFF, attr_p->location);
OR_PUT_INT (rec_p + CATALOG_DISK_ATTR_TYPE_OFF, attr_p->type);
OR_PUT_INT (rec_p + CATALOG_DISK_ATTR_VAL_LENGTH_OFF, attr_p->val_length);
OR_PUT_INT (rec_p + CATALOG_DISK_ATTR_POSITION_OFF, attr_p->position);
OR_PUT_OID (rec_p + CATALOG_DISK_ATTR_CLASSOID_OFF, &attr_p->classoid);
OR_PUT_INT (rec_p + CATALOG_DISK_ATTR_N_BTSTATS_OFF, attr_p->n_btstats);
OR_PUT_INT64 (rec_p + CATALOG_DISK_ATTR_NDV_OFF, &attr_p->ndv);
}
static void
catalog_put_btree_statistics (char *rec_p, BTREE_STATS * stat_p)
{
int i;
OR_PUT_BTID (rec_p + CATALOG_BT_STATS_BTID_OFF, &stat_p->btid);
OR_PUT_INT (rec_p + CATALOG_BT_STATS_LEAFS_OFF, stat_p->leafs);
OR_PUT_INT (rec_p + CATALOG_BT_STATS_PAGES_OFF, stat_p->pages);
OR_PUT_INT (rec_p + CATALOG_BT_STATS_HEIGHT_OFF, stat_p->height);
OR_PUT_INT (rec_p + CATALOG_BT_STATS_KEYS_OFF, stat_p->keys);
OR_PUT_INT (rec_p + CATALOG_BT_STATS_FUNC_INDEX_OFF, stat_p->has_function);
assert (stat_p->pkeys_size <= BTREE_STATS_PKEYS_NUM);
for (i = 0; i < stat_p->pkeys_size; i++)
{
OR_PUT_INT (rec_p + CATALOG_BT_STATS_PKEYS_OFF + (OR_INT_SIZE * i), stat_p->pkeys[i]);
}
/* reserved for future use */
for (i = 0; i < BTREE_STATS_RESERVED_NUM; i++)
{
OR_PUT_INT (rec_p + CATALOG_BT_STATS_RESERVED_OFF + (OR_INT_SIZE * i), 0);
}
}
static void
catalog_get_class_info_from_record (CLS_INFO * class_info_p, char *rec_p)
{
OR_GET_HFID (rec_p + CATALOG_CLS_INFO_HFID_OFF, &class_info_p->ci_hfid);
class_info_p->ci_tot_pages = OR_GET_INT (rec_p + CATALOG_CLS_INFO_TOT_PAGES_OFF);
class_info_p->ci_tot_objects = OR_GET_INT (rec_p + CATALOG_CLS_INFO_TOT_OBJS_OFF);
class_info_p->ci_time_stamp = OR_GET_INT (rec_p + CATALOG_CLS_INFO_TIME_STAMP_OFF);
OR_GET_OID (rec_p + CATALOG_CLS_INFO_REP_DIR_OFF, &(class_info_p->ci_rep_dir));
assert (!OID_ISNULL (&(class_info_p->ci_rep_dir)));
}
static void
catalog_put_class_info_to_record (char *rec_p, CLS_INFO * class_info_p)
{
assert (!OID_ISNULL (&(class_info_p->ci_rep_dir)));
OR_PUT_HFID (rec_p + CATALOG_CLS_INFO_HFID_OFF, &class_info_p->ci_hfid);
OR_PUT_INT (rec_p + CATALOG_CLS_INFO_TOT_PAGES_OFF, class_info_p->ci_tot_pages);
OR_PUT_INT (rec_p + CATALOG_CLS_INFO_TOT_OBJS_OFF, class_info_p->ci_tot_objects);
OR_PUT_INT (rec_p + CATALOG_CLS_INFO_TIME_STAMP_OFF, class_info_p->ci_time_stamp);
OR_PUT_OID (rec_p + CATALOG_CLS_INFO_REP_DIR_OFF, &(class_info_p->ci_rep_dir));
}
static void
catalog_get_repr_item_from_record (CATALOG_REPR_ITEM * item_p, char *rec_p)
{
item_p->page_id.pageid = OR_GET_INT (rec_p + CATALOG_REPR_ITEM_PAGEID_PAGEID_OFF);
item_p->page_id.volid = OR_GET_SHORT (rec_p + CATALOG_REPR_ITEM_PAGEID_VOLID_OFF);
item_p->repr_id = OR_GET_SHORT (rec_p + CATALOG_REPR_ITEM_REPRID_OFF);
item_p->slot_id = OR_GET_SHORT (rec_p + CATALOG_REPR_ITEM_SLOTID_OFF);
}
static void
catalog_put_repr_item_to_record (char *rec_p, CATALOG_REPR_ITEM * item_p)
{
OR_PUT_INT (rec_p + CATALOG_REPR_ITEM_PAGEID_PAGEID_OFF, item_p->page_id.pageid);
OR_PUT_SHORT (rec_p + CATALOG_REPR_ITEM_PAGEID_VOLID_OFF, item_p->page_id.volid);
OR_PUT_SHORT (rec_p + CATALOG_REPR_ITEM_REPRID_OFF, item_p->repr_id);
OR_PUT_SHORT (rec_p + CATALOG_REPR_ITEM_SLOTID_OFF, item_p->slot_id);
}
static void
catalog_initialize_max_space (CATALOG_MAX_SPACE * max_space_p)
{
int rv;
rv = pthread_mutex_lock (&catalog_Max_space_lock);
max_space_p->max_page_id.pageid = NULL_PAGEID;
max_space_p->max_page_id.volid = NULL_VOLID;
max_space_p->max_space = -1;
pthread_mutex_unlock (&catalog_Max_space_lock);
}
static void
catalog_update_max_space (VPID * page_id_p, PGLENGTH space)
{
int rv;
rv = pthread_mutex_lock (&catalog_Max_space_lock);
if (VPID_EQ (page_id_p, &catalog_Max_space.max_page_id))
{
catalog_Max_space.max_space = space;
}
else if (space > catalog_Max_space.max_space)
{
catalog_Max_space.max_page_id = *(page_id_p);
catalog_Max_space.max_space = space;
}
pthread_mutex_unlock (&catalog_Max_space_lock);
}
/*
* catalog_initialize_new_page () -
* return:
* vfid(in):
* file_type(in):
* vpid(in):
* ignore_npages(in):
* pg_ovfl(in):
*/
/*
* catalog_initialize_new_page () - document me!
*
* return : Error code
* thread_p (in) : Thread entry
* page (in) : New catalog page
* args (in) : bool * (is_overflow_page)
*/
static int
catalog_initialize_new_page (THREAD_ENTRY * thread_p, PAGE_PTR page, void *args)
{
CATALOG_PAGE_HEADER page_header;
PGSLOTID slot_id;
int success;
RECDES record = {
CATALOG_PAGE_HEADER_SIZE, CATALOG_PAGE_HEADER_SIZE, REC_HOME, NULL
};
char data[CATALOG_PAGE_HEADER_SIZE + MAX_ALIGNMENT], *aligned_data;
bool is_overflow_page = *(bool *) args;
aligned_data = PTR_ALIGN (data, MAX_ALIGNMENT);
pgbuf_set_page_ptype (thread_p, page, PAGE_CATALOG);
spage_initialize (thread_p, page, ANCHORED_DONT_REUSE_SLOTS, MAX_ALIGNMENT, SAFEGUARD_RVSPACE);
VPID_SET_NULL (&page_header.overflow_page_id);
page_header.dir_count = 0;
page_header.is_overflow_page = is_overflow_page;
recdes_set_data_area (&record, aligned_data, CATALOG_PAGE_HEADER_SIZE);
catalog_put_page_header (record.data, &page_header);
success = spage_insert (thread_p, page, &record, &slot_id);
if (success != SP_SUCCESS || slot_id != CATALOG_HEADER_SLOT)
{
assert (false);
if (success != SP_SUCCESS)
{
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
}
return ER_FAILED;
}
log_append_undoredo_data2 (thread_p, RVCT_NEWPAGE, NULL, page, -1, 0, sizeof (CATALOG_PAGE_HEADER), NULL,
&page_header);
pgbuf_set_dirty (thread_p, page, DONT_FREE);
return NO_ERROR;
}
/*
* catalog_get_new_page () - Get a new page for the catalog
* return: The pointer to a newly allocated page, or NULL
* The parameter page_id is set to the page identifier.
* page_id(out): Set to the page identifier for the newly allocated page
* nearpg(in): A page identifier that may be used in a nearby page allocation.
* (It may be ignored.)
* pg_ovfl(in): Page is an overflow page (1) or not (0)
*
* Note: Allocates a new page for the catalog and inserts the header
* record for the page.
*/
static PAGE_PTR
catalog_get_new_page (THREAD_ENTRY * thread_p, VPID * page_id_p, bool is_overflow_page)
{
PAGE_PTR page_p;
log_sysop_start (thread_p);
if (file_alloc (thread_p, &catalog_Id.vfid, catalog_initialize_new_page, &is_overflow_page, page_id_p, &page_p)
!= NO_ERROR)
{
ASSERT_ERROR ();
log_sysop_abort (thread_p);
return NULL;
}
if (page_p == NULL)
{
assert_release (false);
log_sysop_abort (thread_p);
return NULL;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
log_sysop_commit (thread_p);
return page_p;
}
/*
* catalog_file_map_find_optimal_page () - FILE_MAP_PAGE_FUNC function that checks a catalog page has enough space
* for a new record.
*
* return : error code
* thread_p (in) : thread entry
* page (in/out) : page to check. if page has enough space, its value is moved to context and the output of this pointer
* is NULL.
* stop (out) : output true if page has enough space
* args (in/out) : find optimal page context
*/
static int
catalog_file_map_find_optimal_page (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args)
{
CATALOG_FIND_OPTIMAL_PAGE_CONTEXT *context = (CATALOG_FIND_OPTIMAL_PAGE_CONTEXT *) args;
RECDES record;
int dir_count;
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, *page, PAGE_CATALOG);
#endif /* !NDEBUG */
if (spage_get_record (thread_p, *page, CATALOG_HEADER_SLOT, &record, PEEK) != S_SUCCESS)
{
assert_release (false);
return ER_FAILED;
}
if (CATALOG_GET_PGHEADER_PG_OVFL (record.data) || CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID (record.data) != NULL_PAGEID)
{
/* overflow page */
return NO_ERROR;
}
context->size_optimal_free = spage_max_space_for_new_record (thread_p, *page) - CATALOG_MAX_SLOT_ID_SIZE;
dir_count = CATALOG_GET_PGHEADER_DIR_COUNT (record.data);
if (dir_count > 0)
{
context->size_optimal_free -= (int) (DB_PAGESIZE * (0.25f + (dir_count - 1) * 0.05f));
}
if (context->size_optimal_free > context->size)
{
/* found optimal page */
pgbuf_get_vpid (*page, &context->vpid_optimal);
context->page_optimal = *page;
*page = NULL;
*stop = true;
}
return NO_ERROR;
}
/*
* catalog_find_optimal_page () -
* return: PAGE_PTR
* size(in): The size requested in the page
* page_id(out): Set to the page identifier fetched
*/
static PAGE_PTR
catalog_find_optimal_page (THREAD_ENTRY * thread_p, int size, VPID * page_id_p)
{
PAGE_PTR page_p;
CATALOG_FIND_OPTIMAL_PAGE_CONTEXT context;
assert (page_id_p != NULL);
context.size = size;
VPID_SET_NULL (&context.vpid_optimal);
context.page_optimal = NULL;
pthread_mutex_lock (&catalog_Max_space_lock);
if (catalog_Max_space.max_page_id.pageid != NULL_PAGEID && catalog_Max_space.max_space > size)
{
/* try to use page hint */
bool can_use = false;
*page_id_p = catalog_Max_space.max_page_id;
pthread_mutex_unlock (&catalog_Max_space_lock);
page_p = pgbuf_fix (thread_p, page_id_p, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
ASSERT_ERROR ();
return NULL;
}
if (catalog_file_map_find_optimal_page (thread_p, &page_p, &can_use, &context) != NO_ERROR)
{
ASSERT_ERROR ();
return NULL;
}
if (can_use)
{
/* we can use cached page. careful, it was moved to context. */
assert (!VPID_ISNULL (&context.vpid_optimal));
assert (context.page_optimal != NULL);
assert (page_p == NULL);
*page_id_p = context.vpid_optimal;
return context.page_optimal;
}
else
{
pgbuf_unfix_and_init (thread_p, page_p);
}
/* need another page */
pthread_mutex_lock (&catalog_Max_space_lock);
}
/* todo: search the file table for enough empty space. this system is not ideal at all! but we have no other means
* of reusing free space. we'll need a different design for catalog file or a free space map or anything.
* maybe we'll consider catalog when we'll rethink the best space system of heap file.
*/
if (file_map_pages (thread_p, &catalog_Id.vfid, PGBUF_LATCH_WRITE, PGBUF_CONDITIONAL_LATCH,
catalog_file_map_find_optimal_page, &context) != NO_ERROR)
{
ASSERT_ERROR ();
pthread_mutex_unlock (&catalog_Max_space_lock);
return NULL;
}
if (context.page_optimal != NULL)
{
if (catalog_Max_space.max_page_id.pageid == NULL_PAGEID
|| catalog_Max_space.max_space < context.size_optimal_free - size)
{
/* replace entry in max space */
catalog_Max_space.max_page_id = context.vpid_optimal;
catalog_Max_space.max_space = context.size_optimal_free;
}
pthread_mutex_unlock (&catalog_Max_space_lock);
*page_id_p = context.vpid_optimal;
return context.page_optimal;
}
/* no page with enough space was found */
page_p = catalog_get_new_page (thread_p, page_id_p, false);
if (page_p == NULL)
{
ASSERT_ERROR ();
pthread_mutex_unlock (&catalog_Max_space_lock);
return NULL;
}
catalog_Max_space.max_page_id = *page_id_p;
catalog_Max_space.max_space = spage_max_space_for_new_record (thread_p, page_p) - CATALOG_MAX_SLOT_ID_SIZE;
pthread_mutex_unlock (&catalog_Max_space_lock);
return page_p;
}
/*
* catalog_free_representation () - Free disk representation memory area
* return: nothing
* dr(in): Disk representation structure pointer
*/
void
catalog_free_representation (DISK_REPR * repr_p)
{
int attr_cnt, k, j;
DISK_ATTR *attr_p;
BTREE_STATS *stat_p;
if (repr_p != NULL)
{
attr_cnt = repr_p->n_fixed + repr_p->n_variable;
for (k = 0; k < attr_cnt; k++)
{
attr_p = ((k < repr_p->n_fixed)
? (DISK_ATTR *) repr_p->fixed + k : (DISK_ATTR *) repr_p->variable + (k - repr_p->n_fixed));
if (attr_p->value != NULL)
{
db_private_free_and_init (NULL, attr_p->value);
}
if (attr_p->bt_stats != NULL)
{
for (j = 0; j < attr_p->n_btstats; j++)
{
stat_p = &(attr_p->bt_stats[j]);
if (stat_p->pkeys != NULL)
{
db_private_free_and_init (NULL, stat_p->pkeys);
}
}
db_private_free_and_init (NULL, attr_p->bt_stats);
}
}
if (repr_p->fixed != NULL)
{
db_private_free_and_init (NULL, repr_p->fixed);
}
if (repr_p->variable != NULL)
{
db_private_free_and_init (NULL, repr_p->variable);
}
db_private_free_and_init (NULL, repr_p);
}
}
/*
* catalog_free_class_info () - Free class specific information memory area
* return: nothing
* cls_info(in): Pointer to the class information structure
*/
void
catalog_free_class_info (CLS_INFO * class_info_p)
{
if (class_info_p)
{
db_private_free_and_init (NULL, class_info_p);
}
}
/*
* catalog_get_key_list () -
* return: NO_ERROR or error code
* key(in):
* val(in):
* args(in):
*/
static int
catalog_get_key_list (THREAD_ENTRY * thread_p, void *key, void *ignore_value, void *args)
{
CATALOG_CLASS_ID_LIST *class_id_p, **p;
p = (CATALOG_CLASS_ID_LIST **) args;
class_id_p = (CATALOG_CLASS_ID_LIST *) db_private_alloc (thread_p, sizeof (CATALOG_CLASS_ID_LIST));
if (class_id_p == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
class_id_p->class_id.volid = ((OID *) key)->volid;
class_id_p->class_id.pageid = ((OID *) key)->pageid;
class_id_p->class_id.slotid = ((OID *) key)->slotid;
class_id_p->next = *p;
*p = class_id_p;
return NO_ERROR;
}
/*
* catalog_free_key_list () -
* return:
* clsid_list(in):
*/
static void
catalog_free_key_list (CATALOG_CLASS_ID_LIST * class_id_list)
{
CATALOG_CLASS_ID_LIST *p, *next;
if (class_id_list == NULL)
{
return;
}
for (p = class_id_list; p; p = next)
{
next = p->next;
db_private_free_and_init (NULL, p);
}
class_id_list = NULL;
}
/*
* catalog_entry_alloc () - allocate a catalog entry
* returns: new pointer or NULL on error
*/
static void *
catalog_entry_alloc (void)
{
return malloc (sizeof (CATALOG_ENTRY));
}
/*
* catalog_entry_free () - free a catalog entry
* returns: error code or NO_ERROR
* ent(in): entry to free
*/
static int
catalog_entry_free (void *ent)
{
free (ent);
return NO_ERROR;
}
/*
* catalog_entry_init () - initialize a catalog entry
* returns: error code or NO_ERROR
* ent(in): entry to initialize
*/
static int
catalog_entry_init (void *ent)
{
/* TO BE FILLED IN IF NECESSARY */
return NO_ERROR;
}
/*
* catalog_entry_uninit () - uninitialize a catalog entry
* returns: error code or NO_ERROR
* ent(in): entry to uninitialize
*/
static int
catalog_entry_uninit (void *ent)
{
/* TO BE FILLED IN IF NECESSARY */
return NO_ERROR;
}
/*
* catalog_key_copy () - copy a key
* returns: error code or NO_ERROR
* src(in): source key
* dest(in): destination key
*/
static int
catalog_key_copy (void *src, void *dest)
{
CATALOG_KEY *src_k = (CATALOG_KEY *) src;
CATALOG_KEY *dest_k = (CATALOG_KEY *) dest;
if (src_k == NULL || dest_k == NULL)
{
return ER_FAILED;
}
/* copy key members */
dest_k->page_id = src_k->page_id;
dest_k->repr_id = src_k->repr_id;
dest_k->slot_id = src_k->slot_id;
dest_k->volid = src_k->volid;
/* copy data members */
VPID_COPY (&dest_k->r_page_id, &src_k->r_page_id);
dest_k->r_slot_id = src_k->r_slot_id;
return NO_ERROR;
}
/*
* catalog_compare () - Compare two catalog keys
* return: int (true or false)
* key1(in): First catalog key
* key2(in): Second catalog key
*/
static int
catalog_key_compare (void *key1, void *key2)
{
const CATALOG_KEY *k1, *k2;
k1 = (const CATALOG_KEY *) key1;
k2 = (const CATALOG_KEY *) key2;
/* only compare key members */
if (k1->page_id == k2->page_id && k1->slot_id == k2->slot_id && k1->repr_id == k2->repr_id && k1->volid == k2->volid)
{
/* equal */
return 0;
}
else
{
/* not equal */
return 1;
}
}
/*
* catalog_hash () -
* return: int
* key(in): Catalog key
* htsize(in): Memory Hash Table Size
*
* Note: Generate a hash number for the given key for the given hash table size.
*/
static unsigned int
catalog_key_hash (void *key, int hash_table_size)
{
const CATALOG_KEY *k1 = (const CATALOG_KEY *) key;
unsigned int hash_res;
hash_res =
((((k1)->slot_id | ((k1)->page_id << 8)) ^ (((k1)->page_id >> 8) | (((PAGEID) (k1)->volid) << 24))) + k1->repr_id);
return (hash_res % hash_table_size);
}
/*
* catalog_put_record_into_page () -
* return: NO_ERROR or ER_FAILED
* ct_recordp(in): pointer to CATALOG_RECORD structure
* next(in): flag of next page
* remembered_slotid(in):
*
* Note: Put the catalog record into the page and then prepare next page.
*/
static int
catalog_put_record_into_page (THREAD_ENTRY * thread_p, CATALOG_RECORD * catalog_record_p, int next,
PGSLOTID * remembered_slot_id_p)
{
PAGE_PTR new_page_p;
VPID new_vpid;
/* if some space in 'recdes'data' is remained */
if (catalog_record_p->offset < catalog_record_p->recdes.area_size)
{
return NO_ERROR;
}
if (spage_insert (thread_p, catalog_record_p->page_p, &catalog_record_p->recdes, &catalog_record_p->slotid) !=
SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, catalog_record_p->page_p);
return ER_FAILED;
}
if (*remembered_slot_id_p == NULL_SLOTID)
{
*remembered_slot_id_p = catalog_record_p->slotid;
}
log_append_undoredo_recdes2 (thread_p, RVCT_INSERT, &catalog_Id.vfid, catalog_record_p->page_p,
catalog_record_p->slotid, NULL, &catalog_record_p->recdes);
pgbuf_set_dirty (thread_p, catalog_record_p->page_p, DONT_FREE);
/* if there's no need to get next page; when this is the last page */
if (!next)
{
catalog_record_p->slotid = *remembered_slot_id_p;
*remembered_slot_id_p = NULL_SLOTID;
return NO_ERROR;
}
new_page_p = catalog_get_new_page (thread_p, &new_vpid, true);
if (new_page_p == NULL)
{
pgbuf_unfix_and_init (thread_p, catalog_record_p->page_p);
return ER_FAILED;
}
log_append_undo_data2 (thread_p, RVCT_NEW_OVFPAGE_LOGICAL_UNDO, &catalog_Id.vfid, NULL, -1, sizeof (new_vpid),
&new_vpid);
/* make the previous page point to the newly allocated page */
catalog_record_p->recdes.area_size = DB_PAGESIZE;
(void) spage_get_record (thread_p, catalog_record_p->page_p, CATALOG_HEADER_SLOT, &catalog_record_p->recdes, COPY);
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, catalog_record_p->page_p, CATALOG_HEADER_SLOT,
&catalog_record_p->recdes);
CATALOG_PUT_PGHEADER_OVFL_PGID_PAGEID (catalog_record_p->recdes.data, new_vpid.pageid);
CATALOG_PUT_PGHEADER_OVFL_PGID_VOLID (catalog_record_p->recdes.data, new_vpid.volid);
if (spage_update (thread_p, catalog_record_p->page_p, CATALOG_HEADER_SLOT, &catalog_record_p->recdes) != SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, catalog_record_p->page_p);
return ER_FAILED;
}
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, catalog_record_p->page_p, CATALOG_HEADER_SLOT,
&catalog_record_p->recdes);
pgbuf_set_dirty (thread_p, catalog_record_p->page_p, FREE);
catalog_record_p->vpid.pageid = new_vpid.pageid;
catalog_record_p->vpid.volid = new_vpid.volid;
catalog_record_p->page_p = new_page_p;
catalog_record_p->recdes.area_size = spage_max_space_for_new_record (thread_p, new_page_p) - CATALOG_MAX_SLOT_ID_SIZE;
catalog_record_p->recdes.length = 0;
catalog_record_p->offset = 0;
return NO_ERROR;
}
static int
catalog_write_unwritten_portion (THREAD_ENTRY * thread_p, CATALOG_RECORD * catalog_record_p,
PGSLOTID * remembered_slot_id_p, int format_size)
{
/* if the remained, unwritten portion of the record data is smaller than the size of disk format of structure */
if (catalog_record_p->recdes.area_size - catalog_record_p->offset < format_size)
{
/* set the record length as the current offset, the size of written portion of the record data, and set the
* offset to the end of the record data to write the page */
catalog_record_p->recdes.length = catalog_record_p->offset;
catalog_record_p->offset = catalog_record_p->recdes.area_size;
if (catalog_put_record_into_page (thread_p, catalog_record_p, 1, remembered_slot_id_p) != NO_ERROR)
{
return ER_FAILED;
}
}
return NO_ERROR;
}
/*
* catalog_store_disk_representation () -
* return: NO_ERROR or ER_FAILED
* disk_reprp(in): pointer to DISK_REPR structure (disk representation)
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
* remembered_slotid(in):
*
* Note: Transforms disk representation form into catalog disk form.
* Store DISK_REPR structure into catalog record.
*/
static int
catalog_store_disk_representation (THREAD_ENTRY * thread_p, DISK_REPR * disk_repr_p, CATALOG_RECORD * catalog_record_p,
PGSLOTID * remembered_slot_id_p)
{
if (catalog_write_unwritten_portion (thread_p, catalog_record_p, remembered_slot_id_p, CATALOG_DISK_REPR_SIZE) !=
NO_ERROR)
{
return ER_FAILED;
}
catalog_put_disk_representation (catalog_record_p->recdes.data + catalog_record_p->offset, disk_repr_p);
catalog_record_p->offset += CATALOG_DISK_REPR_SIZE;
return NO_ERROR;
}
/*
* catalog_store_disk_attribute () -
* NO_ERROR or ER_FAILED
* disk_attrp(in): pointer to DISK_ATTR structure (disk representation)
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
* remembered_slotid(in):
*
* Note: Transforms disk representation form into catalog disk form.
* Store DISK_ATTR structure into catalog record.
*/
static int
catalog_store_disk_attribute (THREAD_ENTRY * thread_p, DISK_ATTR * disk_attr_p, CATALOG_RECORD * catalog_record_p,
PGSLOTID * remembered_slot_id_p)
{
if (catalog_write_unwritten_portion (thread_p, catalog_record_p, remembered_slot_id_p, CATALOG_DISK_ATTR_SIZE) !=
NO_ERROR)
{
return ER_FAILED;
}
catalog_put_disk_attribute (catalog_record_p->recdes.data + catalog_record_p->offset, disk_attr_p);
catalog_record_p->offset += CATALOG_DISK_ATTR_SIZE;
return NO_ERROR;
}
/*
* catalog_store_attribute_value () -
* return: NO_ERROR or ER_FAILED
* value(in): pointer to the value data (disk representation)
* length(in): length of the value data
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
* remembered_slotid(in):
*
* Note: Transforms disk representation form into catalog disk form.
* Store value data into catalog record.
*/
static int
catalog_store_attribute_value (THREAD_ENTRY * thread_p, void *value, int length, CATALOG_RECORD * catalog_record_p,
PGSLOTID * remembered_slot_id_p)
{
int offset = 0;
int bufsize;
if (catalog_write_unwritten_portion (thread_p, catalog_record_p, remembered_slot_id_p, length) != NO_ERROR)
{
return ER_FAILED;
}
while (offset < length)
{
if (length - offset <= catalog_record_p->recdes.area_size - catalog_record_p->offset)
{
/* if the size of the value is smaller than or equals to the remaining size of the recdes.data, just copy the
* value into the recdes.data buffer and adjust the offset. */
bufsize = length - offset;
(void) memcpy (catalog_record_p->recdes.data + catalog_record_p->offset, (char *) value + offset, bufsize);
catalog_record_p->offset += bufsize;
break;
}
else
{
/* if the size of the value is larger than the whole size of the recdes.data, we need split the value over N
* pages. The first N-1 pages need to be stored into pages, while the last page can be stored in the
* recdes.data buffer as the existing routine. */
assert (catalog_record_p->offset == 0);
bufsize = catalog_record_p->recdes.area_size;
(void) memcpy (catalog_record_p->recdes.data, (char *) value + offset, bufsize);
offset += bufsize;
/* write recdes.data and fill catalog_record_p as new page */
catalog_record_p->offset = catalog_record_p->recdes.area_size;
catalog_record_p->recdes.length = catalog_record_p->offset;
if (catalog_put_record_into_page (thread_p, catalog_record_p, 1, remembered_slot_id_p) != NO_ERROR)
{
return ER_FAILED;
}
}
}
return NO_ERROR;
}
/*
* catalog_store_btree_statistics () -
* return: NO_ERROR or ER_FAILED
* bt_statsp(in): pointer to BTREE_STATS structure (disk representation)
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
* remembered_slotid(in):
*
* Note: Transforms disk representation form into catalog disk form.
* Store BTREE_STATS structure into catalog record.
*/
static int
catalog_store_btree_statistics (THREAD_ENTRY * thread_p, BTREE_STATS * btree_stats_p, CATALOG_RECORD * catalog_record_p,
PGSLOTID * remembered_slot_id_p)
{
if (catalog_write_unwritten_portion (thread_p, catalog_record_p, remembered_slot_id_p, CATALOG_BT_STATS_SIZE) !=
NO_ERROR)
{
return ER_FAILED;
}
catalog_put_btree_statistics (catalog_record_p->recdes.data + catalog_record_p->offset, btree_stats_p);
catalog_record_p->offset += CATALOG_BT_STATS_SIZE;
return NO_ERROR;
}
/*
* catalog_get_record_from_page () - Get the catalog record from the page.
* return: NO_ERROR or ER_FAILED
* ct_recordp(in): pointer to CATALOG_RECORD structure
*/
static int
catalog_get_record_from_page (THREAD_ENTRY * thread_p, CATALOG_RECORD * catalog_record_p)
{
/* if some data in 'recdes.data' is remained */
if (catalog_record_p->offset < catalog_record_p->recdes.length)
{
return NO_ERROR;
}
/* if it is not first time, if there was the page previously read */
if (catalog_record_p->page_p)
{
if (spage_get_record (thread_p, catalog_record_p->page_p, CATALOG_HEADER_SLOT, &catalog_record_p->recdes, PEEK) !=
S_SUCCESS)
{
return ER_FAILED;
}
catalog_record_p->vpid.pageid = CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID (catalog_record_p->recdes.data);
catalog_record_p->vpid.volid = CATALOG_GET_PGHEADER_OVFL_PGID_VOLID (catalog_record_p->recdes.data);
catalog_record_p->slotid = 1;
pgbuf_unfix_and_init (thread_p, catalog_record_p->page_p);
}
if (catalog_record_p->vpid.pageid == NULL_PAGEID || catalog_record_p->vpid.volid == NULL_VOLID)
{
return ER_FAILED;
}
catalog_record_p->page_p =
pgbuf_fix (thread_p, &catalog_record_p->vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (catalog_record_p->page_p == NULL)
{
return ER_FAILED;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, catalog_record_p->page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (spage_get_record (thread_p, catalog_record_p->page_p, catalog_record_p->slotid, &catalog_record_p->recdes, PEEK)
!= S_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, catalog_record_p->page_p);
return ER_FAILED;
}
catalog_record_p->offset = 0;
return NO_ERROR;
}
static int
catalog_read_unread_portion (THREAD_ENTRY * thread_p, CATALOG_RECORD * catalog_record_p, int format_size)
{
if (catalog_record_p->recdes.length - catalog_record_p->offset < format_size)
{
catalog_record_p->offset = catalog_record_p->recdes.length;
if (catalog_get_record_from_page (thread_p, catalog_record_p) != NO_ERROR)
{
return ER_FAILED;
}
}
return NO_ERROR;
}
/*
* catalog_fetch_disk_representation () -
* return: NO_ERROR or ER_FAILED
* disk_reprp(in): pointer to DISK_REPR structure (disk representation)
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
*
* Note: Transforms catalog disk form into disk representation form.
* Fetch DISK_REPR structure from catalog record.
*/
static int
catalog_fetch_disk_representation (THREAD_ENTRY * thread_p, DISK_REPR * disk_repr_p, CATALOG_RECORD * catalog_record_p)
{
if (catalog_read_unread_portion (thread_p, catalog_record_p, CATALOG_DISK_REPR_SIZE) != NO_ERROR)
{
return ER_FAILED;
}
catalog_get_disk_representation (disk_repr_p, catalog_record_p->recdes.data + catalog_record_p->offset);
catalog_record_p->offset += CATALOG_DISK_REPR_SIZE;
return NO_ERROR;
}
/*
* catalog_fetch_disk_attribute () -
* return: NO_ERROR or ER_FAILED
* disk_attrp(in): pointer to DISK_ATTR structure (disk representation)
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
*
* Note: Transforms catalog disk form into disk representation form.
* Fetch DISK_ATTR structure from catalog record.
*/
static int
catalog_fetch_disk_attribute (THREAD_ENTRY * thread_p, DISK_ATTR * disk_attr_p, CATALOG_RECORD * catalog_record_p)
{
if (catalog_read_unread_portion (thread_p, catalog_record_p, CATALOG_DISK_ATTR_SIZE) != NO_ERROR)
{
return ER_FAILED;
}
catalog_get_disk_attribute (disk_attr_p, catalog_record_p->recdes.data + catalog_record_p->offset);
catalog_record_p->offset += CATALOG_DISK_ATTR_SIZE;
return NO_ERROR;
}
/*
* catalog_fetch_attribute_value () -
* return: NO_ERROR or ER_FAILED
* value(in): pointer to the value data (disk representation)
* length(in): length of the value data
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
*
* Note: Transforms catalog disk form into disk representation form.
* Fetch value data from catalog record.
*/
static int
catalog_fetch_attribute_value (THREAD_ENTRY * thread_p, void *value, int length, CATALOG_RECORD * catalog_record_p)
{
int offset = 0;
int bufsize;
if (catalog_read_unread_portion (thread_p, catalog_record_p, length) != NO_ERROR)
{
return ER_FAILED;
}
while (offset < length)
{
if (length - offset <= catalog_record_p->recdes.length - catalog_record_p->offset)
{
/* if the size of the value is smaller than or equals to the remaining length of the recdes.data, just read
* the value from the recdes.data buffer and adjust the offset. */
bufsize = length - offset;
(void) memcpy ((char *) value + offset, catalog_record_p->recdes.data + catalog_record_p->offset, bufsize);
catalog_record_p->offset += bufsize;
break;
}
else
{
/* if the size of the value is larger than the whole length of the recdes.data, that means the value has been
* stored in N pages, we need to fetch these N pages and read value from them. in first N-1 page, the whole
* page will be read into the value buffer, while in last page, the remaining value will be read into value
* buffer as the existing routine. */
assert (catalog_record_p->offset == 0);
bufsize = catalog_record_p->recdes.length;
(void) memcpy ((char *) value + offset, catalog_record_p->recdes.data, bufsize);
offset += bufsize;
/* read next page and fill the catalog_record_p */
catalog_record_p->offset = catalog_record_p->recdes.length;
if (catalog_get_record_from_page (thread_p, catalog_record_p) != NO_ERROR)
{
return ER_FAILED;
}
}
}
return NO_ERROR;
}
/*
* catalog_fetch_btree_statistics () -
* return: NO_ERROR or ER_FAILED
* bt_statsp(in): pointer to BTREE_STATS structure (disk representation)
* ct_recordp(in): pointer to CATALOG_RECORD structure (catalog record)
*
* Note: Transforms catalog disk form into disk representation form.
* Fetch BTREE_STATS structure from catalog record.
*/
static int
catalog_fetch_btree_statistics (THREAD_ENTRY * thread_p, BTREE_STATS * btree_stats_p, CATALOG_RECORD * catalog_record_p)
{
VPID root_vpid;
PAGE_PTR root_page_p;
BTREE_ROOT_HEADER *root_header = NULL;
int i;
OR_BUF buf;
if (catalog_read_unread_portion (thread_p, catalog_record_p, CATALOG_BT_STATS_SIZE) != NO_ERROR)
{
return ER_FAILED;
}
btree_stats_p->leafs = 0;
btree_stats_p->pages = 0;
btree_stats_p->height = 0;
btree_stats_p->keys = 0;
btree_stats_p->pkeys_size = 0;
btree_stats_p->pkeys = NULL;
CATALOG_GET_BT_STATS_BTID (&btree_stats_p->btid, catalog_record_p->recdes.data + catalog_record_p->offset);
root_vpid.pageid = btree_stats_p->btid.root_pageid;
root_vpid.volid = btree_stats_p->btid.vfid.volid;
if (VPID_ISNULL (&root_vpid))
{
/* after create the catalog record of the class, and before create the catalog record of the constraints for the
* class currently, does not know BTID */
btree_stats_p->key_type = &tp_Null_domain;
goto exit_on_end;
}
root_page_p = pgbuf_fix (thread_p, &root_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (root_page_p == NULL)
{
return ER_FAILED;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, root_page_p, PAGE_BTREE);
#endif /* !NDEBUG */
root_header = btree_get_root_header (thread_p, root_page_p);
if (root_header == NULL)
{
pgbuf_unfix_and_init (thread_p, root_page_p);
return ER_FAILED;
}
or_init (&buf, root_header->packed_key_domain, -1);
btree_stats_p->key_type = or_get_domain (&buf, NULL, NULL);
pgbuf_unfix_and_init (thread_p, root_page_p);
if (TP_DOMAIN_TYPE (btree_stats_p->key_type) == DB_TYPE_MIDXKEY)
{
btree_stats_p->pkeys_size = tp_domain_size (btree_stats_p->key_type->setdomain);
btree_stats_p->dedup_idx = GET_DECOMPRESS_IDX_HEADER (root_header);
}
else
{
btree_stats_p->pkeys_size = 1;
btree_stats_p->dedup_idx = -1;
}
/* cut-off to stats */
if (btree_stats_p->pkeys_size > BTREE_STATS_PKEYS_NUM)
{
btree_stats_p->pkeys_size = BTREE_STATS_PKEYS_NUM;
}
btree_stats_p->pkeys = (int *) db_private_alloc (thread_p, btree_stats_p->pkeys_size * sizeof (int));
if (btree_stats_p->pkeys == NULL)
{
return ER_FAILED;
}
assert (btree_stats_p->pkeys_size <= BTREE_STATS_PKEYS_NUM);
memset (btree_stats_p->pkeys, 0x00, btree_stats_p->pkeys_size * sizeof (int));
exit_on_end:
catalog_get_btree_statistics (btree_stats_p, catalog_record_p->recdes.data + catalog_record_p->offset);
catalog_record_p->offset += CATALOG_BT_STATS_SIZE;
return NO_ERROR;
}
static int
catalog_drop_representation_helper (THREAD_ENTRY * thread_p, PAGE_PTR page_p, VPID * page_id_p, PGSLOTID slot_id)
{
PAGE_PTR overflow_page_p;
VPID overflow_vpid, new_overflow_vpid;
PGLENGTH new_space;
RECDES record = { 0, -1, REC_HOME, NULL };
int error_code;
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
return ER_FAILED;
}
if (spage_get_record (thread_p, page_p, slot_id, &record, COPY) != S_SUCCESS)
{
recdes_free_data_area (&record);
if (er_errid () == ER_SP_UNKNOWN_SLOTID)
{
return NO_ERROR;
}
return ER_FAILED;
}
log_append_undoredo_recdes2 (thread_p, RVCT_DELETE, &catalog_Id.vfid, page_p, slot_id, &record, NULL);
if (spage_delete (thread_p, page_p, slot_id) != slot_id)
{
recdes_free_data_area (&record);
return ER_FAILED;
}
new_space = spage_max_space_for_new_record (thread_p, page_p);
catalog_update_max_space (page_id_p, new_space);
spage_get_record (thread_p, page_p, CATALOG_HEADER_SLOT, &record, COPY);
overflow_vpid.pageid = CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID (record.data);
overflow_vpid.volid = CATALOG_GET_PGHEADER_OVFL_PGID_VOLID (record.data);
if (overflow_vpid.pageid != NULL_PAGEID)
{
/* For undo purpose, PEEK a record before update.
* Instead of allocating new space (heap or stack) and copying for undo records,
* PEEKing a record is simpler and cheaper */
RECDES undo_record = RECDES_INITIALIZER;
spage_get_record (thread_p, page_p, CATALOG_HEADER_SLOT, &undo_record, PEEK);
CATALOG_PUT_PGHEADER_OVFL_PGID_PAGEID (record.data, NULL_PAGEID);
CATALOG_PUT_PGHEADER_OVFL_PGID_VOLID (record.data, NULL_VOLID);
log_append_undoredo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, CATALOG_HEADER_SLOT, &undo_record,
&record);
if (spage_update (thread_p, page_p, CATALOG_HEADER_SLOT, &record) != SP_SUCCESS)
{
recdes_free_data_area (&record);
return ER_FAILED;
}
// free data because it is used only for peeking below
recdes_free_data_area (&record);
do
{
/* delete the records in the overflow pages, if any */
overflow_page_p =
pgbuf_fix (thread_p, &overflow_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if (overflow_page_p == NULL)
{
return ER_FAILED;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, overflow_page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
spage_get_record (thread_p, overflow_page_p, CATALOG_HEADER_SLOT, &record, PEEK);
new_overflow_vpid.pageid = CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID (record.data);
new_overflow_vpid.volid = CATALOG_GET_PGHEADER_OVFL_PGID_VOLID (record.data);
pgbuf_unfix_and_init (thread_p, overflow_page_p);
error_code = file_dealloc (thread_p, &catalog_Id.vfid, &overflow_vpid, FILE_CATALOG);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
overflow_vpid = new_overflow_vpid;
}
while (overflow_vpid.pageid != NULL_PAGEID);
}
else
{
recdes_free_data_area (&record);
}
return NO_ERROR;
}
/*
* catalog_drop_disk_representation_from_page () -
* return: NO_ERROR or ER_FAILED
* page_id(in): Page identifier for the catalog unit
* slot_id(in): Slot identifier for the catalog unit
*
* Note: The catalog storage unit whose location is identified with
* the given page and slot identifier is deleted from the
* catalog page. If there overflow pages pointed by the given
* catalog unit, the overflow pages are deallocated.
*/
static int
catalog_drop_disk_representation_from_page (THREAD_ENTRY * thread_p, VPID * page_id_p, PGSLOTID slot_id)
{
PAGE_PTR page_p;
page_p = pgbuf_fix (thread_p, page_id_p, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
return ER_FAILED;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (catalog_drop_representation_helper (thread_p, page_p, page_id_p, slot_id) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, page_p);
return ER_FAILED;
}
pgbuf_set_dirty (thread_p, page_p, FREE);
return NO_ERROR;
}
/*
* catalog_drop_representation_class_from_page () -
* return: NO_ERROR or ER_FAILED
* dir_pgid(in): Directory page identifier
* dir_pgptr(in/out): Directory page pointer
* page_id(in): Catalog unit page identifier
* slot_id(in): Catalog unit slot identifier
*
* Note: The catalog storage unit which can be disk representation or
* class information record, whose location is identified by the
* given page and slot identifier is deleted from the catalog page.
*/
static int
catalog_drop_representation_class_from_page (THREAD_ENTRY * thread_p, VPID * dir_page_id_p, PAGE_PTR * dir_page_p,
VPID * page_id_p, PGSLOTID slot_id)
{
PAGE_PTR page_p = NULL;
bool same_page;
assert (dir_page_p != NULL && (*dir_page_p) != NULL);
/* directory and repr. to be deleted are on the same page? */
same_page = VPID_EQ (page_id_p, dir_page_id_p) ? true : false;
if (same_page)
{
page_p = *dir_page_p;
}
else
{
int again_count = 0;
int again_max = 20;
try_again:
/* avoid page deadlock */
page_p = pgbuf_fix (thread_p, page_id_p, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_CONDITIONAL_LATCH);
if (page_p == NULL)
{
/* try reverse order */
pgbuf_unfix_and_init (thread_p, *dir_page_p);
page_p = pgbuf_fix (thread_p, page_id_p, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
return ER_FAILED;
}
*dir_page_p = pgbuf_fix (thread_p, dir_page_id_p, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_CONDITIONAL_LATCH);
if ((*dir_page_p) == NULL)
{
pgbuf_unfix_and_init (thread_p, page_p);
*dir_page_p = pgbuf_fix (thread_p, dir_page_id_p, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if ((*dir_page_p) == NULL)
{
return ER_FAILED;
}
if (again_count++ >= again_max)
{
if (er_errid () == NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PAGE_LATCH_ABORTED, 2, page_id_p->volid,
page_id_p->pageid);
}
return ER_FAILED;
}
else
{
goto try_again;
}
}
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
}
if (catalog_drop_representation_helper (thread_p, page_p, page_id_p, slot_id) != NO_ERROR)
{
if (!same_page)
{
pgbuf_unfix_and_init (thread_p, page_p);
}
return ER_FAILED;
}
if (same_page)
{
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
}
else
{
pgbuf_set_dirty (thread_p, page_p, FREE);
}
return NO_ERROR;
}
static int
catalog_get_rep_dir (THREAD_ENTRY * thread_p, OID * class_oid_p, OID * rep_dir_p, bool lookup_hash)
{
CATALOG_REPR_ITEM repr_item = CATALOG_REPR_ITEM_INITIALIZER;
PAGE_PTR page_p;
CLS_INFO class_info = CLS_INFO_INITIALIZER;
HEAP_SCANCACHE scan_cache;
RECDES record = { -1, -1, REC_HOME, NULL };
int error_code = NO_ERROR;
assert (class_oid_p != NULL);
assert (!OID_ISNULL (class_oid_p));
assert (rep_dir_p != NULL);
assert (OID_ISNULL (rep_dir_p));
/* 1st try: look up class_info record in Catalog hash */
assert (repr_item.repr_id == NULL_REPRID);
repr_item.repr_id = NULL_REPRID;
if (lookup_hash == true && catalog_get_representation_item (thread_p, class_oid_p, &repr_item) == NO_ERROR
&& !VPID_ISNULL (&(repr_item.page_id)))
{
page_p = pgbuf_fix (thread_p, &repr_item.page_id, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
return error_code;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (spage_get_record (thread_p, page_p, repr_item.slot_id, &record, PEEK) != S_SUCCESS)
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
pgbuf_unfix_and_init (thread_p, page_p);
return error_code;
}
catalog_get_class_info_from_record (&class_info, record.data);
pgbuf_unfix_and_init (thread_p, page_p);
assert (!OID_ISNULL (&(class_info.ci_rep_dir)));
COPY_OID (rep_dir_p, &(class_info.ci_rep_dir));
}
if (OID_ISNULL (rep_dir_p))
{
/* 2nd try: look up class record in Rootclass */
heap_scancache_quick_start_root_hfid (thread_p, &scan_cache);
if (heap_get_class_record (thread_p, class_oid_p, &record, &scan_cache, PEEK) == S_SUCCESS)
{
or_class_rep_dir (&record, rep_dir_p);
}
heap_scancache_end (thread_p, &scan_cache);
}
if (OID_ISNULL (rep_dir_p))
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
return error_code;
}
assert (error_code == NO_ERROR);
return error_code;
}
static PAGE_PTR
catalog_get_representation_record (THREAD_ENTRY * thread_p, OID * rep_dir_p, RECDES * record_p, PGBUF_LATCH_MODE latch,
int is_peek, int *out_repr_count_p)
{
PAGE_PTR page_p;
VPID vpid;
assert (rep_dir_p != NULL);
assert (!OID_ISNULL (rep_dir_p));
vpid.volid = rep_dir_p->volid;
vpid.pageid = rep_dir_p->pageid;
page_p = pgbuf_fix (thread_p, &vpid, OLD_PAGE, latch, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
if (er_errid () == ER_PB_BAD_PAGEID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, rep_dir_p->volid, rep_dir_p->pageid,
rep_dir_p->slotid);
}
return NULL;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (spage_get_record (thread_p, page_p, rep_dir_p->slotid, record_p, is_peek) != S_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, page_p);
return NULL;
}
assert (record_p->length == CATALOG_REPR_ITEM_SIZE * 2);
*out_repr_count_p = CATALOG_GET_REPR_ITEM_COUNT (record_p->data);
assert (*out_repr_count_p == 1 || *out_repr_count_p == 2);
return page_p;
}
static PAGE_PTR
catalog_get_representation_record_after_search (THREAD_ENTRY * thread_p, OID * class_id_p, RECDES * record_p,
PGBUF_LATCH_MODE latch, int is_peek, OID * rep_dir_p,
int *out_repr_count_p, bool lookup_hash)
{
assert (class_id_p != NULL);
assert (!OID_ISNULL (class_id_p));
assert (rep_dir_p != NULL);
assert (OID_ISNULL (rep_dir_p));
/* get old directory for the class */
if (catalog_get_rep_dir (thread_p, class_id_p, rep_dir_p, lookup_hash) != NO_ERROR || OID_ISNULL (rep_dir_p))
{
assert (er_errid () != NO_ERROR);
return NULL;
}
return catalog_get_representation_record (thread_p, rep_dir_p, record_p, latch, is_peek, out_repr_count_p);
}
static int
catalog_adjust_directory_count (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_p, int delta)
{
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
spage_get_record (thread_p, page_p, CATALOG_HEADER_SLOT, record_p, COPY);
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, CATALOG_HEADER_SLOT, record_p);
CATALOG_PUT_PGHEADER_DIR_COUNT (record_p->data, CATALOG_GET_PGHEADER_DIR_COUNT (record_p->data) + delta);
if (spage_update (thread_p, page_p, CATALOG_HEADER_SLOT, record_p) != SP_SUCCESS)
{
return ER_FAILED;
}
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, CATALOG_HEADER_SLOT, record_p);
return NO_ERROR;
}
static void
catalog_delete_key (THREAD_ENTRY * thread_p, OID * class_id_p, REPR_ID repr_id)
{
CATALOG_KEY catalog_key;
catalog_key.page_id = class_id_p->pageid;
catalog_key.volid = class_id_p->volid;
catalog_key.slot_id = class_id_p->slotid;
catalog_key.repr_id = repr_id;
(void) catalog_Hashmap.erase (thread_p, catalog_key);
}
static char *
catalog_find_representation_item_position (INT16 repr_id, int repr_cnt, char *repr_p, int *out_position_p)
{
int position = 0;
while (position < repr_cnt && repr_id != CATALOG_GET_REPR_ITEM_REPRID (repr_p))
{
position++;
repr_p += CATALOG_REPR_ITEM_SIZE;
}
*out_position_p = position;
assert (*out_position_p <= 2); /* class info repr, last repr */
return repr_p;
}
static int
catalog_insert_representation_item (THREAD_ENTRY * thread_p, RECDES * record_p, OID * rep_dir_p)
{
PAGE_PTR page_p;
VPID page_id;
PGSLOTID slot_id;
PGLENGTH new_space;
assert (record_p != NULL);
assert (OR_GET_BYTE (record_p->data + CATALOG_REPR_ITEM_COUNT_OFF) == 1);
assert (record_p->length == CATALOG_REPR_ITEM_SIZE * 2);
assert (rep_dir_p != NULL);
assert (OID_ISNULL (rep_dir_p));
page_p = catalog_find_optimal_page (thread_p, record_p->length, &page_id);
if (page_p == NULL)
{
return ER_FAILED;
}
if (spage_insert (thread_p, page_p, record_p, &slot_id) != SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, page_p);
return ER_FAILED;
}
log_append_undoredo_recdes2 (thread_p, RVCT_INSERT, &catalog_Id.vfid, page_p, slot_id, NULL, record_p);
if (catalog_adjust_directory_count (thread_p, page_p, record_p, 1) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, page_p);
return ER_FAILED;
}
new_space = spage_max_space_for_new_record (thread_p, page_p);
pgbuf_set_dirty (thread_p, page_p, FREE);
catalog_update_max_space (&page_id, new_space);
rep_dir_p->volid = page_id.volid;
rep_dir_p->pageid = page_id.pageid;
rep_dir_p->slotid = slot_id;
if (OID_ISNULL (rep_dir_p))
{
assert (false); /* is impossible */
return ER_FAILED;
}
return NO_ERROR;
}
/*
* catalog_put_representation_item () -
* return: NO_ERROR or ER_FAILED
* class_id_p(in): Class object identifier
* repr_item_p(in): Representation Item
* rep_dir_p(in/out): Representation Directory
*
* Note: The given representation item is inserted to the class
* directory, if any. If there is no class directory, one is
* created to contain the given item.
* If there is already a class directory, the given
* item replaces the old representation item with the same
* representation identifier, if any, otherwise it is added to
* the directory.
*/
static int
catalog_put_representation_item (THREAD_ENTRY * thread_p, OID * class_id_p, CATALOG_REPR_ITEM * repr_item_p,
OID * rep_dir_p)
{
PAGE_PTR page_p;
VPID page_id;
PGSLOTID slot_id;
#if 0 /* TODO - dead code; do not delete me for future use */
PGLENGTH new_space;
char page_header_data[CATALOG_PAGE_HEADER_SIZE + MAX_ALIGNMENT];
char *aligned_page_header_data;
#endif
RECDES record = { 0, -1, REC_HOME, NULL };
RECDES tmp_record = { 0, -1, REC_HOME, NULL };
int repr_pos, repr_count;
char *repr_p;
int success;
assert (rep_dir_p != NULL);
#if 0 /* TODO - dead code; do not delete me for future use */
aligned_page_header_data = PTR_ALIGN (page_header_data, MAX_ALIGNMENT);
#endif
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
return ER_FAILED;
}
/* add new directory for the class */
if (OID_ISNULL (rep_dir_p))
{
record.length = CATALOG_REPR_ITEM_SIZE * 2;
catalog_put_repr_item_to_record (record.data, repr_item_p);
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, 1);
assert (record.length == CATALOG_REPR_ITEM_SIZE * 2);
if (catalog_insert_representation_item (thread_p, &record, rep_dir_p) != NO_ERROR || OID_ISNULL (rep_dir_p))
{
recdes_free_data_area (&record);
return ER_FAILED;
}
assert (!OID_ISNULL (rep_dir_p));
}
else
{
/* get old directory for the class */
page_id.volid = rep_dir_p->volid;
page_id.pageid = rep_dir_p->pageid;
page_p = catalog_get_representation_record (thread_p, rep_dir_p, &record, PGBUF_LATCH_WRITE, COPY, &repr_count);
if (page_p == NULL)
{
recdes_free_data_area (&record);
return ER_FAILED;
}
repr_p = catalog_find_representation_item_position (repr_item_p->repr_id, repr_count, record.data, &repr_pos);
if (repr_pos < repr_count)
{
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir_p->slotid, &record);
page_id.pageid = CATALOG_GET_REPR_ITEM_PAGEID_PAGEID (repr_p);
page_id.volid = CATALOG_GET_REPR_ITEM_PAGEID_VOLID (repr_p);
slot_id = CATALOG_GET_REPR_ITEM_SLOTID (repr_p);
catalog_delete_key (thread_p, class_id_p, repr_item_p->repr_id);
catalog_put_repr_item_to_record (repr_p, repr_item_p);
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, repr_count);
assert (record.length == CATALOG_REPR_ITEM_SIZE * 2);
if (spage_update (thread_p, page_p, rep_dir_p->slotid, &record) != SP_SUCCESS)
{
recdes_free_data_area (&record);
pgbuf_unfix_and_init (thread_p, page_p);
return ER_FAILED;
}
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir_p->slotid, &record);
pgbuf_set_dirty (thread_p, page_p, FREE);
/* delete the old representation */
if (catalog_drop_disk_representation_from_page (thread_p, &page_id, slot_id) != NO_ERROR)
{
recdes_free_data_area (&record);
return ER_FAILED;
}
}
else /* a new representation identifier */
{
assert (repr_count == 1);
/* copy old directory for logging purposes */
if (recdes_allocate_data_area (&tmp_record, record.length) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
return ER_FAILED;
}
tmp_record.length = record.length;
tmp_record.type = record.type;
memcpy (tmp_record.data, record.data, record.length);
/* add the new representation item */
catalog_put_repr_item_to_record (record.data + CATALOG_REPR_ITEM_SIZE, repr_item_p);
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, repr_count + 1);
assert (OR_GET_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF) == 2);
assert (record.length == CATALOG_REPR_ITEM_SIZE * 2);
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir_p->slotid, &tmp_record);
success = spage_update (thread_p, page_p, rep_dir_p->slotid, &record);
if (success == SP_SUCCESS)
{
recdes_free_data_area (&tmp_record);
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir_p->slotid, &record);
pgbuf_set_dirty (thread_p, page_p, FREE);
}
#if 0 /* TODO - dead code; do not delete me for future use */
else if (success == SP_DOESNT_FIT)
{
assert (false); /* is impossible */
/* the directory needs to be deleted from the current page and moved to another page. */
ehash_delete (thread_p, &catalog_Id.xhid, key);
log_append_undoredo_recdes2 (thread_p, RVCT_DELETE, &catalog_Id.vfid, page_p, rep_dir_p->slotid,
&tmp_record, NULL);
recdes_free_data_area (&tmp_record);
spage_delete (thread_p, page_p, rep_dir_p->slotid);
new_space = spage_max_space_for_new_record (thread_p, page_p);
recdes_set_data_area (&tmp_record, aligned_page_header_data, CATALOG_PAGE_HEADER_SIZE);
if (catalog_adjust_directory_count (thread_p, page_p, &tmp_record, -1) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
return ER_FAILED;
}
pgbuf_set_dirty (thread_p, page_p, FREE);
catalog_update_max_space (&page_id, new_space);
if (catalog_insert_representation_item (thread_p, &record, rep_dir_p) != NO_ERROR)
{
recdes_free_data_area (&record);
return ER_FAILED;
}
}
#endif
else
{
assert (false); /* is impossible */
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&tmp_record);
recdes_free_data_area (&record);
return ER_FAILED;
}
}
}
recdes_free_data_area (&record);
return NO_ERROR;
}
/*
* catalog_get_representation_item () -
* return: NO_ERROR or ER_FAILED
* class_id(in): Class object identifier
* repr_item(in/out): Set to the Representation Item
* (repr_item->repr_id must be set by the caller)
*
* Note: The representation item for the given class and the specified
* representation identifier (set by the caller in repr_item) is
* extracted from the class directory. This information tells
* the caller where, in the catalog, the specifed represenation unit resides.
*/
static int
catalog_get_representation_item (THREAD_ENTRY * thread_p, OID * class_id_p, CATALOG_REPR_ITEM * repr_item_p)
{
PAGE_PTR page_p;
RECDES record;
OID rep_dir;
CATALOG_ENTRY *catalog_value_p;
int repr_pos, repr_count;
char *repr_p;
CATALOG_KEY catalog_key;
OID_SET_NULL (&rep_dir); /* init */
catalog_key.page_id = class_id_p->pageid;
catalog_key.volid = class_id_p->volid;
catalog_key.slot_id = class_id_p->slotid;
catalog_key.repr_id = repr_item_p->repr_id;
catalog_value_p = catalog_Hashmap.find (thread_p, catalog_key);
if (catalog_value_p != NULL)
{
/* entry already exists */
repr_item_p->page_id.volid = catalog_value_p->key.r_page_id.volid;
repr_item_p->page_id.pageid = catalog_value_p->key.r_page_id.pageid;
repr_item_p->slot_id = catalog_value_p->key.r_slot_id;
/* end transaction */
catalog_Hashmap.unlock (thread_p, catalog_value_p);
return NO_ERROR;
}
else
{
/* fresh entry, fetch data for it */
assert (OID_ISNULL (&rep_dir));
page_p =
catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_READ, PEEK, &rep_dir,
&repr_count, false /* lookup_hash */ );
if (page_p == NULL)
{
return ER_FAILED;
}
repr_p = catalog_find_representation_item_position (repr_item_p->repr_id, repr_count, record.data, &repr_pos);
if (repr_pos == repr_count)
{
pgbuf_unfix_and_init (thread_p, page_p);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_UNKNOWN_REPRID, 1, repr_item_p->repr_id);
return ER_FAILED;
}
repr_item_p->page_id.pageid = CATALOG_GET_REPR_ITEM_PAGEID_PAGEID (repr_p);
repr_item_p->page_id.volid = CATALOG_GET_REPR_ITEM_PAGEID_VOLID (repr_p);
repr_item_p->slot_id = CATALOG_GET_REPR_ITEM_SLOTID (repr_p);
pgbuf_unfix_and_init (thread_p, page_p);
/* set entry info for further use */
catalog_key.r_page_id.pageid = repr_item_p->page_id.pageid;
catalog_key.r_page_id.volid = repr_item_p->page_id.volid;
catalog_key.r_slot_id = repr_item_p->slot_id;
/* insert value */
catalog_Hashmap.find_or_insert (thread_p, catalog_key, catalog_value_p);
if (catalog_value_p != NULL)
{
catalog_Hashmap.unlock (thread_p, catalog_value_p);
return NO_ERROR;
}
else
{
/* impossible case */
#if defined(CT_DEBUG)
er_log_debug (ARG_FILE_LINE,
"catalog_get_representation_item: Insertion to hash table failed.\n "
"Key: Class_Id: { %d , %d , %d } Repr: %d", class_id_p->pageid, class_id_p->volid,
class_id_p->slotid, repr_item_p->repr_id);
#endif /* CT_DEBUG */
assert (false);
return ER_FAILED;
}
}
}
static int
catalog_drop_directory (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_p, OID * oid_p, OID * class_id_p)
{
log_append_undoredo_recdes2 (thread_p, RVCT_DELETE, &catalog_Id.vfid, page_p, oid_p->slotid, record_p, NULL);
if (spage_delete (thread_p, page_p, oid_p->slotid) != oid_p->slotid)
{
return ER_FAILED;
}
if (catalog_adjust_directory_count (thread_p, page_p, record_p, -1) != NO_ERROR)
{
return ER_FAILED;
}
return NO_ERROR;
}
/*
* catalog_drop_representation_item () -
* return: NO_ERROR or ER_FAILED
* class_id(in): Class object identifier
* repr_item(in): Representation Item
* (repr_item->repr_id must be set by the caller)
*
* Note: The representation item for the given class and the specified
* representation identifier (set by the caller in repr_item) is
* dropped from the class directory.
*/
static int
catalog_drop_representation_item (THREAD_ENTRY * thread_p, OID * class_id_p, CATALOG_REPR_ITEM * repr_item_p)
{
PAGE_PTR page_p;
PGLENGTH new_space;
char *repr_p, *next_p;
int repr_pos, repr_count;
RECDES record;
OID rep_dir;
VPID vpid;
CATALOG_REPR_ITEM tmp_repr_item = CATALOG_REPR_ITEM_INITIALIZER;
OID_SET_NULL (&rep_dir); /* init */
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
return ER_FAILED;
}
page_p =
catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_WRITE, COPY, &rep_dir,
&repr_count, true /* lookup_hash */ );
if (page_p == NULL)
{
recdes_free_data_area (&record);
return ER_FAILED;
}
repr_p = catalog_find_representation_item_position (repr_item_p->repr_id, repr_count, record.data, &repr_pos);
if (repr_pos >= repr_count)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_UNKNOWN_REPRID, 1, repr_item_p->repr_id);
return ER_FAILED;
}
repr_item_p->page_id.pageid = CATALOG_GET_REPR_ITEM_PAGEID_PAGEID (repr_p);
repr_item_p->page_id.volid = CATALOG_GET_REPR_ITEM_PAGEID_VOLID (repr_p);
repr_item_p->slot_id = CATALOG_GET_REPR_ITEM_SLOTID (repr_p);
catalog_delete_key (thread_p, class_id_p, repr_item_p->repr_id);
if (repr_count > 1)
{
assert (repr_count == 2);
/* the directory will be updated */
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir.slotid, &record);
next_p = repr_p;
for (next_p += CATALOG_REPR_ITEM_SIZE; repr_pos < (repr_count - 1);
repr_pos++, repr_p += CATALOG_REPR_ITEM_SIZE, next_p += CATALOG_REPR_ITEM_SIZE)
{
catalog_get_repr_item_from_record (&tmp_repr_item, next_p);
catalog_put_repr_item_to_record (repr_p, &tmp_repr_item);
}
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, repr_count - 1);
assert (OR_GET_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF) == 1);
assert (record.length == CATALOG_REPR_ITEM_SIZE * 2);
if (spage_update (thread_p, page_p, rep_dir.slotid, &record) != SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
return ER_FAILED;
}
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir.slotid, &record);
}
else
{
if (catalog_drop_directory (thread_p, page_p, &record, &rep_dir, class_id_p) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
return ER_FAILED;
}
}
new_space = spage_max_space_for_new_record (thread_p, page_p);
pgbuf_set_dirty (thread_p, page_p, FREE);
vpid.volid = rep_dir.volid;
vpid.pageid = rep_dir.pageid;
catalog_update_max_space (&vpid, new_space);
recdes_free_data_area (&record);
return NO_ERROR;
}
static void
catalog_copy_btree_statistic (BTREE_STATS * new_btree_stats_p, int new_btree_stats_count,
BTREE_STATS * pre_btree_stats_p, int pre_btree_stats_count)
{
BTREE_STATS *pre_stats_p, *new_stats_p;
int i, j, k;
for (i = 0, new_stats_p = new_btree_stats_p; i < new_btree_stats_count; i++, new_stats_p++)
{
for (j = 0, pre_stats_p = pre_btree_stats_p; j < pre_btree_stats_count; j++, pre_stats_p++)
{
if (!BTID_IS_EQUAL (&new_stats_p->btid, &pre_stats_p->btid))
{
continue;
}
new_stats_p->btid = pre_stats_p->btid;
new_stats_p->leafs = pre_stats_p->leafs;
new_stats_p->pages = pre_stats_p->pages;
new_stats_p->height = pre_stats_p->height;
new_stats_p->keys = pre_stats_p->keys;
new_stats_p->key_type = pre_stats_p->key_type;
new_stats_p->pkeys_size = pre_stats_p->pkeys_size;
new_stats_p->dedup_idx = pre_stats_p->dedup_idx;
assert (new_stats_p->pkeys_size <= BTREE_STATS_PKEYS_NUM);
for (k = 0; k < new_stats_p->pkeys_size; k++)
{
new_stats_p->pkeys[k] = pre_stats_p->pkeys[k];
}
#if 0 /* reserved for future use */
for (k = 0; k < BTREE_STATS_RESERVED_NUM; k++)
{
new_stats_p->reserved[k] = pre_stats_p->reserved[k];
}
#endif
break;
}
}
}
static void
catalog_copy_disk_attributes (DISK_ATTR * new_attrs_p, int new_attr_count, DISK_ATTR * pre_attrs_p, int pre_attr_count)
{
DISK_ATTR *pre_attr_p, *new_attr_p;
int i, j;
for (i = 0, new_attr_p = new_attrs_p; i < new_attr_count; i++, new_attr_p++)
{
for (j = 0, pre_attr_p = pre_attrs_p; j < pre_attr_count; j++, pre_attr_p++)
{
if (new_attr_p->id != pre_attr_p->id)
{
continue;
}
new_attr_p->ndv = pre_attr_p->ndv;
catalog_copy_btree_statistic (new_attr_p->bt_stats, new_attr_p->n_btstats, pre_attr_p->bt_stats,
pre_attr_p->n_btstats);
}
}
}
/*
* Catalog interface routines
*/
/*
* catalog_initialize () - Initialize data for further catalog operations
* return: nothing
* catid(in): Catalog identifier taken from the system page
*
* Note: Creates and initializes a main memory hash table that will be
* used by catalog operations for fast access to catalog data.
* The max_rec_size global variable which shows the maximum
* available space on a catalog page with a header, is set by the
* data in the catalog header. This routine should always be
* called before any other catalog operations, except catalog creation.
*/
void
catalog_initialize (CTID * catalog_id_p)
{
// protect against repeated hashmap initializations
catalog_Hashmap.destroy ();
VFID_COPY (&catalog_Id.xhid, &catalog_id_p->xhid);
catalog_Id.xhid.pageid = catalog_id_p->xhid.pageid;
catalog_Id.vfid.fileid = catalog_id_p->vfid.fileid;
catalog_Id.vfid.volid = catalog_id_p->vfid.volid;
catalog_Id.hpgid = catalog_id_p->hpgid;
// init
catalog_Hashmap.init (catalog_Ts, THREAD_TS_CATALOG, CATALOG_HASH_SIZE, 2, 100, catalog_entry_Descriptor);
catalog_Max_record_size =
spage_max_record_size () - CATALOG_PAGE_HEADER_SIZE - CATALOG_MAX_SLOT_ID_SIZE - CATALOG_MAX_SLOT_ID_SIZE;
if (catalog_is_header_initialized == false)
{
catalog_initialize_max_space (&catalog_Max_space);
catalog_is_header_initialized = true;
}
}
/*
* catalog_finalize () - Finalize the catalog operations by destroying the catalog
* memory hash table.
* return: nothing
*/
void
catalog_finalize (void)
{
catalog_Hashmap.destroy ();
}
/*
* catalog_create () -
* return: CTID * (catid on success and NULL on failure)
* catalog_id_p(out): Catalog identifier.
* All the fields in the identifier are set except the catalog
* and catalog index volume identifiers which should have been
* set by the caller.
*
* Note: Creates the catalog and an index that will be used for fast
* catalog search. The index used is an extendible hashing index.
* The first page (header page) of the catalog is allocated and
* catalog header information is initialized.
*/
CTID *
catalog_create (THREAD_ENTRY * thread_p, CTID * catalog_id_p)
{
PAGE_PTR page_p;
VPID first_page_vpid;
int new_space;
bool is_overflow_page = false;
log_sysop_start (thread_p);
if (xehash_create (thread_p, &catalog_id_p->xhid, DB_TYPE_OBJECT, 1, oid_Root_class_oid, -1, false) == NULL)
{
ASSERT_ERROR ();
goto error;
}
if (file_create_with_npages (thread_p, FILE_CATALOG, 1, NULL, &catalog_id_p->vfid) != NO_ERROR)
{
ASSERT_ERROR ();
goto error;
}
if (file_alloc_sticky_first_page (thread_p, &catalog_id_p->vfid, catalog_initialize_new_page, &is_overflow_page,
&first_page_vpid, &page_p) != NO_ERROR)
{
ASSERT_ERROR ();
goto error;
}
if (first_page_vpid.volid != catalog_id_p->vfid.volid)
{
assert_release (false);
goto error;
}
if (page_p == NULL)
{
assert_release (false);
goto error;
}
catalog_id_p->hpgid = first_page_vpid.pageid;
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (catalog_is_header_initialized == false)
{
catalog_initialize_max_space (&catalog_Max_space);
catalog_is_header_initialized = true;
}
new_space = spage_max_space_for_new_record (thread_p, page_p);
catalog_update_max_space (&first_page_vpid, new_space);
pgbuf_unfix_and_init (thread_p, page_p);
/* success */
log_sysop_attach_to_outer (thread_p);
return catalog_id_p;
error:
log_sysop_abort (thread_p);
return NULL;
}
#if defined (SA_MODE)
/*
* catalog_file_map_is_empty () - FILE_MAP_PAGE_FUNC to check if catalog pages are empty. if true, it is save into a
* collector.
*
* return : NO_ERROR
* thread_p (in) : thread entry
* page (in) : catalog page pointer
* stop (in) : not used
* args (in) : CATALOG_PAGE_COLLECTOR *
*/
static int
catalog_file_map_is_empty (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args)
{
CATALOG_PAGE_COLLECTOR *collector = (CATALOG_PAGE_COLLECTOR *) args;
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, *page, PAGE_CATALOG);
#endif /* !NDEBUG */
if (spage_number_of_records (*page) <= 1)
{
pgbuf_get_vpid (*page, &collector->vpids[collector->n_vpids++]);
}
return NO_ERROR;
}
#endif /* SA_MODE */
/*
* catalog_reclaim_space () - Reclaim catalog space by deallocating all the empty
* pages.
* return: NO_ERROR or ER_FAILED
*
* Note: This routine is supposed to be called only OFF-LINE.
*/
int
catalog_reclaim_space (THREAD_ENTRY * thread_p)
{
#if defined (SA_MODE)
CATALOG_PAGE_COLLECTOR collector;
int n_pages;
int iter_vpid;
int error_code = NO_ERROR;
/* reinitialize catalog hinted page information, this is needed since the page pointed by this header may be
* deallocated by this routine and the hinted page structure may have a dangling page pointer. */
catalog_initialize_max_space (&catalog_Max_space);
error_code = file_get_num_user_pages (thread_p, &catalog_Id.vfid, &n_pages);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
if (n_pages <= 0)
{
assert_release (false);
return ER_FAILED;
}
collector.vpids = (VPID *) malloc (n_pages * sizeof (VPID));
if (collector.vpids == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, n_pages * sizeof (VPID));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
collector.n_vpids = 0;
error_code =
file_map_pages (thread_p, &catalog_Id.vfid, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH, catalog_file_map_is_empty,
&collector);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
free (collector.vpids);
return error_code;
}
for (iter_vpid = 0; iter_vpid < collector.n_vpids; iter_vpid++)
{
error_code = file_dealloc (thread_p, &catalog_Id.vfid, &collector.vpids[iter_vpid], FILE_CATALOG);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
free (collector.vpids);
return error_code;
}
}
free (collector.vpids);
#endif /* SA_MODE */
return NO_ERROR;
}
static int
catalog_sum_disk_attribute_size (DISK_ATTR * attrs_p, int count)
{
int i, j, size = 0;
DISK_ATTR *disk_attrp;
for (i = 0, disk_attrp = attrs_p; i < count; i++, disk_attrp++)
{
size += CATALOG_DISK_ATTR_SIZE;
size += disk_attrp->val_length + (MAX_ALIGNMENT * 2);
for (j = 0; j < disk_attrp->n_btstats; j++)
{
size += CATALOG_BT_STATS_SIZE;
}
}
return size;
}
/*
* catalog_add_representation () - Add disk representation to the catalog
* return: int
* class_id_p(in): Class identifier
* repr_id(in): Disk Representation identifier
* disk_repr_p(in): Pointer to the disk representation structure
* rep_dir_p(in/out): Representation Directory
* catalog_access_info_p(in): access info on catalog; if this is NULL
* locking of directory OID is performed here,
* otherwise the caller is reponsible for
* protecting concurrent access.*
*/
int
catalog_add_representation (THREAD_ENTRY * thread_p, OID * class_id_p, REPR_ID repr_id, DISK_REPR * disk_repr_p,
OID * rep_dir_p, CATALOG_ACCESS_INFO * catalog_access_info_p)
{
CATALOG_REPR_ITEM repr_item = CATALOG_REPR_ITEM_INITIALIZER;
CATALOG_RECORD catalog_record;
PAGE_PTR page_p;
PGLENGTH new_space;
VPID vpid;
DISK_ATTR *disk_attr_p;
BTREE_STATS *btree_stats_p;
int size, i, j;
char *data;
PGSLOTID remembered_slot_id = NULL_SLOTID;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
int error_code = NO_ERROR;
bool do_end_access = false;
assert (rep_dir_p != NULL);
if (OID_ISTEMP (class_id_p))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_CLASSID, 3, class_id_p->volid, class_id_p->pageid,
class_id_p->slotid);
return ER_CT_INVALID_CLASSID;
}
if (repr_id == NULL_REPRID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_REPRID, 1, repr_id);
return ER_CT_INVALID_REPRID;
}
size = CATALOG_DISK_REPR_SIZE;
size += catalog_sum_disk_attribute_size (disk_repr_p->fixed, disk_repr_p->n_fixed);
size += catalog_sum_disk_attribute_size (disk_repr_p->variable, disk_repr_p->n_variable);
if (catalog_access_info_p == NULL)
{
do_end_access = true;
error_code = catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid);
if (error_code != NO_ERROR)
{
return error_code;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
error_code = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, X_LOCK);
if (error_code != NO_ERROR)
{
return error_code;
}
catalog_access_info_p = &catalog_access_info;
}
page_p = catalog_find_optimal_page (thread_p, size, &vpid);
if (page_p == NULL)
{
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
repr_item.page_id.pageid = vpid.pageid;
repr_item.page_id.volid = vpid.volid;
repr_item.slot_id = NULL_SLOTID;
repr_item.repr_id = repr_id;
data = (char *) db_private_alloc (thread_p, DB_PAGESIZE);
if (!data)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return ER_OUT_OF_VIRTUAL_MEMORY;
}
catalog_record.vpid.pageid = repr_item.page_id.pageid;
catalog_record.vpid.volid = repr_item.page_id.volid;
catalog_record.slotid = NULL_SLOTID;
catalog_record.page_p = page_p;
catalog_record.recdes.area_size = spage_max_space_for_new_record (thread_p, page_p) - CATALOG_MAX_SLOT_ID_SIZE;
catalog_record.recdes.length = 0;
catalog_record.recdes.type = REC_HOME;
catalog_record.recdes.data = data;
catalog_record.offset = 0;
if (catalog_store_disk_representation (thread_p, disk_repr_p, &catalog_record, &remembered_slot_id) != NO_ERROR)
{
db_private_free_and_init (thread_p, data);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
new_space = spage_max_space_for_new_record (thread_p, catalog_record.page_p);
catalog_update_max_space (&repr_item.page_id, new_space);
for (i = 0; i < disk_repr_p->n_fixed + disk_repr_p->n_variable; i++)
{
if (i < disk_repr_p->n_fixed)
{
disk_attr_p = &disk_repr_p->fixed[i];
}
else
{
disk_attr_p = &disk_repr_p->variable[i - disk_repr_p->n_fixed];
}
if (catalog_store_disk_attribute (thread_p, disk_attr_p, &catalog_record, &remembered_slot_id) != NO_ERROR)
{
db_private_free_and_init (thread_p, data);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
if (catalog_store_attribute_value (thread_p, disk_attr_p->value, disk_attr_p->val_length, &catalog_record,
&remembered_slot_id) != NO_ERROR)
{
db_private_free_and_init (thread_p, data);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
for (j = 0; j < disk_attr_p->n_btstats; j++)
{
btree_stats_p = &disk_attr_p->bt_stats[j];
assert (btree_stats_p->pkeys_size <= BTREE_STATS_PKEYS_NUM);
if (catalog_store_btree_statistics (thread_p, btree_stats_p, &catalog_record, &remembered_slot_id) !=
NO_ERROR)
{
db_private_free_and_init (thread_p, data);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
}
}
catalog_record.recdes.length = catalog_record.offset;
catalog_record.offset = catalog_record.recdes.area_size;
if (catalog_put_record_into_page (thread_p, &catalog_record, 0, &remembered_slot_id) != NO_ERROR)
{
db_private_free_and_init (thread_p, data);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
pgbuf_unfix_and_init (thread_p, catalog_record.page_p);
repr_item.slot_id = catalog_record.slotid;
if (catalog_put_representation_item (thread_p, class_id_p, &repr_item, rep_dir_p) != NO_ERROR
|| OID_ISNULL (rep_dir_p))
{
db_private_free_and_init (thread_p, data);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, NO_ERROR);
}
db_private_free_and_init (thread_p, data);
return NO_ERROR;
}
/*
* catalog_add_class_info () - Add class information to the catalog
* return: int
* class_id_p(in): Class identifier
* class_info_p(in): Pointer to class specific information structure
* catalog_access_info_p(in): access info on catalog; if this is NULL
* locking of directory OID is performed here,
* otherwise the caller is reponsible for
* protecting concurrent access.
*/
int
catalog_add_class_info (THREAD_ENTRY * thread_p, OID * class_id_p, CLS_INFO * class_info_p,
CATALOG_ACCESS_INFO * catalog_access_info_p)
{
PAGE_PTR page_p;
VPID page_id;
PGLENGTH new_space;
RECDES record = { -1, -1, REC_HOME, NULL };
CATALOG_REPR_ITEM repr_item = CATALOG_REPR_ITEM_INITIALIZER;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
int success;
int error_code;
bool do_end_access = false;
assert (class_info_p != NULL);
assert (!OID_ISNULL (&(class_info_p->ci_rep_dir)));
OID_SET_NULL (&dir_oid);
if (OID_ISTEMP (class_id_p))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_CLASSID, 3, class_id_p->volid, class_id_p->pageid,
class_id_p->slotid);
return ER_CT_INVALID_CLASSID;
}
assert (repr_item.repr_id == NULL_REPRID);
if (catalog_access_info_p == NULL)
{
do_end_access = true;
error_code = catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid);
if (error_code != NO_ERROR)
{
return error_code;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
error_code = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, X_LOCK);
if (error_code != NO_ERROR)
{
return error_code;
}
catalog_access_info_p = &catalog_access_info;
}
page_p = catalog_find_optimal_page (thread_p, CATALOG_CLS_INFO_SIZE, &page_id);
if (page_p == NULL)
{
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
repr_item.page_id = page_id;
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
/* copy the given representation into a slotted page record */
record.length = CATALOG_CLS_INFO_SIZE;
catalog_put_class_info_to_record (record.data, class_info_p);
memset ((char *) record.data + (CATALOG_CLS_INFO_SIZE - CATALOG_CLS_INFO_RESERVED), 0, CATALOG_CLS_INFO_RESERVED);
success = spage_insert (thread_p, page_p, &record, &repr_item.slot_id);
if (success != SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
log_append_undoredo_recdes2 (thread_p, RVCT_INSERT, &catalog_Id.vfid, page_p, repr_item.slot_id, NULL, &record);
new_space = spage_max_space_for_new_record (thread_p, page_p);
pgbuf_set_dirty (thread_p, page_p, FREE);
catalog_update_max_space (&repr_item.page_id, new_space);
if (catalog_put_representation_item (thread_p, class_id_p, &repr_item, &(class_info_p->ci_rep_dir)) != NO_ERROR)
{
recdes_free_data_area (&record);
ASSERT_ERROR_AND_SET (error_code);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return error_code;
}
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, NO_ERROR);
}
recdes_free_data_area (&record);
return NO_ERROR;
}
/*
* catalog_update_class_info () - Update class information to the catalog
* return: CLS_INFO* or NULL
* class_id(in): Class identifier
* cls_info(in): Pointer to class specific information structure
* catalog_access_info_p(in): access info on catalog; if this is NULL
* locking of directory OID is performed here,
* otherwise the caller is reponsible for
* protecting concurrent access.
* skip_logging(in): true for skip logging. Otherwise, false
*
* Note: The given class specific information structure is used to
* update existing class specific information. On success, it
* returns the cls_info parameter itself, on failure it returns NULL.
*
* Note: This routine assumes that cls_info structure is of fixed size, ie.
* the new record size is equal to the old record size, therefore it is
* possible to update the old record in_place without moving.
*
* Note: The skip_logging parameter of the function may have FALSE value
* in most cases. However it may have TRUE value in exceptional cases such as
* the locator_increase_catalog_count() or locator_decrease_catalog_count().
* Be sure that no log will be made when the parameter is TRUE.
*/
CLS_INFO *
catalog_update_class_info (THREAD_ENTRY * thread_p, OID * class_id_p, CLS_INFO * class_info_p,
CATALOG_ACCESS_INFO * catalog_access_info_p, bool skip_logging)
{
PAGE_PTR page_p;
char data[CATALOG_CLS_INFO_SIZE + MAX_ALIGNMENT], *aligned_data;
RECDES record = { CATALOG_CLS_INFO_SIZE, CATALOG_CLS_INFO_SIZE, REC_HOME, NULL };
CATALOG_REPR_ITEM repr_item = CATALOG_REPR_ITEM_INITIALIZER;
LOG_DATA_ADDR addr;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
bool do_end_access = false;
assert (class_info_p != NULL);
assert (!OID_ISNULL (&(class_info_p->ci_rep_dir)));
if (catalog_access_info_p == NULL)
{
do_end_access = true;
if (catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid) != NO_ERROR)
{
return NULL;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
if (catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, X_LOCK) != NO_ERROR)
{
return NULL;
}
catalog_access_info_p = &catalog_access_info;
}
aligned_data = PTR_ALIGN (data, MAX_ALIGNMENT);
assert (repr_item.repr_id == NULL_REPRID);
repr_item.repr_id = NULL_REPRID;
if (catalog_get_representation_item (thread_p, class_id_p, &repr_item) != NO_ERROR)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
page_p = pgbuf_fix (thread_p, &repr_item.page_id, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
recdes_set_data_area (&record, aligned_data, CATALOG_CLS_INFO_SIZE);
if (spage_get_record (thread_p, page_p, repr_item.slot_id, &record, COPY) != S_SUCCESS)
{
#if defined(CT_DEBUG)
if (er_errid () == ER_SP_UNKNOWN_SLOTID)
er_log_debug (ARG_FILE_LINE, "catalog_update_class_info: ",
"no class information record found in catalog.\n"
"possibly catalog index points to a non_existent disk repr.\n");
#endif /* CT_DEBUG */
pgbuf_unfix_and_init (thread_p, page_p);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
if (skip_logging != true)
{
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, repr_item.slot_id, &record);
}
catalog_put_class_info_to_record (record.data, class_info_p);
memset ((char *) record.data + (CATALOG_CLS_INFO_SIZE - CATALOG_CLS_INFO_RESERVED), 0, CATALOG_CLS_INFO_RESERVED);
if (spage_update (thread_p, page_p, repr_item.slot_id, &record) != SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, page_p);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
if (skip_logging != true)
{
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, repr_item.slot_id, &record);
}
if (skip_logging == true)
{
addr.vfid = &catalog_Id.vfid;
addr.pgptr = page_p;
addr.offset = repr_item.slot_id;
log_skip_logging (thread_p, &addr);
}
pgbuf_set_dirty (thread_p, page_p, FREE);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, NO_ERROR);
}
return class_info_p;
}
/*
* catalog_drop () - Drop representation/class information from catalog
* return: int
* class_id(in): Class identifier
* repr_id(in): Representation identifier
*
*
*/
static int
catalog_drop (THREAD_ENTRY * thread_p, OID * class_id_p, REPR_ID repr_id)
{
CATALOG_REPR_ITEM repr_item;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
int error_code = NO_ERROR;
repr_item.repr_id = repr_id;
VPID_SET_NULL (&repr_item.page_id);
repr_item.slot_id = NULL_SLOTID;
error_code = catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid);
if (error_code != NO_ERROR)
{
return error_code;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
error_code = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, X_LOCK);
if (error_code != NO_ERROR)
{
return error_code;
}
if ((catalog_drop_representation_item (thread_p, class_id_p, &repr_item) != NO_ERROR)
|| repr_item.page_id.pageid == NULL_PAGEID || repr_item.page_id.volid == NULL_VOLID
|| repr_item.slot_id == NULL_SLOTID)
{
ASSERT_ERROR_AND_SET (error_code);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return error_code;
}
if (catalog_drop_disk_representation_from_page (thread_p, &repr_item.page_id, repr_item.slot_id) != NO_ERROR)
{
ASSERT_ERROR_AND_SET (error_code);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return error_code;
}
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, NO_ERROR);
return NO_ERROR;
}
/*
* catalog_drop_all () - Drop all representations for the class from the catalog
* return: int
* class_id(in): Class identifier
*
* Note: All the disk representations of the given class identifier are
* dropped from the catalog.
*/
static int
catalog_drop_all (THREAD_ENTRY * thread_p, OID * class_id_p)
{
PAGE_PTR page_p;
RECDES record;
int i, repr_count;
OID rep_dir;
char *repr_p;
REPR_ID repr_id;
int error;
OID_SET_NULL (&rep_dir); /* init */
if (OID_ISTEMP (class_id_p))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_CLASSID, 3, class_id_p->volid, class_id_p->pageid,
class_id_p->slotid);
return ER_CT_INVALID_CLASSID;
}
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
page_p = catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_READ, COPY,
&rep_dir, &repr_count, true /* lookup_hash */ );
if (page_p == NULL)
{
recdes_free_data_area (&record);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
repr_p = record.data;
pgbuf_unfix_and_init (thread_p, page_p);
/* drop each representation one by one */
for (i = 0; i < repr_count; i++, repr_p += CATALOG_REPR_ITEM_SIZE)
{
repr_id = CATALOG_GET_REPR_ITEM_REPRID (repr_p);
if (repr_id == NULL_REPRID)
{
continue;
}
error = catalog_drop (thread_p, class_id_p, repr_id);
if (error < 0)
{
recdes_free_data_area (&record);
return error;
}
}
recdes_free_data_area (&record);
return NO_ERROR;
}
/*
* catalog_drop_all_representation_and_class () -
* return: NO_ERROR or ER_FAILED
* class_id(in): Class identifier
*
* Note: All the disk representations of the given class identifier and
* the class specific information of the class, if any, are
* deleted from the catalog, as well as the class representations
* directory and the index entry for the given class.
* If there is no representation and class specific information
* for the given class identifier, an error condition is raised.
*
* The catalog index is consulted to locate the class directory.
* The class specific information and each representation
* pointed to by the class directory is deleted from the catalog.
*/
static int
catalog_drop_all_representation_and_class (THREAD_ENTRY * thread_p, OID * class_id_p)
{
PAGE_PTR page_p;
PGLENGTH new_space;
RECDES record;
int i, repr_count;
OID rep_dir;
VPID vpid;
char *repr_p;
REPR_ID repr_id;
VPID page_id;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
PGSLOTID slot_id;
int error_code = NO_ERROR;
OID_SET_NULL (&rep_dir); /* init */
if (OID_ISTEMP (class_id_p))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_CLASSID, 3, class_id_p->volid, class_id_p->pageid,
class_id_p->slotid);
return ER_FAILED;
}
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
return ER_FAILED;
}
error_code = catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid);
if (error_code != NO_ERROR)
{
recdes_free_data_area (&record);
return error_code;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
error_code = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, X_LOCK);
if (error_code != NO_ERROR)
{
recdes_free_data_area (&record);
return error_code;
}
page_p = catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_WRITE, COPY,
&rep_dir, &repr_count, true /* lookup_hash */ );
if (page_p == NULL)
{
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return ER_FAILED;
}
repr_p = record.data;
vpid.volid = rep_dir.volid;
vpid.pageid = rep_dir.pageid;
/* drop each representation item one by one */
for (i = 0; i < repr_count; i++, repr_p += CATALOG_REPR_ITEM_SIZE)
{
repr_id = CATALOG_GET_REPR_ITEM_REPRID (repr_p);
page_id.pageid = CATALOG_GET_REPR_ITEM_PAGEID_PAGEID (repr_p);
page_id.volid = CATALOG_GET_REPR_ITEM_PAGEID_VOLID (repr_p);
slot_id = CATALOG_GET_REPR_ITEM_SLOTID (repr_p);
catalog_delete_key (thread_p, class_id_p, repr_id);
if (catalog_drop_representation_class_from_page (thread_p, &vpid, &page_p, &page_id, slot_id) != NO_ERROR)
{
if (page_p != NULL)
{
pgbuf_unfix_and_init (thread_p, page_p);
}
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return ER_FAILED;
}
assert (page_p != NULL);
}
if (catalog_drop_directory (thread_p, page_p, &record, &rep_dir, class_id_p) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return ER_FAILED;
}
new_space = spage_max_space_for_new_record (thread_p, page_p);
pgbuf_set_dirty (thread_p, page_p, FREE);
catalog_update_max_space (&vpid, new_space);
recdes_free_data_area (&record);
catalog_delete_key (thread_p, class_id_p, CATALOG_DIR_REPR_KEY);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, NO_ERROR);
return NO_ERROR;
}
/*
* catalog_drop_old_representations () -
* return: NO_ERROR or ER_FAILED
* class_id(in): Class identifier
*
* Note: All the disk representations of the given class identifier but
* the most recent one are deleted from the catalog. if there is
* no such class stored in the catalog, an error condition is raised.
*/
int
catalog_drop_old_representations (THREAD_ENTRY * thread_p, OID * class_id_p)
{
PAGE_PTR page_p;
PGLENGTH new_space;
RECDES record;
int i, repr_count;
OID rep_dir;
VPID vpid;
char *repr_p, *last_repr_p;
REPR_ID repr_id;
VPID page_id;
PGSLOTID slot_id;
CATALOG_REPR_ITEM last_repr, class_repr;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
int is_any_dropped;
bool error_code = NO_ERROR;
OID_SET_NULL (&rep_dir); /* init */
if (OID_ISTEMP (class_id_p))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_CLASSID, 3, class_id_p->volid, class_id_p->pageid,
class_id_p->slotid);
return ER_FAILED;
}
if (recdes_allocate_data_area (&record, DB_PAGESIZE) != NO_ERROR)
{
return ER_FAILED;
}
error_code = catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid);
if (error_code != NO_ERROR)
{
recdes_free_data_area (&record);
return error_code;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
error_code = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, X_LOCK);
if (error_code != NO_ERROR)
{
recdes_free_data_area (&record);
return error_code;
}
page_p = catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_WRITE, COPY,
&rep_dir, &repr_count, true /* lookup_hash */ );
if (page_p == NULL)
{
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return ER_FAILED;
}
repr_p = record.data;
vpid.volid = rep_dir.volid;
vpid.pageid = rep_dir.pageid;
VPID_SET_NULL (&last_repr.page_id);
VPID_SET_NULL (&class_repr.page_id);
class_repr.repr_id = NULL_REPRID;
class_repr.slot_id = NULL_SLOTID;
if (repr_count > 0)
{
last_repr_p = repr_p + ((repr_count - 1) * CATALOG_REPR_ITEM_SIZE);
catalog_get_repr_item_from_record (&last_repr, last_repr_p);
if (last_repr.repr_id == NULL_REPRID && repr_count > 1)
{
last_repr_p = repr_p + ((repr_count - 2) * CATALOG_REPR_ITEM_SIZE);
catalog_get_repr_item_from_record (&last_repr, last_repr_p);
}
}
is_any_dropped = false;
for (i = 0; i < repr_count; i++, repr_p += CATALOG_REPR_ITEM_SIZE)
{
repr_id = CATALOG_GET_REPR_ITEM_REPRID (repr_p);
page_id.pageid = CATALOG_GET_REPR_ITEM_PAGEID_PAGEID (repr_p);
page_id.volid = CATALOG_GET_REPR_ITEM_PAGEID_VOLID (repr_p);
slot_id = CATALOG_GET_REPR_ITEM_SLOTID (repr_p);
if (repr_id != NULL_REPRID && repr_id != last_repr.repr_id)
{
catalog_delete_key (thread_p, class_id_p, repr_id);
if (catalog_drop_representation_class_from_page (thread_p, &vpid, &page_p, &page_id, slot_id) != NO_ERROR)
{
if (page_p != NULL)
{
pgbuf_unfix_and_init (thread_p, page_p);
}
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return ER_FAILED;
}
assert (page_p != NULL);
is_any_dropped = true;
}
else if (repr_id == NULL_REPRID)
{
class_repr.repr_id = repr_id;
class_repr.page_id = page_id;
class_repr.slot_id = slot_id;
}
}
/* update the class directory itself, if needed */
if (is_any_dropped)
{
log_append_undo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir.slotid, &record);
if (class_repr.page_id.pageid != NULL_PAGEID)
{
/* first class info */
catalog_put_repr_item_to_record (record.data, &class_repr);
if (last_repr.page_id.pageid != NULL_PAGEID)
{
catalog_put_repr_item_to_record (record.data + CATALOG_REPR_ITEM_SIZE, &last_repr);
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, 2);
}
else
{
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, 1);
}
}
else if (last_repr.page_id.pageid != NULL_PAGEID)
{
/* last repr item */
catalog_put_repr_item_to_record (record.data, &last_repr);
/* save #repr */
OR_PUT_BYTE (record.data + CATALOG_REPR_ITEM_COUNT_OFF, 1);
}
record.length = CATALOG_REPR_ITEM_SIZE * 2;
if (spage_update (thread_p, page_p, rep_dir.slotid, &record) != SP_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, page_p);
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, ER_FAILED);
return ER_FAILED;
}
log_append_redo_recdes2 (thread_p, RVCT_UPDATE, &catalog_Id.vfid, page_p, rep_dir.slotid, &record);
}
new_space = spage_max_space_for_new_record (thread_p, page_p);
pgbuf_set_dirty (thread_p, page_p, FREE);
catalog_update_max_space (&vpid, new_space);
recdes_free_data_area (&record);
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, NO_ERROR);
return NO_ERROR;
}
/*
* xcatalog_check_rep_dir () -
* return: NO_ERROR or ER_FAILED
* class_id(in): Class identifier
* rep_dir_p(out): Representation Directory
*
* Note: Get oid of class representation record
*/
int
xcatalog_check_rep_dir (THREAD_ENTRY * thread_p, OID * class_id_p, OID * rep_dir_p)
{
RECDES record;
PAGE_PTR page_p;
int repr_count;
assert (rep_dir_p != NULL);
OID_SET_NULL (rep_dir_p); /* init */
record.area_size = -1;
if (OID_ISTEMP (class_id_p))
{
assert (false); /* should avoid */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_CLASSID, 3, class_id_p->volid, class_id_p->pageid,
class_id_p->slotid);
return ER_FAILED;
}
page_p = catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_WRITE, PEEK,
rep_dir_p, &repr_count, true /* lookup_hash */ );
assert (er_errid () != ER_HEAP_NODATA_NEWADDRESS); /* TODO - */
if (page_p == NULL)
{
return ER_FAILED;
}
pgbuf_unfix_and_init (thread_p, page_p);
assert (repr_count == 1 || repr_count == 2);
if (OID_ISNULL (rep_dir_p))
{
assert (false);
return ER_FAILED;
}
return NO_ERROR;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* catalog_fixup_missing_disk_representation () -
* return:
* class_oid(in):
* reprid(in):
*/
static int
catalog_fixup_missing_disk_representation (THREAD_ENTRY * thread_p, OID * class_oid_p, REPR_ID repr_id)
{
RECDES record;
DISK_REPR *disk_repr_p;
HEAP_SCANCACHE scan_cache;
OID rep_dir = { NULL_PAGEID, NULL_SLOTID, NULL_VOLID };
int ret = NO_ERROR;
heap_scancache_quick_start_root_hfid (thread_p, &scan_cache);
if (heap_get (thread_p, class_oid_p, &record, &scan_cache, PEEK, NULL_CHN) == S_SUCCESS)
{
disk_repr_p = orc_diskrep_from_record (thread_p, &record);
if (disk_repr_p == NULL)
{
ASSERT_ERROR_AND_SET (ret);
goto end;
}
or_class_rep_dir (&record, &rep_dir);
assert (!OID_ISNULL (&rep_dir));
if (catalog_add_representation (thread_p, class_oid_p, repr_id, disk_repr_p, &rep_dir) < 0)
{
ASSERT_ERROR_AND_SET (ret);
orc_free_diskrep (disk_repr_p);
goto end;
}
orc_free_diskrep (disk_repr_p);
}
end:
heap_scancache_end (thread_p, &scan_cache);
return ret;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* catalog_assign_attribute () -
* return: NO_ERROR or ER_FAILED
* disk_attrp(in): pointer to DISK_ATTR structure (disk representation)
* catalog_record_p(in): pointer to CATALOG_RECORD structure (catalog record)
*/
static int
catalog_assign_attribute (THREAD_ENTRY * thread_p, DISK_ATTR * disk_attr_p, CATALOG_RECORD * catalog_record_p)
{
BTREE_STATS *btree_stats_p;
int i, n_btstats;
if (catalog_fetch_disk_attribute (thread_p, disk_attr_p, catalog_record_p) != NO_ERROR)
{
return ER_FAILED;
}
if (disk_attr_p->val_length > 0)
{
disk_attr_p->value = db_private_alloc (thread_p, disk_attr_p->val_length);
if (disk_attr_p->value == NULL)
{
return ER_FAILED;
}
}
if (catalog_fetch_attribute_value (thread_p, disk_attr_p->value, disk_attr_p->val_length, catalog_record_p) !=
NO_ERROR)
{
return ER_FAILED;
}
n_btstats = disk_attr_p->n_btstats;
if (n_btstats > 0)
{
disk_attr_p->bt_stats = (BTREE_STATS *) db_private_alloc (thread_p, (sizeof (BTREE_STATS) * n_btstats));
if (disk_attr_p->bt_stats == NULL)
{
return ER_FAILED;
}
/* init */
memset (disk_attr_p->bt_stats, 0, sizeof (BTREE_STATS) * n_btstats);
/* fetch all B+tree index statistics of the attribute */
for (i = 0; i < n_btstats; i++)
{
btree_stats_p = &disk_attr_p->bt_stats[i];
if (catalog_fetch_btree_statistics (thread_p, btree_stats_p, catalog_record_p) != NO_ERROR)
{
return ER_FAILED;
}
}
}
return NO_ERROR;
}
/*
* catalog_get_representation () - Get disk representation from the catalog
* return: Pointer to the disk representation structure, or NULL.
* class_id(in): Class identifier
* repr_id(in): Disk Representation identifier
* catalog_access_info_p(in): access info on catalog; if this is NULL
* locking of directory OID is performed here,
* otherwise the caller is reponsible for
* protecting concurrent access.
*
* Note: The disk representation structure for the given class and
* representation identifier is extracted from the catalog and
* returned. If there is no representation with the given class
* identifier and representation identifier, NULL is returned.
* The memory area for the representation structure should be
* freed explicitly by the caller, possibly by calling the
* catalog_free_representation() routine.
*
* First, the memory hash table is consulted to get the location
* of the representation in the catalog in terms of a page and a
* slot identifier. If the memory hash table doesn't have this
* information, then the catalog index is consulted to get the
* location of the class representations directory in the catalog
* The class directory is searched to locate the entry that
* points to the specified disk representation in the catalog.
* The location of the representation is recorded in the memory
* hash table for further use. If the memory hash table becomes
* full, it is cleared. The disk representation structure is
* formed from the catalog page record and the overflow page
* records, if any, and is returned.
*/
DISK_REPR *
catalog_get_representation (THREAD_ENTRY * thread_p, OID * class_id_p, REPR_ID repr_id,
CATALOG_ACCESS_INFO * catalog_access_info_p)
{
CATALOG_REPR_ITEM repr_item;
CATALOG_RECORD catalog_record;
DISK_REPR *disk_repr_p = NULL;
DISK_ATTR *disk_attr_p = NULL;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
int i;
int error = NO_ERROR;
bool do_end_access = false;
catalog_record.page_p = NULL;
if (catalog_access_info_p == NULL)
{
if (catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid) != NO_ERROR)
{
goto exit_on_error;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
if (catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, S_LOCK) != NO_ERROR)
{
goto exit_on_error;
}
do_end_access = true;
catalog_access_info_p = &catalog_access_info;
}
if (repr_id == NULL_REPRID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_REPRID, 1, repr_id);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
repr_item.page_id.pageid = NULL_PAGEID;
repr_item.page_id.volid = NULL_VOLID;
repr_item.slot_id = NULL_SLOTID;
assert (repr_id != NULL_REPRID);
repr_item.repr_id = repr_id;
if (catalog_get_representation_item (thread_p, class_id_p, &repr_item) != NO_ERROR)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
catalog_record.vpid.pageid = repr_item.page_id.pageid;
catalog_record.vpid.volid = repr_item.page_id.volid;
catalog_record.slotid = repr_item.slot_id;
catalog_record.page_p = NULL;
catalog_record.recdes.length = 0;
catalog_record.offset = 0;
disk_repr_p = (DISK_REPR *) db_private_alloc (thread_p, sizeof (DISK_REPR));
if (!disk_repr_p)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
memset (disk_repr_p, 0, sizeof (DISK_REPR));
if (catalog_fetch_disk_representation (thread_p, disk_repr_p, &catalog_record) != NO_ERROR)
{
if (disk_repr_p)
{
catalog_free_representation_and_init (disk_repr_p);
}
if (catalog_record.page_p)
{
pgbuf_unfix_and_init (thread_p, catalog_record.page_p);
}
if (er_errid () == ER_SP_UNKNOWN_SLOTID)
{
assert (false);
}
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
if (disk_repr_p->n_fixed > 0)
{
disk_repr_p->fixed = (DISK_ATTR *) db_private_alloc (thread_p, (sizeof (DISK_ATTR) * disk_repr_p->n_fixed));
if (!disk_repr_p->fixed)
{
goto exit_on_error;
}
/* init */
memset (disk_repr_p->fixed, 0, sizeof (DISK_ATTR) * disk_repr_p->n_fixed);
}
else
{
disk_repr_p->fixed = NULL;
}
if (disk_repr_p->n_variable > 0)
{
disk_repr_p->variable = (DISK_ATTR *) db_private_alloc (thread_p, (sizeof (DISK_ATTR) * disk_repr_p->n_variable));
if (!disk_repr_p->variable)
{
goto exit_on_error;
}
/* init */
memset (disk_repr_p->variable, 0, sizeof (DISK_ATTR) * disk_repr_p->n_variable);
}
else
{
disk_repr_p->variable = NULL;
}
for (i = 0; i < disk_repr_p->n_fixed; i++)
{
if (catalog_assign_attribute (thread_p, &disk_repr_p->fixed[i], &catalog_record) != NO_ERROR)
{
goto exit_on_error;
}
}
for (i = 0; i < disk_repr_p->n_variable; i++)
{
if (catalog_assign_attribute (thread_p, &disk_repr_p->variable[i], &catalog_record) != NO_ERROR)
{
goto exit_on_error;
}
}
exit_on_end:
if (catalog_record.page_p)
{
pgbuf_unfix_and_init (thread_p, catalog_record.page_p);
}
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, error);
}
return disk_repr_p;
exit_on_error:
error = ER_FAILED;
if (disk_repr_p)
{
catalog_free_representation_and_init (disk_repr_p);
}
goto exit_on_end;
}
#if defined (ENABLE_UNUSED_FUNCTION)
static int
catalog_fixup_missing_class_info (THREAD_ENTRY * thread_p, OID * class_oid_p)
{
RECDES record;
HEAP_SCANCACHE scan_cache;
CLS_INFO class_info = CLS_INFO_INITIALIZER;
heap_scancache_quick_start_root_hfid (thread_p, &scan_cache);
if (heap_get (thread_p, class_oid_p, &record, &scan_cache, PEEK, NULL_CHN) == S_SUCCESS)
{
or_class_hfid (&record, &(class_info.ci_hfid));
or_class_rep_dir (&record, &(class_info.ci_rep_dir));
assert (!OID_ISNULL (&(class_info.ci_rep_dir)));
if (catalog_add_class_info (thread_p, class_oid_p, &class_info) < 0)
{
heap_scancache_end (thread_p, &scan_cache);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
heap_scancache_end (thread_p, &scan_cache);
return NO_ERROR;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* catalog_get_class_info () - Get class information from the catalog
* return: Pointer to the class information structure, or NULL.
* class_id_p(in): Class identifier
* catalog_access_info_p(in): access info on catalog; if this is NULL
* locking of directory OID is performed here,
* otherwise the caller is reponsible for
* protecting concurrent access.
*
* Note: The class information structure for the given class is
* extracted from the catalog and returned. If there is no class
* in the catalog with the given class identifier, NULL is returned.
*
* First, the memory hash table is consulted to get the location
* of the class information in the catalog in terms of a page
* and a slot identifier. If the memory hash table doesn't have
* this information, then the catalog index is consulted to get
* location of the class representations directory in the catalog
* The class directory is searched to locate the entry that
* points to the specified class information in the catalog. The
* ocation of the class information is recorded in the memory
* hash table for further use. If the memory hash table becomes
* full, it is cleared. The class information structure is formed
* from the catalog page record and is returned.
*/
CLS_INFO *
catalog_get_class_info (THREAD_ENTRY * thread_p, OID * class_id_p, CATALOG_ACCESS_INFO * catalog_access_info_p)
{
CLS_INFO *class_info_p = NULL;
PAGE_PTR page_p;
char data[CATALOG_CLS_INFO_SIZE + MAX_ALIGNMENT], *aligned_data;
RECDES record = { CATALOG_CLS_INFO_SIZE, CATALOG_CLS_INFO_SIZE, REC_HOME, NULL };
CATALOG_REPR_ITEM repr_item = CATALOG_REPR_ITEM_INITIALIZER;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
bool do_end_access = false;
aligned_data = PTR_ALIGN (data, MAX_ALIGNMENT);
if (catalog_access_info_p == NULL)
{
if (catalog_get_dir_oid_from_cache (thread_p, class_id_p, &dir_oid) != NO_ERROR)
{
return NULL;
}
catalog_access_info.class_oid = class_id_p;
catalog_access_info.dir_oid = &dir_oid;
if (catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, S_LOCK) != NO_ERROR)
{
return NULL;
}
do_end_access = true;
catalog_access_info_p = &catalog_access_info;
}
assert (repr_item.repr_id == NULL_REPRID);
repr_item.repr_id = NULL_REPRID;
if (catalog_get_representation_item (thread_p, class_id_p, &repr_item) != NO_ERROR)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
page_p = pgbuf_fix (thread_p, &repr_item.page_id, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (page_p == NULL)
{
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
recdes_set_data_area (&record, aligned_data, CATALOG_CLS_INFO_SIZE);
if (spage_get_record (thread_p, page_p, repr_item.slot_id, &record, COPY) != S_SUCCESS)
{
#if defined(CT_DEBUG)
if (er_errid () == ER_SP_UNKNOWN_SLOTID)
{
/* If this case happens, means, catalog doesn't contain a page slot which is referred to by index. It is
* possible that the slot has been deleted from the catalog page, but necessary changes have not been
* reflected to the index or to the memory hash table. */
er_log_debug (ARG_FILE_LINE, "catalog_get_class_info: ",
"no class information record found in catalog.\n"
"possibly catalog index points to a non_existent disk repr.\n");
}
#endif /* CT_DEBUG */
if (er_errid () == ER_SP_UNKNOWN_SLOTID)
{
assert (false);
}
pgbuf_unfix_and_init (thread_p, page_p);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
class_info_p = (CLS_INFO *) db_private_alloc (thread_p, sizeof (CLS_INFO));
if (class_info_p == NULL)
{
pgbuf_unfix_and_init (thread_p, page_p);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, ER_FAILED);
}
return NULL;
}
catalog_get_class_info_from_record (class_info_p, record.data);
pgbuf_unfix_and_init (thread_p, page_p);
if (do_end_access)
{
catalog_end_access_with_dir_oid (thread_p, catalog_access_info_p, NO_ERROR);
}
assert (!OID_ISNULL (&(class_info_p->ci_rep_dir)));
return class_info_p;
}
/*
* catalog_get_representation_directory () - Get class representations directory
* return: int
* class_id(in): Class identifier
* reprid_set(out): Set to the set of representation identifiers for the class
* repr_cnt(out): Set to the representation count
*
* Note: The set of representation identifiers for the specified class
* is extracted from the catalog and returned in the memory area
* allocated in this routine and pointed to by reprid_set. The
* variable repr_cnt is set to the number of identifiers in this
* set. The representation identifiers are in a chronological
* order of sequence, the first one representing the oldest and
* last one representing the most recent representation for the given class.
*/
int
catalog_get_representation_directory (THREAD_ENTRY * thread_p, OID * class_id_p, REPR_ID ** repr_id_set_p,
int *repr_count_p)
{
OID rep_dir;
PAGE_PTR page_p;
RECDES record;
int i, item_count;
char *repr_p;
REPR_ID *repr_set_p;
size_t buf_size;
OID_SET_NULL (&rep_dir); /* init */
*repr_count_p = 0;
page_p = catalog_get_representation_record_after_search (thread_p, class_id_p, &record, PGBUF_LATCH_READ, PEEK,
&rep_dir, &item_count, true /* lookup_hash */ );
if (page_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
*repr_count_p = item_count;
buf_size = *repr_count_p * sizeof (REPR_ID);
*repr_id_set_p = (REPR_ID *) malloc (buf_size);
if (*repr_id_set_p == NULL)
{
pgbuf_unfix_and_init (thread_p, page_p);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
repr_set_p = *repr_id_set_p;
for (i = 0, repr_p = record.data; i < item_count; i++, repr_p += CATALOG_REPR_ITEM_SIZE)
{
if (CATALOG_GET_REPR_ITEM_REPRID (repr_p) == NULL_REPRID)
{
(*repr_count_p)--;
}
else
{
*repr_set_p = CATALOG_GET_REPR_ITEM_REPRID (repr_p);
repr_set_p++;
}
}
pgbuf_unfix_and_init (thread_p, page_p);
return NO_ERROR;
}
/*
* catalog_get_last_representation_id () -
* return: int
* cls_oid(in): Class identifier
* repr_id(out): Set to the last representation id or NULL_REPRID
*
* Note: The representation identifiers directory for the given class
* is fetched from the catalog and the repr_id parameter is set
* to the most recent representation identifier, or NULL_REPRID.
*/
int
catalog_get_last_representation_id (THREAD_ENTRY * thread_p, OID * class_oid_p, REPR_ID * repr_id_p)
{
OID rep_dir;
PAGE_PTR page_p;
RECDES record;
int i, item_count;
char *repr_p;
OID_SET_NULL (&rep_dir); /* init */
*repr_id_p = NULL_REPRID;
page_p = catalog_get_representation_record_after_search (thread_p, class_oid_p, &record, PGBUF_LATCH_READ, PEEK,
&rep_dir, &item_count, true /* lookup_hash */ );
if (page_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
for (i = 0, repr_p = record.data; i < item_count; i++, repr_p += CATALOG_REPR_ITEM_SIZE)
{
if (CATALOG_GET_REPR_ITEM_REPRID (repr_p) != NULL_REPRID)
{
*repr_id_p = CATALOG_GET_REPR_ITEM_REPRID (repr_p);
}
}
pgbuf_unfix_and_init (thread_p, page_p);
return NO_ERROR;
}
/*
* catalog_insert () - Insert class representation information to catalog
* return: int
* record(in): Record descriptor containing class disk representation info.
* classoid(in): Class object identifier
* rep_dir_p(out): Representation Directory
*
* Note: The disk representation information for the initial
* representation of the class and the class specific information
* is extracted from the record descriptor and stored in the
* catalog. This routine must be the first routine called for the
* storage of class representation informations in the catalog.
*/
int
catalog_insert (THREAD_ENTRY * thread_p, RECDES * record_p, OID * class_oid_p, OID * rep_dir_p)
{
REPR_ID new_repr_id;
DISK_REPR *disk_repr_p = NULL;
CLS_INFO *class_info_p = NULL;
assert (class_oid_p != NULL);
assert (record_p != NULL);
assert (rep_dir_p != NULL);
assert (OID_ISNULL (rep_dir_p));
new_repr_id = (REPR_ID) or_rep_id (record_p);
if (new_repr_id == NULL_REPRID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_REPRID, 1, new_repr_id);
return ER_CT_INVALID_REPRID;
}
disk_repr_p = orc_diskrep_from_record (thread_p, record_p);
if (disk_repr_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
assert (OID_ISNULL (rep_dir_p));
if (catalog_add_representation (thread_p, class_oid_p, new_repr_id, disk_repr_p, rep_dir_p, NULL) < 0
|| OID_ISNULL (rep_dir_p))
{
orc_free_diskrep (disk_repr_p);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
assert (!OID_ISNULL (rep_dir_p));
orc_free_diskrep (disk_repr_p);
class_info_p = orc_class_info_from_record (record_p);
if (class_info_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
/* save representation directory oid into class_info record */
assert (OID_ISNULL (&(class_info_p->ci_rep_dir)));
COPY_OID (&(class_info_p->ci_rep_dir), rep_dir_p);
if (catalog_add_class_info (thread_p, class_oid_p, class_info_p, NULL) < 0)
{
orc_free_class_info (class_info_p);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
orc_free_class_info (class_info_p);
return NO_ERROR;
}
/*
* catalog_update () - Update class representation information to catalog
* return: int
* record(in): Record descriptor containing class disk representation info.
* classoid(in): Class object identifier
*
* Note: The disk representation information for the specified class and
* also possibly the class specific information is updated, if
* the record descriptor contains a new disk representation, ie.
* has a new representation identifier different from the most
* recent representation identifier of the class. The class
* information is extracted and only the HFID field is set, if
* needed, in order to preserve old class statistics.
*/
int
catalog_update (THREAD_ENTRY * thread_p, RECDES * record_p, OID * class_oid_p)
{
REPR_ID current_repr_id, new_repr_id;
DISK_REPR *disk_repr_p = NULL;
DISK_REPR *old_repr_p = NULL;
CLS_INFO *class_info_p = NULL;
HFID class_hfid;
OID rep_dir;
int err;
new_repr_id = (REPR_ID) or_rep_id (record_p);
if (new_repr_id == NULL_REPRID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_INVALID_REPRID, 1, new_repr_id);
return ER_CT_INVALID_REPRID;
}
or_class_rep_dir (record_p, &rep_dir);
assert (!OID_ISNULL (&rep_dir));
disk_repr_p = orc_diskrep_from_record (thread_p, record_p);
if (disk_repr_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (catalog_get_last_representation_id (thread_p, class_oid_p, ¤t_repr_id) < 0)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (current_repr_id != NULL_REPRID)
{
old_repr_p = catalog_get_representation (thread_p, class_oid_p, current_repr_id, NULL);
if (old_repr_p == NULL)
{
if (er_errid () != ER_SP_UNKNOWN_SLOTID)
{
orc_free_diskrep (disk_repr_p);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
/* Migrate statistics from the old representation to the new one */
if (old_repr_p)
{
catalog_copy_disk_attributes (disk_repr_p->fixed, disk_repr_p->n_fixed, old_repr_p->fixed,
old_repr_p->n_fixed);
catalog_copy_disk_attributes (disk_repr_p->variable, disk_repr_p->n_variable, old_repr_p->variable,
old_repr_p->n_variable);
catalog_free_representation_and_init (old_repr_p);
err = catalog_drop (thread_p, class_oid_p, current_repr_id);
if (err != NO_ERROR)
{
orc_free_diskrep (disk_repr_p);
return err;
}
}
}
assert (!OID_ISNULL (&rep_dir));
if (catalog_add_representation (thread_p, class_oid_p, new_repr_id, disk_repr_p, &rep_dir, NULL) < 0)
{
orc_free_diskrep (disk_repr_p);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
orc_free_diskrep (disk_repr_p);
class_info_p = catalog_get_class_info (thread_p, class_oid_p, NULL);
if (class_info_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
assert (OID_EQ (&rep_dir, &(class_info_p->ci_rep_dir)));
or_class_hfid (record_p, &(class_hfid));
if (!HFID_EQ (&class_info_p->ci_hfid, &class_hfid))
{
class_info_p->ci_hfid = class_hfid;
if (!HFID_IS_NULL (&class_info_p->ci_hfid))
{
if (catalog_update_class_info (thread_p, class_oid_p, class_info_p, NULL, false) == NULL)
{
catalog_free_class_info (class_info_p);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
}
catalog_free_class_info (class_info_p);
return NO_ERROR;
}
/*
* catalog_delete () - Delete class representation information from catalog
* return: int
* classoid(in): Class object identifier
*
* Note: All the disk representation information for the class and the
* class specific information are dropped from the catalog.
*/
int
catalog_delete (THREAD_ENTRY * thread_p, OID * class_oid_p)
{
if (catalog_drop_all_representation_and_class (thread_p, class_oid_p) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
return NO_ERROR;
}
/*
* Checkdb consistency check routines
*/
/*
* catalog_check_class_consistency () -
* return: DISK_VALID, DISK_INVALID or DISK_ERROR
* class_oid_p(in): Class object identifier
*
* Note: This function checks the consistency of the class information
* in the class and returns the consistency result. It checks
* if the class points a valid page entry that
* contains the representations directory for the class. It then
* checks for each entry in the representations directory if
* corresponding page slot entry exits.
*/
static DISK_ISVALID
catalog_check_class_consistency (THREAD_ENTRY * thread_p, OID * class_oid_p)
{
OID rep_dir;
RECDES record;
CATALOG_REPR_ITEM repr_item;
VPID vpid;
PAGE_PTR page_p;
PAGE_PTR repr_page_p;
int repr_count;
char *repr_p;
CLS_INFO class_info = CLS_INFO_INITIALIZER;
int i;
DISK_ISVALID valid;
OID_SET_NULL (&rep_dir); /* init */
record.area_size = -1;
/* get old directory for the class */
if (catalog_get_rep_dir (thread_p, class_oid_p, &rep_dir, true /* lookup_hash */ ) != NO_ERROR
|| OID_ISNULL (&rep_dir))
{
assert (er_errid () != NO_ERROR);
return DISK_ERROR;
}
/* Check if the catalog index points to an existing representations directory. */
vpid.volid = rep_dir.volid;
vpid.pageid = rep_dir.pageid;
valid = file_check_vpid (thread_p, &catalog_Id.vfid, &vpid);
if (valid != DISK_VALID)
{
if (er_errid () == ER_PB_BAD_PAGEID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, rep_dir.volid, rep_dir.pageid,
rep_dir.slotid);
}
return valid;
}
page_p = catalog_get_representation_record (thread_p, &rep_dir, &record, PGBUF_LATCH_READ, PEEK, &repr_count);
if (page_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_MISSING_REPR_DIR, 3, class_oid_p->volid, class_oid_p->pageid,
class_oid_p->slotid);
return DISK_ERROR;
}
for (i = 0, repr_p = record.data; i < repr_count; i++, repr_p += CATALOG_REPR_ITEM_SIZE)
{
repr_item.page_id.pageid = CATALOG_GET_REPR_ITEM_PAGEID_PAGEID (repr_p);
repr_item.page_id.volid = CATALOG_GET_REPR_ITEM_PAGEID_VOLID (repr_p);
repr_item.slot_id = CATALOG_GET_REPR_ITEM_SLOTID (repr_p);
repr_item.repr_id = CATALOG_GET_REPR_ITEM_REPRID (repr_p);
valid = file_check_vpid (thread_p, &catalog_Id.vfid, &repr_item.page_id);
if (valid != DISK_VALID)
{
pgbuf_unfix_and_init (thread_p, page_p);
return valid;
}
repr_page_p = pgbuf_fix (thread_p, &repr_item.page_id, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (repr_page_p == NULL)
{
pgbuf_unfix_and_init (thread_p, page_p);
return DISK_ERROR;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, repr_page_p, PAGE_CATALOG);
#endif /* !NDEBUG */
if (spage_get_record (thread_p, repr_page_p, repr_item.slot_id, &record, PEEK) != S_SUCCESS)
{
pgbuf_unfix_and_init (thread_p, repr_page_p);
pgbuf_unfix_and_init (thread_p, page_p);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_MISSING_REPR_INFO, 4, class_oid_p->volid, class_oid_p->pageid,
class_oid_p->slotid, repr_item.repr_id);
return DISK_ERROR;
}
/* is class info record */
if (repr_item.repr_id == NULL_REPRID)
{
catalog_get_class_info_from_record (&class_info, record.data);
if (!OID_EQ (&rep_dir, &(class_info.ci_rep_dir)))
{
assert (false);
pgbuf_unfix_and_init (thread_p, repr_page_p);
pgbuf_unfix_and_init (thread_p, page_p);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CT_MISSING_REPR_DIR, 3, class_oid_p->volid,
class_oid_p->pageid, class_oid_p->slotid);
return DISK_ERROR;
}
}
pgbuf_unfix_and_init (thread_p, repr_page_p);
}
pgbuf_unfix_and_init (thread_p, page_p);
return DISK_VALID;
}
/*
* catalog_check_consistency () - Check if catalog is in a consistent (valid)
* state.
* return: DISK_VALID, DISK_INVALID or DISK_ERROR
*/
DISK_ISVALID
catalog_check_consistency (THREAD_ENTRY * thread_p)
{
DISK_ISVALID ct_valid = DISK_VALID;
RECDES peek = RECDES_INITIALIZER; /* Record descriptor for peeking object */
HFID root_hfid;
OID class_oid;
#if !defined(NDEBUG)
char *classname = NULL;
#endif
HEAP_SCANCACHE scan_cache;
MVCC_SNAPSHOT *mvcc_snapshot = NULL;
mvcc_snapshot = logtb_get_mvcc_snapshot (thread_p);
if (mvcc_snapshot == NULL)
{
return DISK_ERROR;
}
/* Find every single class */
if (boot_find_root_heap (&root_hfid) != NO_ERROR || HFID_IS_NULL (&root_hfid))
{
goto exit_on_error;
}
if (heap_scancache_start (thread_p, &scan_cache, &root_hfid, oid_Root_class_oid, true, mvcc_snapshot) != NO_ERROR)
{
goto exit_on_error;
}
class_oid.volid = root_hfid.vfid.volid;
class_oid.pageid = NULL_PAGEID;
class_oid.slotid = NULL_SLOTID;
ct_valid = DISK_VALID;
while (heap_next (thread_p, &root_hfid, oid_Root_class_oid, &class_oid, &peek, &scan_cache, PEEK) == S_SUCCESS)
{
#if !defined(NDEBUG)
classname = or_class_name (&peek);
assert (classname != NULL);
assert (strlen (classname) < DB_MAX_IDENTIFIER_LENGTH);
#endif
if (lock_object (thread_p, &class_oid, oid_Root_class_oid, SCH_S_LOCK, LK_COND_LOCK) != LK_GRANTED)
{
/* we do the checks by best-effort. we need the lock to avoid all kind of inconsistencies from doing checks on
* incomplete changes. doing an unconditional lock here is too complicated and unnecessary, since it is
* unusual to do checkdb and schema changes concurrently. */
er_log_debug (ARG_FILE_LINE,
"Skipping checking catalog for class %d|%d|%d because conditional lock failed. \n",
OID_AS_ARGS (&class_oid));
continue;
}
/* check catalog consistency */
ct_valid = catalog_check_class_consistency (thread_p, &class_oid);
/* remove lock */
lock_unlock_object (thread_p, &class_oid, oid_Root_class_oid, SCH_S_LOCK, true);
if (ct_valid != DISK_VALID)
{
break;
}
} /* while (...) */
/* End the scan cursor */
if (heap_scancache_end (thread_p, &scan_cache) != NO_ERROR)
{
ct_valid = DISK_ERROR;
}
return ct_valid;
exit_on_error:
return DISK_ERROR;
}
/*
* catalog_dump_disk_attribute () -
* return:
* atr(in):
*/
static void
catalog_dump_disk_attribute (DISK_ATTR * attr_p)
{
char *value_p;
int i, k;
const char *prefix = "";
fprintf (stdout, "\n");
fprintf (stdout, "Attribute Information: \n\n ");
fprintf (stdout, "Id: %d \n", attr_p->id);
fprintf (stdout, " Type: ");
switch (attr_p->type)
{
case DB_TYPE_INTEGER:
fprintf (stdout, "DB_TYPE_INTEGER \n");
break;
case DB_TYPE_BIGINT:
fprintf (stdout, "DB_TYPE_BIGINT \n");
break;
case DB_TYPE_FLOAT:
fprintf (stdout, "DB_TYPE_FLOAT \n");
break;
case DB_TYPE_DOUBLE:
fprintf (stdout, "DB_TYPE_DOUBLE \n");
break;
case DB_TYPE_STRING:
fprintf (stdout, "DB_TYPE_STRING \n");
break;
case DB_TYPE_OBJECT:
fprintf (stdout, "DB_TYPE_OBJECT \n");
break;
case DB_TYPE_SET:
fprintf (stdout, "DB_TYPE_SET \n");
break;
case DB_TYPE_MULTISET:
fprintf (stdout, "DB_TYPE_MULTISET \n");
break;
case DB_TYPE_SEQUENCE:
fprintf (stdout, "DB_TYPE_SEQUENCE \n");
break;
case DB_TYPE_TIME:
fprintf (stdout, "DB_TYPE_TIME \n");
break;
case DB_TYPE_MONETARY:
fprintf (stdout, "DB_TYPE_MONETARY \n");
break;
case DB_TYPE_DATE:
fprintf (stdout, "DB_TYPE_DATE \n");
break;
case DB_TYPE_BLOB:
fprintf (stdout, "DB_TYPE_BLOB \n");
break;
case DB_TYPE_CLOB:
fprintf (stdout, "DB_TYPE_CLOB \n");
break;
case DB_TYPE_VARIABLE:
fprintf (stdout, "DB_TYPE_VARIABLE \n");
break;
case DB_TYPE_SUB:
fprintf (stdout, "DB_TYPE_SUB \n");
break;
case DB_TYPE_POINTER:
fprintf (stdout, "DB_TYPE_POINTER \n");
break;
case DB_TYPE_NULL:
fprintf (stdout, "DB_TYPE_NULL \n");
break;
case DB_TYPE_JSON:
fprintf (stdout, "DB_TYPE_JSON \n");
break;
default:
break;
}
fprintf (stdout, " Location: %d \n", attr_p->location);
fprintf (stdout, " Source Class_Id: { %d , %d , %d } \n", attr_p->classoid.volid, attr_p->classoid.pageid,
attr_p->classoid.slotid);
fprintf (stdout, " Source Position: %d \n", attr_p->position);
fprintf (stdout, " Def. Value Length: %d \n", attr_p->val_length);
if (attr_p->val_length > 0)
{
value_p = (char *) attr_p->value;
fprintf (stdout, " Value: ");
for (k = 0; k < attr_p->val_length; k++, value_p++)
{
fprintf (stdout, "%02X ", (unsigned char) (*value_p));
}
fprintf (stdout, " \n");
}
fprintf (stdout, " BTree statistics:\n");
for (k = 0; k < attr_p->n_btstats; k++)
{
BTREE_STATS *bt_statsp = &attr_p->bt_stats[k];
fprintf (stdout, " BTID: { %d , %d }\n", bt_statsp->btid.vfid.volid, bt_statsp->btid.vfid.fileid);
fprintf (stdout, " Cardinality: %d (", bt_statsp->keys);
prefix = "";
assert (bt_statsp->pkeys_size <= BTREE_STATS_PKEYS_NUM);
assert (bt_statsp->dedup_idx != 0);
int pkeys_size = (bt_statsp->dedup_idx >= 0) ? bt_statsp->dedup_idx : bt_statsp->pkeys_size;
for (i = 0; i < pkeys_size; i++)
{
fprintf (stdout, "%s%d", prefix, bt_statsp->pkeys[i]);
prefix = ",";
}
fprintf (stdout, ") ,");
fprintf (stdout, " Total Pages: %d , Leaf Pages: %d , Height: %d\n", bt_statsp->pages, bt_statsp->leafs,
bt_statsp->height);
}
fprintf (stdout, "\n");
}
/*
* catalog_dump_representation () -
* return:
* dr(in):
*/
static void
catalog_dump_representation (DISK_REPR * disk_repr_p)
{
DISK_ATTR *attr_p;
int i;
if (disk_repr_p != NULL)
{
fprintf (stdout, " DISK REPRESENTATION: \n\n");
fprintf (stdout, " Repr_Id : %d N_Fixed : %d Fixed_Length : %d N_Variable : %d\n\n", disk_repr_p->id,
disk_repr_p->n_fixed, disk_repr_p->fixed_length, disk_repr_p->n_variable);
fprintf (stdout, " Fixed Attribute Representations : \n\n");
attr_p = disk_repr_p->fixed;
for (i = 0; i < disk_repr_p->n_fixed; i++, attr_p++)
{
catalog_dump_disk_attribute (attr_p);
}
fprintf (stdout, " Variable Attribute Representations : \n\n");
attr_p = disk_repr_p->variable;
for (i = 0; i < disk_repr_p->n_variable; i++, attr_p++)
{
catalog_dump_disk_attribute (attr_p);
}
}
}
/*
* catalog_file_map_page_dump () - FILE_MAP_PAGE_FUNC for catalog page dump
*
* return : error code
* thread_p (in) : thread entry
* page (in) : catalog page pointer
* stop (in) : not used
* args (in) : FILE *
*/
static int
catalog_file_map_page_dump (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args)
{
CATALOG_PAGE_DUMP_CONTEXT *context = (CATALOG_PAGE_DUMP_CONTEXT *) args;
RECDES record;
if (spage_get_record (thread_p, *page, CATALOG_HEADER_SLOT, &record, PEEK) != S_SUCCESS)
{
assert_release (false);
return ER_FAILED;
}
fprintf (context->fp, "\n-----------------------------------------------\n");
fprintf (context->fp, "\n Page %d \n", context->page_index);
fprintf (context->fp, "\nPage_Id: {%d , %d}\n", PGBUF_PAGE_VPID_AS_ARGS (*page));
fprintf (context->fp, "Directory Cnt: %d\n", CATALOG_GET_PGHEADER_DIR_COUNT (record.data));
fprintf (context->fp, "Overflow Page Id: {%d , %d}\n\n", CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID (record.data),
CATALOG_GET_PGHEADER_OVFL_PGID_VOLID (record.data));
spage_dump (thread_p, context->fp, *page, 0);
context->page_index++;
return NO_ERROR;
}
/*
* catalog_file_map_overflow_count () - FILE_MAP_PAGE_FUNC to count overflows
*
* return : error code
* thread_p (in) : thread entry
* page (in) : catalog page pointer
* stop (in) : not used
* args (in) : overflow count
*/
static int
catalog_file_map_overflow_count (THREAD_ENTRY * thread_p, PAGE_PTR * page, bool * stop, void *args)
{
int *overflow_count = (int *) args;
RECDES record;
if (spage_get_record (thread_p, *page, CATALOG_HEADER_SLOT, &record, PEEK) != S_SUCCESS)
{
assert_release (false);
return ER_FAILED;
}
if (CATALOG_GET_PGHEADER_OVFL_PGID_PAGEID (record.data) != NULL_PAGEID)
{
(*overflow_count)++;
}
return NO_ERROR;
}
/*
* catalog_dump () - The content of catalog is dumped.
* return: nothing
* dump_flg(in): Catalog dump flag. Should be set to:
* 0 : for catalog information content dump
* 1 : for catalog and catalog index slotted page dump
*/
void
catalog_dump (THREAD_ENTRY * thread_p, FILE * fp, int dump_flag)
{
RECDES peek = RECDES_INITIALIZER; /* Record descriptor for peeking object */
HFID root_hfid;
OID class_oid;
#if !defined(NDEBUG)
char *classname = NULL;
#endif
HEAP_SCANCACHE scan_cache;
MVCC_SNAPSHOT *mvcc_snapshot = NULL;
REPR_ID *repr_id_set, *repr_id_p;
DISK_REPR *disk_repr_p = NULL;
int repr_count;
CLS_INFO *class_info_p = NULL;
int n, overflow_count;
CATALOG_PAGE_DUMP_CONTEXT page_dump_context;
fprintf (fp, "\n <<<<< C A T A L O G D U M P >>>>> \n\n");
fprintf (fp, "\n Catalog Dump: \n\n");
mvcc_snapshot = logtb_get_mvcc_snapshot (thread_p);
if (mvcc_snapshot == NULL)
{
return;
}
/* Find every single class */
if (boot_find_root_heap (&root_hfid) != NO_ERROR || HFID_IS_NULL (&root_hfid))
{
return;
}
if (heap_scancache_start (thread_p, &scan_cache, &root_hfid, oid_Root_class_oid, true, mvcc_snapshot) != NO_ERROR)
{
return;
}
class_oid.volid = root_hfid.vfid.volid;
class_oid.pageid = NULL_PAGEID;
class_oid.slotid = NULL_SLOTID;
while (heap_next (thread_p, &root_hfid, oid_Root_class_oid, &class_oid, &peek, &scan_cache, PEEK) == S_SUCCESS)
{
#if !defined(NDEBUG)
classname = or_class_name (&peek);
assert (classname != NULL);
assert (strlen (classname) < DB_MAX_IDENTIFIER_LENGTH);
#endif
fprintf (fp, " -------------------------------------------------\n");
fprintf (fp, " CLASS_ID: { %d , %d , %d } \n", class_oid.volid, class_oid.pageid, class_oid.slotid);
repr_id_set = NULL;
catalog_get_representation_directory (thread_p, &class_oid, &repr_id_set, &repr_count);
fprintf (fp, " Repr_cnt: %d \n", repr_count);
/* get the representations identifiers set for the class */
repr_id_p = repr_id_set;
for (repr_id_p += repr_count - 1; repr_count; repr_id_p--, repr_count--)
{
if (*repr_id_p == NULL_REPRID)
{
continue;
}
fprintf (fp, " Repr_id: %d\n", *repr_id_p);
}
fprintf (fp, "\n");
/* Get the class specific information for this class */
class_info_p = catalog_get_class_info (thread_p, &class_oid, NULL);
if (class_info_p != NULL)
{
fprintf (fp, " Class Specific Information: \n\n");
fprintf (fp, " HFID: { vfid = { %d , %d }, hpgid = %d }\n", class_info_p->ci_hfid.vfid.fileid,
class_info_p->ci_hfid.vfid.volid, class_info_p->ci_hfid.hpgid);
fprintf (fp, " Total Pages in Heap: %d\n", class_info_p->ci_tot_pages);
fprintf (fp, " Total Objects: %d\n", class_info_p->ci_tot_objects);
fprintf (fp, " Representation directory OID: { %d , %d , %d } \n", class_info_p->ci_rep_dir.volid,
class_info_p->ci_rep_dir.pageid, class_info_p->ci_rep_dir.slotid);
catalog_free_class_info_and_init (class_info_p);
}
fprintf (fp, "\n");
/* get the representations identifiers set for the class */
repr_id_p = repr_id_set;
for (repr_id_p += repr_count - 1; repr_count; repr_id_p--, repr_count--)
{
if (*repr_id_p == NULL_REPRID)
{
continue;
}
disk_repr_p = catalog_get_representation (thread_p, &class_oid, *repr_id_p, NULL);
if (disk_repr_p == NULL)
{
continue; /* is error */
}
catalog_dump_representation (disk_repr_p);
catalog_free_representation_and_init (disk_repr_p);
}
if (repr_id_set != NULL)
{
free_and_init (repr_id_set);
}
heap_classrepr_dump_all (thread_p, fp, &class_oid);
} /* while (...) */
/* End the scan cursor */
if (heap_scancache_end (thread_p, &scan_cache) != NO_ERROR)
{
return;
}
if (dump_flag == 1)
{
/* slotted page dump */
fprintf (fp, "\n Catalog Directory Dump: \n\n");
if (file_get_num_user_pages (thread_p, &catalog_Id.vfid, &n) != NO_ERROR)
{
ASSERT_ERROR ();
return;
}
fprintf (fp, "Total Pages Count: %d\n\n", n);
overflow_count = 0;
if (file_map_pages (thread_p, &catalog_Id.vfid, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH,
catalog_file_map_overflow_count, &overflow_count) != NO_ERROR)
{
ASSERT_ERROR ();
return;
}
fprintf (fp, "Regular Pages Count: %d\n\n", n - overflow_count);
fprintf (fp, "Overflow Pages Count: %d\n\n", overflow_count);
page_dump_context.fp = fp;
page_dump_context.page_index = 0;
if (file_map_pages (thread_p, &catalog_Id.vfid, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH,
catalog_file_map_page_dump, &page_dump_context) != NO_ERROR)
{
ASSERT_ERROR ();
return;
}
}
}
static void
catalog_clear_hash_table (THREAD_ENTRY * thread_p)
{
catalog_Hashmap.clear (thread_p);
}
/*
* catalog_rv_new_page_redo () - Redo the initializations of a new catalog page
* return: int
* recv(in): Recovery structure
*/
int
catalog_rv_new_page_redo (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
char data[CATALOG_PAGE_HEADER_SIZE + MAX_ALIGNMENT], *aligned_data;
RECDES record = { CATALOG_PAGE_HEADER_SIZE, CATALOG_PAGE_HEADER_SIZE, REC_HOME, NULL };
PGSLOTID slot_id;
int success;
aligned_data = PTR_ALIGN (data, MAX_ALIGNMENT);
catalog_clear_hash_table (thread_p);
(void) pgbuf_set_page_ptype (thread_p, recv_p->pgptr, PAGE_CATALOG);
spage_initialize (thread_p, recv_p->pgptr, ANCHORED_DONT_REUSE_SLOTS, MAX_ALIGNMENT, SAFEGUARD_RVSPACE);
recdes_set_data_area (&record, aligned_data, CATALOG_PAGE_HEADER_SIZE);
catalog_put_page_header (record.data, (CATALOG_PAGE_HEADER *) recv_p->data);
success = spage_insert (thread_p, recv_p->pgptr, &record, &slot_id);
if (success != SP_SUCCESS || slot_id != CATALOG_HEADER_SLOT)
{
if (success != SP_SUCCESS)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
}
assert (er_errid () != NO_ERROR);
return er_errid ();
}
pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
return NO_ERROR;
}
/*
* catalog_rv_insert_redo () - Redo the insertion of a record at a specific slot.
* return: int
* recv(in): Recovery structure
*/
int
catalog_rv_insert_redo (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
PGSLOTID slot_id;
RECDES record;
int success;
catalog_clear_hash_table (thread_p);
slot_id = recv_p->offset;
recdes_set_data_area (&record, (char *) (recv_p->data) + sizeof (record.type), recv_p->length - sizeof (record.type));
record.length = record.area_size;
record.type = *(INT16 *) (recv_p->data);
success = spage_insert_for_recovery (thread_p, recv_p->pgptr, slot_id, &record);
if (success != SP_SUCCESS)
{
if (success != SP_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
}
assert (er_errid () != NO_ERROR);
return er_errid ();
}
pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
return NO_ERROR;
}
/*
* catalog_rv_insert_undo () - Undo the insert of a record by deleting the record.
* return: int
* recv(in): Recovery structure
*/
int
catalog_rv_insert_undo (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
PGSLOTID slot_id;
catalog_clear_hash_table (thread_p);
slot_id = recv_p->offset;
(void) spage_delete_for_recovery (thread_p, recv_p->pgptr, slot_id);
pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
return NO_ERROR;
}
/*
* catalog_rv_delete_redo () - Redo the deletion of a record.
* return: int
* recv(in): Recovery structure
*/
int
catalog_rv_delete_redo (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
PGSLOTID slot_id;
catalog_clear_hash_table (thread_p);
slot_id = recv_p->offset;
(void) spage_delete (thread_p, recv_p->pgptr, slot_id);
pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
return NO_ERROR;
}
/*
* catalog_get_cardinality () - gets the cardinality of an index using
* class OID, class DISK_REPR, index BTID
* and partial key count
* return: NO_ERROR, or error code
* thread_p(in) : thread context
* class_oid(in): class OID
* disk_repr_p(in): class DISK_REPR
* btid(in): index BTID
* key_pos(in) : partial key (i-th column from index definition)
* cardinality(out): number of distinct values
*
*/
int
catalog_get_cardinality (THREAD_ENTRY * thread_p, OID * class_oid, DISK_REPR * rep, BTID * btid, const int key_pos,
int *cardinality)
{
int idx_cnt;
int att_cnt;
int error = NO_ERROR;
BTREE_STATS *p_stat_info = NULL;
BTREE_STATS *curr_stat_info = NULL;
DISK_ATTR *disk_attr_p = NULL;
BTID curr_bitd;
bool is_btree_found;
DISK_REPR *disk_repr_p = NULL;
bool free_disk_rep = false;
int key_size;
OID *partitions = NULL;
int count = 0;
CLS_INFO *subcls_info = NULL;
DISK_REPR *subcls_disk_rep = NULL;
OR_CLASSREP *subcls_rep = NULL;
OR_CLASSREP *cls_rep = NULL;
DISK_ATTR *subcls_attr = NULL;
const BTREE_STATS *subcls_stats = NULL;
REPR_ID subcls_repr_id;
int subcls_idx_cache;
int idx_cache;
int i;
bool is_subcls_attr_found = false;
bool free_cls_rep = false;
CATALOG_ACCESS_INFO catalog_access_info = CATALOG_ACCESS_INFO_INITIALIZER;
OID dir_oid;
assert (class_oid != NULL && btid != NULL && cardinality != NULL);
*cardinality = -1;
if (rep != NULL)
{
disk_repr_p = rep;
}
else
{
int repr_id;
error = catalog_get_dir_oid_from_cache (thread_p, class_oid, &dir_oid);
if (error != NO_ERROR)
{
goto exit;
}
catalog_access_info.class_oid = class_oid;
catalog_access_info.dir_oid = &dir_oid;
error = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, S_LOCK);
if (error != NO_ERROR)
{
goto exit;
}
/* get last representation id, if is not already known */
error = catalog_get_last_representation_id (thread_p, class_oid, &repr_id);
if (error != NO_ERROR)
{
goto exit;
}
/* get disk representation : the disk representation contains a some pre-filled BTREE statistics, but no partial
* keys info yet */
disk_repr_p = catalog_get_representation (thread_p, class_oid, repr_id, &catalog_access_info);
if (disk_repr_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UNEXPECTED, 1, "Disk representation not found.");
error = ER_UNEXPECTED;
goto exit;
}
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, NO_ERROR);
free_disk_rep = true;
}
cls_rep = heap_classrepr_get (thread_p, class_oid, NULL, NULL_REPRID, &idx_cache);
if (cls_rep == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UNEXPECTED, 1, "Class representation not found.");
error = ER_UNEXPECTED;
goto exit_cleanup;
}
free_cls_rep = true;
/* There should be only one OR_ATTRIBUTE element that contains the index; this is the element corresponding to the
* attribute which is the first key in the index */
p_stat_info = NULL;
is_btree_found = false;
/* first, search in the fixed attributes : */
for (att_cnt = 0, disk_attr_p = disk_repr_p->fixed; att_cnt < disk_repr_p->n_fixed; att_cnt++, disk_attr_p++)
{
/* search for BTID in each BTREE_STATS element from current attribute */
for (idx_cnt = 0, curr_stat_info = disk_attr_p->bt_stats; idx_cnt < disk_attr_p->n_btstats;
idx_cnt++, curr_stat_info++)
{
curr_bitd = curr_stat_info->btid;
if (BTID_IS_EQUAL (&curr_bitd, btid))
{
p_stat_info = curr_stat_info;
is_btree_found = true;
break;
}
}
if (is_btree_found)
{
break;
}
}
if (!is_btree_found)
{
assert_release (p_stat_info == NULL);
/* not found, repeat the search for variable attributes */
for (att_cnt = 0, disk_attr_p = disk_repr_p->variable; att_cnt < disk_repr_p->n_variable;
att_cnt++, disk_attr_p++)
{
/* search for BTID in each BTREE_STATS element from current attribute */
for (idx_cnt = 0, curr_stat_info = disk_attr_p->bt_stats; idx_cnt < disk_attr_p->n_btstats;
idx_cnt++, curr_stat_info++)
{
curr_bitd = curr_stat_info->btid;
if (BTID_IS_EQUAL (&curr_bitd, btid))
{
p_stat_info = curr_stat_info;
is_btree_found = true;
break;
}
}
if (is_btree_found)
{
break;
}
}
}
if (!is_btree_found)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UNEXPECTED, 1, "B-Tree not found.");
error = ER_UNEXPECTED;
goto exit_cleanup;
}
assert_release (p_stat_info != NULL);
assert_release (BTID_IS_EQUAL (&(p_stat_info->btid), btid));
assert_release (p_stat_info->pkeys_size > 0);
assert_release (p_stat_info->pkeys_size <= BTREE_STATS_PKEYS_NUM);
/* since btree_get_stats is too slow, use the old statistics. the user must previously execute 'update statistics on
* class_name', in order to get updated statistics. */
if (TP_DOMAIN_TYPE (p_stat_info->key_type) == DB_TYPE_MIDXKEY)
{
key_size = tp_domain_size (p_stat_info->key_type->setdomain);
if (p_stat_info->dedup_idx > 0)
{
// Avoid providing information about columns added with deduplicate options.
key_size--;
}
}
else
{
key_size = 1;
}
if (key_pos >= key_size || key_pos < 0)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_QPROC_FUNCTION_ARG_ERROR, 1, "index_cardinality()");
goto exit_cleanup;
}
error = partition_get_partition_oids (thread_p, class_oid, &partitions, &count);
if (error != NO_ERROR)
{
goto exit_cleanup;
}
if (count == 0)
{
*cardinality = p_stat_info->keys;
}
else
{
*cardinality = 0;
for (i = 0; i < count; i++)
{
/* clean subclass loaded in previous iteration */
if (subcls_info != NULL)
{
catalog_free_class_info_and_init (subcls_info);
}
if (subcls_disk_rep != NULL)
{
catalog_free_representation_and_init (subcls_disk_rep);
}
if (subcls_rep != NULL)
{
heap_classrepr_free_and_init (subcls_rep, &subcls_idx_cache);
}
if (catalog_get_dir_oid_from_cache (thread_p, &partitions[i], &dir_oid) != NO_ERROR)
{
continue;
}
catalog_access_info.class_oid = &partitions[i];
catalog_access_info.dir_oid = &dir_oid;
error = catalog_start_access_with_dir_oid (thread_p, &catalog_access_info, S_LOCK);
if (error != NO_ERROR)
{
goto exit_cleanup;
}
/* load new subclass */
subcls_info = catalog_get_class_info (thread_p, &partitions[i], &catalog_access_info);
if (subcls_info == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit_cleanup;
}
/* get disk repr for subclass */
error = catalog_get_last_representation_id (thread_p, &partitions[i], &subcls_repr_id);
if (error != NO_ERROR)
{
goto exit_cleanup;
}
subcls_disk_rep = catalog_get_representation (thread_p, &partitions[i], subcls_repr_id, &catalog_access_info);
if (subcls_disk_rep == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit_cleanup;
}
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, NO_ERROR);
subcls_rep = heap_classrepr_get (thread_p, &partitions[i], NULL, NULL_REPRID, &subcls_idx_cache);
if (subcls_rep == NULL)
{
error = ER_FAILED;
goto exit_cleanup;
}
is_subcls_attr_found = false;
for (att_cnt = 0, subcls_attr = subcls_disk_rep->fixed; att_cnt < subcls_disk_rep->n_fixed;
att_cnt++, subcls_attr++)
{
if (disk_attr_p->id == subcls_attr->id)
{
is_subcls_attr_found = true;
break;
}
}
if (!is_subcls_attr_found)
{
for (att_cnt = 0, subcls_attr = subcls_disk_rep->variable; att_cnt < subcls_disk_rep->n_variable;
att_cnt++, subcls_attr++)
{
if (disk_attr_p->id == subcls_attr->id)
{
is_subcls_attr_found = true;
break;
}
}
}
if (!is_subcls_attr_found)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UNEXPECTED, 1, "Attribute of subclass not found.");
error = ER_UNEXPECTED;
goto exit_cleanup;
}
subcls_stats = stats_find_inherited_index_stats (cls_rep, subcls_rep, subcls_attr, btid);
if (subcls_stats == NULL)
{
error = ER_FAILED;
goto exit_cleanup;
}
*cardinality = *cardinality + subcls_stats->keys;
}
}
exit_cleanup:
if (free_disk_rep)
{
catalog_free_representation_and_init (disk_repr_p);
}
if (free_cls_rep)
{
heap_classrepr_free_and_init (cls_rep, &idx_cache);
}
if (subcls_info != NULL)
{
catalog_free_class_info_and_init (subcls_info);
}
if (subcls_disk_rep != NULL)
{
catalog_free_representation_and_init (subcls_disk_rep);
}
if (subcls_rep != NULL)
{
heap_classrepr_free_and_init (subcls_rep, &subcls_idx_cache);
}
if (partitions != NULL)
{
db_private_free (thread_p, partitions);
}
exit:
catalog_end_access_with_dir_oid (thread_p, &catalog_access_info, error);
return error;
}
/*
* catalog_get_cardinality_by_name () - gets the cardinality of an index using
* its name and partial key count
* return: NO_ERROR, or error code
* thread_p(in) : thread context
* class_name(in): name of class
* index_name(in): name of index
* key_pos(in) : partial key (i-th column from index definition)
* cardinality(out): number of distinct values
*
*/
int
catalog_get_cardinality_by_name (THREAD_ENTRY * thread_p, const char *class_name, const char *index_name,
const int key_pos, int *cardinality)
{
int error = NO_ERROR;
BTID found_btid;
BTID curr_bitd;
OID class_oid;
char cls_lower[DB_MAX_IDENTIFIER_LENGTH] = { 0 };
LC_FIND_CLASSNAME status;
BTID_SET_NULL (&found_btid);
BTID_SET_NULL (&curr_bitd);
assert (class_name != NULL);
assert (index_name != NULL);
assert (cardinality != NULL);
*cardinality = -1;
/* get class OID from class name */
intl_identifier_lower (class_name, cls_lower);
status = xlocator_find_class_oid (thread_p, cls_lower, &class_oid, NULL_LOCK);
if (status == LC_CLASSNAME_ERROR)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_LC_UNKNOWN_CLASSNAME, 1, cls_lower);
return ER_FAILED;
}
if (status == LC_CLASSNAME_DELETED || OID_ISNULL (&class_oid))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_LC_UNKNOWN_CLASSNAME, 1, class_name);
goto exit;
}
if (lock_object (thread_p, &class_oid, oid_Root_class_oid, SCH_S_LOCK, LK_UNCOND_LOCK) != LK_GRANTED)
{
error = ER_FAILED;
goto exit;
}
error = heap_get_btid_from_index_name (thread_p, &class_oid, index_name, &found_btid);
if (error != NO_ERROR)
{
goto exit;
}
if (BTID_IS_NULL (&found_btid))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INDEX_NOT_FOUND, 0);
goto exit;
}
return catalog_get_cardinality (thread_p, &class_oid, NULL, &found_btid, key_pos, cardinality);
exit:
return error;
}
/*
* catalog_rv_delete_undo () - Undo the deletion of a record.
* return: int
* recv(in): Recovery structure
*/
int
catalog_rv_delete_undo (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
catalog_clear_hash_table (thread_p);
return catalog_rv_insert_redo (thread_p, recv_p);
}
/*
* catalog_rv_update () - Recover an update either for undo or redo
* return: int
* recv(in): Recovery structure
*
* Note: Recover an update to a record in a slotted page
*/
int
catalog_rv_update (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
PGSLOTID slot_id;
RECDES record;
catalog_clear_hash_table (thread_p);
slot_id = recv_p->offset;
recdes_set_data_area (&record, (char *) (recv_p->data) + sizeof (record.type), recv_p->length - sizeof (record.type));
record.length = record.area_size;
record.type = *(INT16 *) (recv_p->data);
if (spage_update (thread_p, recv_p->pgptr, slot_id, &record) != SP_SUCCESS)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
return NO_ERROR;
}
/*
* catalog_rv_ovf_page_logical_insert_undo () - Undo new overflow page creation.
* return: int
* recv(in): Recovery structure
*/
int
catalog_rv_ovf_page_logical_insert_undo (THREAD_ENTRY * thread_p, LOG_RCV * recv_p)
{
VPID *vpid_p;
catalog_clear_hash_table (thread_p);
vpid_p = (VPID *) recv_p->data;
return file_dealloc (thread_p, &catalog_Id.vfid, vpid_p, FILE_CATALOG);
}
/*
* catalog_get_dir_oid_from_cache () - Get directory OID from cache or class record
* return: error status
* class_id_p(in): Class identifier
* dir_oid_p(out): directory OID
*
*/
int
catalog_get_dir_oid_from_cache (THREAD_ENTRY * thread_p, const OID * class_id_p, OID * dir_oid_p)
{
CATALOG_ENTRY *catalog_value_p;
CATALOG_KEY catalog_key;
HEAP_SCANCACHE scan_cache;
RECDES record = { -1, -1, REC_HOME, NULL };
int error = NO_ERROR;
assert (dir_oid_p != NULL);
OID_SET_NULL (dir_oid_p);
catalog_key.page_id = class_id_p->pageid;
catalog_key.volid = class_id_p->volid;
catalog_key.slot_id = class_id_p->slotid;
catalog_key.repr_id = CATALOG_DIR_REPR_KEY;
catalog_value_p = catalog_Hashmap.find (thread_p, catalog_key);
if (catalog_value_p != NULL)
{
/* entry already exists */
dir_oid_p->volid = catalog_value_p->key.r_page_id.volid;
dir_oid_p->pageid = catalog_value_p->key.r_page_id.pageid;
dir_oid_p->slotid = catalog_value_p->key.r_slot_id;
/* end transaction */
catalog_Hashmap.unlock (thread_p, catalog_value_p);
return NO_ERROR;
}
/* not found in cache, get it from class record */
heap_scancache_quick_start_root_hfid (thread_p, &scan_cache);
if (heap_get_class_record (thread_p, class_id_p, &record, &scan_cache, PEEK) == S_SUCCESS)
{
or_class_rep_dir (&record, dir_oid_p);
}
else
{
error = er_errid ();
if (error == NO_ERROR)
{
error = ER_FAILED;
}
heap_scancache_end (thread_p, &scan_cache);
return error;
}
heap_scancache_end (thread_p, &scan_cache);
if (OID_ISNULL (dir_oid_p))
{
/* directory not created yet, don't cache NULL OID */
return NO_ERROR;
}
catalog_key.r_page_id.pageid = dir_oid_p->pageid;
catalog_key.r_page_id.volid = dir_oid_p->volid;
catalog_key.r_slot_id = dir_oid_p->slotid;
/* insert value */
(void) catalog_Hashmap.find_or_insert (thread_p, catalog_key, catalog_value_p);
if (catalog_value_p != NULL)
{
catalog_Hashmap.unlock (thread_p, catalog_value_p);
return NO_ERROR;
}
else
{
assert (false);
return ER_FAILED;
}
return NO_ERROR;
}
/*
* catalog_start_access_with_dir_oid () - starts an access on catalog using directory OID for locking purpose
* return: error code
* catalog_access_info(in/out): catalog access helper structure
* lock_mode(in): should be X_LOCK for update on catalog and S_LOCK for read
*/
int
catalog_start_access_with_dir_oid (THREAD_ENTRY * thread_p, CATALOG_ACCESS_INFO * catalog_access_info, LOCK lock_mode)
{
#if defined (SERVER_MODE)
LOCK current_lock;
#endif /* SERVER_MODE */
int error_code = NO_ERROR;
int lk_grant_code;
OID virtual_class_dir_oid;
assert (catalog_access_info != NULL);
assert (catalog_access_info->access_started == false);
if (catalog_access_info->access_started == true)
{
(void) catalog_end_access_with_dir_oid (thread_p, catalog_access_info, NO_ERROR);
}
if (BO_IS_SERVER_RESTARTED () == false || OID_ISNULL (catalog_access_info->dir_oid))
{
/* server not started or class dir not created yet: do not use locking */
return NO_ERROR;
}
catalog_access_info->need_unlock = false;
catalog_access_info->is_update = (lock_mode == X_LOCK) ? true : false;
if (lock_mode == X_LOCK)
{
log_sysop_start (thread_p);
#if !defined (NDEBUG)
catalog_access_info->is_systemop_started = true;
#endif
}
OID_GET_VIRTUAL_CLASS_OF_DIR_OID (catalog_access_info->class_oid, &virtual_class_dir_oid);
#if defined (SERVER_MODE)
current_lock = lock_get_object_lock (catalog_access_info->dir_oid, &virtual_class_dir_oid);
if (current_lock != NULL_LOCK)
{
assert (false);
if (lock_mode == X_LOCK)
{
log_sysop_abort (thread_p);
}
error_code = ER_FAILED;
return error_code;
}
#endif /* SERVER_MODE */
/* before go further, we should get the lock to disable updating schema */
lk_grant_code = lock_object (thread_p, catalog_access_info->dir_oid, &virtual_class_dir_oid, lock_mode,
LK_UNCOND_LOCK);
if (lk_grant_code != LK_GRANTED)
{
/* deadlocked, timedout or interrupted */
assert (lk_grant_code == LK_NOTGRANTED_DUE_ABORTED || lk_grant_code == LK_NOTGRANTED_DUE_TIMEOUT
|| lk_grant_code == LK_NOTGRANTED_DUE_ERROR);
if (catalog_access_info->class_name == NULL)
{
if (heap_get_class_name (thread_p, catalog_access_info->class_oid, &catalog_access_info->class_name) !=
NO_ERROR)
{
/* ignore */
er_clear ();
}
else if (catalog_access_info->class_name != NULL)
{
catalog_access_info->need_free_class_name = true;
}
}
if (lock_mode == X_LOCK)
{
log_sysop_abort (thread_p);
}
#if !defined (NDEBUG)
catalog_access_info->is_systemop_started = false;
#endif
error_code = ER_UPDATE_STAT_CANNOT_GET_LOCK;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1,
catalog_access_info->class_name ? catalog_access_info->class_name : "*UNKNOWN-CLASS*");
goto error;
}
catalog_access_info->need_unlock = true;
catalog_access_info->access_started = true;
return error_code;
error:
return error_code;
}
/*
* catalog_end_access_with_dir_oid () - ends access on catalog using directory OID
* return: error code
* catalog_access_info(in/out): catalog access helper structure
* error(in): error code
*/
int
catalog_end_access_with_dir_oid (THREAD_ENTRY * thread_p, CATALOG_ACCESS_INFO * catalog_access_info, int error)
{
OID virtual_class_dir_oid;
LOCK current_lock;
assert (catalog_access_info != NULL);
if (catalog_access_info->access_started == false)
{
assert (catalog_access_info->need_unlock == false);
#if !defined (NDEBUG)
assert (catalog_access_info->is_systemop_started == false);
#endif
return NO_ERROR;
}
assert (BO_IS_SERVER_RESTARTED () == true);
if (catalog_access_info->is_update == true)
{
if (error != NO_ERROR)
{
log_sysop_abort (thread_p);
}
else
{
#if defined (SERVER_MODE)
current_lock = lock_get_object_lock (catalog_access_info->class_oid, oid_Root_class_oid);
if (current_lock == SCH_M_LOCK)
{
/* when class was created or schema was changed commit the statistics changes along with schema change */
log_sysop_attach_to_outer (thread_p);
}
else
{
/* this case applies with UPDATE STATISTICS */
log_sysop_commit (thread_p);
}
#else
log_sysop_attach_to_outer (thread_p);
#endif /* SERVER_MODE */
}
#if !defined (NDEBUG)
catalog_access_info->is_systemop_started = false;
#endif
}
#if !defined (NDEBUG)
assert (catalog_access_info->is_systemop_started == false);
#endif
OID_GET_VIRTUAL_CLASS_OF_DIR_OID (catalog_access_info->class_oid, &virtual_class_dir_oid);
if (catalog_access_info->need_unlock == true)
{
current_lock = catalog_access_info->is_update ? X_LOCK : S_LOCK;
lock_unlock_object_donot_move_to_non2pl (thread_p, catalog_access_info->dir_oid, &virtual_class_dir_oid,
current_lock);
}
catalog_access_info->access_started = false;
catalog_access_info->is_update = false;
catalog_access_info->need_unlock = false;
if (catalog_access_info->class_name != NULL && catalog_access_info->need_free_class_name == true)
{
free_and_init (catalog_access_info->class_name);
}
catalog_access_info->need_free_class_name = false;
return NO_ERROR;
}