File slotted_page.c¶
File List > cubrid > src > storage > slotted_page.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.
*
*/
/*
* slotted_page.c - slotted page management module (at the server)
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "slotted_page.h"
#include "storage_common.h"
#include "memory_alloc.h"
#include "error_manager.h"
#include "system_parameter.h"
#include "memory_hash.h"
#include "object_representation.h"
#include "page_buffer.h"
#include "porting_inline.hpp"
#include "log_manager.h"
#include "critical_section.h"
#include "lock_free.h"
#include "mvcc.h"
#if defined(SERVER_MODE)
#include "connection_error.h"
#endif /* SERVER_MODE */
#include "dbtype.h"
#include "thread_entry.hpp"
#include "thread_lockfree_hash_map.hpp"
#include "thread_manager.hpp" // for thread_get_thread_entry_info
// 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 SPAGE_SEARCH_NEXT 1
#define SPAGE_SEARCH_PREV -1
static PGLENGTH spage_User_page_size;
#define SPAGE_DB_PAGESIZE \
(spage_User_page_size != 0 ? assert (spage_User_page_size == DB_PAGESIZE), spage_User_page_size : DB_PAGESIZE)
#define SPAGE_VERIFY_HEADER(sphdr) \
do { \
assert ((sphdr) != NULL); \
assert ((sphdr)->total_free >= 0); \
assert ((sphdr)->cont_free >= 0); \
assert ((sphdr)->cont_free <= (sphdr)->total_free); \
assert ((sphdr)->offset_to_free_area < SPAGE_DB_PAGESIZE); \
assert ((sphdr)->num_records >= 0); \
assert ((sphdr)->num_slots >= 0); \
assert ((sphdr)->num_records <= (sphdr)->num_slots); \
} while (0)
enum
{
SPAGE_EMPTY_OFFSET = 0 /* uninitialized offset */
};
typedef struct spage_save_entry SPAGE_SAVE_ENTRY;
typedef struct spage_save_head SPAGE_SAVE_HEAD;
struct spage_save_entry
{ /* A hash entry to save space for future undos */
TRANID tranid; /* Transaction identifier */
int saved; /* Amount of space saved */
SPAGE_SAVE_ENTRY *next; /* Next save */
SPAGE_SAVE_ENTRY *prev; /* Previous save */
SPAGE_SAVE_ENTRY *tran_next_save; /* Next save stored by the same transaction */
SPAGE_SAVE_HEAD *head; /* Head of the save list */
};
struct spage_save_head
{ /* Head of a saving space */
SPAGE_SAVE_HEAD *rstack; /* Pointer for retired stack of freelist */
SPAGE_SAVE_HEAD *next; /* Next entry in hash bucket */
pthread_mutex_t mutex; /* Mutex protecting "total_saved" and "first" */
UINT64 del_id; /* delete transaction ID (for lock free) */
VPID vpid; /* Page and volume where the space is saved */
int total_saved; /* Total saved space by all transactions */
SPAGE_SAVE_ENTRY *first; /* First saving space entry */
// *INDENT-OFF*
spage_save_head ();
~spage_save_head ();
// *INDENT-ON*
};
#define SPAGE_OVERFLOW(offset) ((int) (offset) > SPAGE_DB_PAGESIZE)
/*
* Savings hash table
*/
static void *spage_save_head_alloc (void);
static int spage_save_head_free (void *entry_p);
static int spage_save_head_init (void *entry_p);
static int spage_save_head_uninit (void *entry_p);
static LF_ENTRY_DESCRIPTOR spage_Saving_entry_descriptor = {
/* signature of SPAGE_SAVE_HEAD */
offsetof (SPAGE_SAVE_HEAD, rstack),
offsetof (SPAGE_SAVE_HEAD, next),
offsetof (SPAGE_SAVE_HEAD, del_id),
offsetof (SPAGE_SAVE_HEAD, vpid),
offsetof (SPAGE_SAVE_HEAD, mutex),
/* using mutex? */
LF_EM_USING_MUTEX,
LF_ENTRY_DESCRIPTOR_MAX_ALLOC,
/* function callbacks */
spage_save_head_alloc,
spage_save_head_free,
spage_save_head_init,
spage_save_head_uninit,
lf_callback_vpid_copy,
lf_callback_vpid_compare,
lf_callback_vpid_hash,
NULL /* no inserts */
};
// *INDENT-OFF*
using spage_saving_hashmap_type = cubthread::lockfree_hashmap<VPID, spage_save_head>;
// *INDENT-ON*
static spage_saving_hashmap_type spage_Saving_hashmap;
/* context for slotted page header scan */
typedef struct spage_header_context SPAGE_HEADER_CONTEXT;
struct spage_header_context
{
VPID vpid;
SPAGE_HEADER header;
};
/* context for slotted page slots scan */
typedef struct spage_slots_context SPAGE_SLOTS_CONTEXT;
struct spage_slots_context
{
VPID vpid; /* vpid of specified page */
PAGE_PTR pgptr; /* The page load by pgbuf_fix */
SPAGE_SLOT *slot; /* Iterator about slot in the page */
};
static int spage_save_space (THREAD_ENTRY * thread_p, SPAGE_HEADER * sphdr, PAGE_PTR pgptr, int save);
static int spage_get_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p,
int *other_saved_spaces);
#if defined (ENABLE_UNUSED_FUNCTION)
static int spage_get_saved_spaces_by_other_trans (THREAD_ENTRY * thread_p, SPAGE_HEADER * sphdr, PAGE_PTR pgptr);
#endif /* ENABLE_UNUSED_FUNCTION */
static int spage_get_total_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p);
static void spage_dump_saved_spaces_by_other_trans (THREAD_ENTRY * thread_p, FILE * fp, VPID * vpid);
static int spage_compare_slot_offset (const void *arg1, const void *arg2);
static bool spage_is_slotted_page_type (PAGE_TYPE ptype);
static int spage_check_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space);
static void spage_set_slot (SPAGE_SLOT * slot_p, int offset, int length, INT16 type);
static int spage_find_empty_slot (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, int length, INT16 type, SPAGE_SLOT ** sptr,
int *space, PGSLOTID * slotid);
static int spage_find_empty_slot_at (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, PGSLOTID slotid, int length, INT16 type,
SPAGE_SLOT ** sptr);
static void spage_shift_slot_up (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p);
static void spage_shift_slot_down (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p);
static int spage_add_new_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
int *out_space_p);
static int spage_take_slot_in_use (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
PGSLOTID slot_id, SPAGE_SLOT * slot_p, int *out_space_p);
static int spage_check_record_for_insert (RECDES * record_descriptor_p);
static int spage_insert_data (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, RECDES * recdes, void *slotptr);
static bool spage_is_record_located_at_end (SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p);
static void spage_reduce_a_slot (PAGE_PTR page_p);
static int spage_check_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id,
int record_descriptor_length, SPAGE_SLOT ** out_slot_p, int *out_space_p,
int *out_old_waste_p, int *out_new_waste_p);
#if defined (ENABLE_UNUSED_FUNCTION)
static int spage_check_mvcc_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id,
int mvcc_delete_record_length, int mvcc_insert_record_length);
#endif /* ENABLE_UNUSED_FUNCTION */
static int spage_update_record_in_place (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p,
const RECDES * record_descriptor_p, int space);
static int spage_update_record_after_compact (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
SPAGE_SLOT * slot_p, const RECDES * record_descriptor_p, int space,
int old_waste, int new_waste);
static SCAN_CODE spage_search_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p,
int is_peeking, int direction, bool skip_empty);
static const char *spage_record_type_string (INT16 record_type);
static void spage_dump_header (FILE * fp, const SPAGE_HEADER * sphdr);
static void spage_dump_header_to_string (char *buffer, int size, const SPAGE_HEADER * page_header_p);
static void spage_dump_slots (FILE * fp, const SPAGE_SLOT * sptr, PGNSLOTS nslots, unsigned short alignment);
static void spage_dump_record (FILE * Fp, PAGE_PTR page_p, PGSLOTID slot_id, SPAGE_SLOT * slot_p);
static INLINE bool spage_is_unknown_slot (PGSLOTID slotid, SPAGE_HEADER * sphdr, SPAGE_SLOT * sptr)
__attribute__ ((ALWAYS_INLINE));
static INLINE SPAGE_SLOT *spage_find_slot (PAGE_PTR pgptr, SPAGE_HEADER * sphdr, PGSLOTID slotid,
bool is_unknown_slot_check) __attribute__ ((ALWAYS_INLINE));
static INLINE int spage_find_slot_for_insert (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, RECDES * recdes,
PGSLOTID * slotid, void **slotptr, int *used_space)
__attribute__ ((ALWAYS_INLINE));
static SCAN_CODE spage_get_record_data (PAGE_PTR pgptr, SPAGE_SLOT * sptr, RECDES * recdes, bool ispeeking);
static bool spage_has_enough_total_space (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, SPAGE_HEADER * sphdr, int space);
static bool spage_has_enough_contiguous_space (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, SPAGE_HEADER * sphdr,
int space);
#if defined (ENABLE_UNUSED_FUNCTION)
static int spage_put_helper (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, PGSLOTID slotid, int offset,
const RECDES * recdes, bool is_append);
#endif /* ENABLE_UNUSED_FUNCTION */
static void spage_add_contiguous_free_space (PAGE_PTR pgptr, int space);
#if defined (ENABLE_UNUSED_FUNCTION)
static void spage_reduce_contiguous_free_space (PAGE_PTR pgptr, int space);
#endif /* ENABLE_UNUSED_FUNCTION */
static INLINE void spage_verify_header (PAGE_PTR page_p) __attribute__ ((ALWAYS_INLINE));
// *INDENT-OFF*
spage_save_head::spage_save_head ()
{
pthread_mutex_init (&mutex, NULL);
}
spage_save_head::~spage_save_head ()
{
pthread_mutex_destroy (&mutex);
}
// *INDENT-ON*
/*
* spage_save_head_alloc () - callback for allocation of a SPAGE_SAVE_HEAD
* object
* returns: pointer to new SPAGE_SAVE_HEAD or NULL on error
*/
static void *
spage_save_head_alloc (void)
{
SPAGE_SAVE_HEAD *entry_p;
entry_p = (SPAGE_SAVE_HEAD *) malloc (sizeof (SPAGE_SAVE_HEAD));
if (entry_p != NULL)
{
pthread_mutex_init (&entry_p->mutex, NULL);
}
return (void *) entry_p;
}
/*
* spage_save_head_free () - callback for deallocation of a SPAGE_SAVE_HEAD
* returns: error code or NO_ERROR
* entry_p(in): entry to deallocate
*/
static int
spage_save_head_free (void *entry_p)
{
free (entry_p);
return NO_ERROR;
}
/*
* spage_save_head_init () - initialization callback for SPAGE_SAVE_HEAD
* returns: error code or NO_ERROR
* entry_p(in): entry to initialize
*/
static int
spage_save_head_init (void *entry_p)
{
SPAGE_SAVE_HEAD *head_p = (SPAGE_SAVE_HEAD *) entry_p;
if (head_p != NULL)
{
head_p->vpid.pageid = NULL_PAGEID;
head_p->vpid.volid = NULL_PAGEID;
head_p->first = NULL;
head_p->total_saved = 0;
}
return NO_ERROR;
}
/*
* spage_save_head_uninit () - uninitialization callback for SPAGE_SAVE_HEAD
* returns: error code or NO_ERROR
* entry_p(in): entry to uninitializa
*/
static int
spage_save_head_uninit (void *entry_p)
{
SPAGE_SAVE_HEAD *head_p = (SPAGE_SAVE_HEAD *) entry_p;
SPAGE_SAVE_ENTRY *list, *next;
if (head_p != NULL)
{
list = head_p->first;
while (list)
{
next = list->next;
free (list);
list = next;
}
}
return NO_ERROR;
}
/*
* spage_verify_header () -
* return:
*
* page_p(in): Pointer to slotted page
*/
STATIC_INLINE void
spage_verify_header (PAGE_PTR page_p)
{
char header_info[1024];
SPAGE_HEADER *sphdr;
VPID vpid;
sphdr = (SPAGE_HEADER *) page_p;
if (sphdr == NULL || sphdr->total_free < 0 || sphdr->cont_free < 0 || sphdr->cont_free > sphdr->total_free
|| sphdr->offset_to_free_area >= SPAGE_DB_PAGESIZE
|| (PTR_ALIGN (page_p + sphdr->offset_to_free_area, sphdr->alignment) != page_p + sphdr->offset_to_free_area)
|| sphdr->num_records < 0 || sphdr->num_slots < 0 || sphdr->num_records > sphdr->num_slots)
{
spage_dump_header_to_string (header_info, sizeof (header_info), sphdr);
pgbuf_get_vpid (page_p, &vpid);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_INVALID_HEADER, 3, vpid.pageid,
fileio_get_volume_label (vpid.volid, PEEK), header_info);
assert (false);
}
}
/*
* spage_is_valid_anchor_type ()
* return: whether the given number represents a valid anchor type constant
* for a slotted page
*
* anchor_type(in): the anchor type constant
*/
bool
spage_is_valid_anchor_type (const INT16 anchor_type)
{
assert (ANCHORED <= anchor_type && anchor_type <= UNANCHORED_KEEP_SEQUENCE);
return (anchor_type == ANCHORED || anchor_type == ANCHORED_DONT_REUSE_SLOTS || anchor_type == UNANCHORED_ANY_SEQUENCE
|| anchor_type == UNANCHORED_KEEP_SEQUENCE);
}
/*
* spage_free_saved_spaces () - Release the savings of transaction
* return: void
*
* first_save_entry(in): first save entry to be released
*
* Note: This function could be called once a transaction has finished.
* This is optional, it does not need to be done.
*/
void
spage_free_saved_spaces (THREAD_ENTRY * thread_p, void *first_save_entry)
{
SPAGE_SAVE_ENTRY *entry, *current;
SPAGE_SAVE_HEAD *head;
int rv;
assert (first_save_entry != NULL);
entry = (SPAGE_SAVE_ENTRY *) first_save_entry;
while (entry != NULL)
{
/* we are about to access a lock-free pointer; make sure it's retained until we're done with it */
spage_Saving_hashmap.start_tran (thread_p);
current = entry;
head = entry->head;
entry = entry->tran_next_save;
assert (current->tranid == logtb_find_current_tranid (thread_p));
/* since we only remove hash entries when they are empty AND each transaction uses only one thread (and thus
* cannot call this function concurrently), we can assume that the entry we just fetched is valid and in the hash
* table */
rv = pthread_mutex_lock (&head->mutex);
/* mutex acquired, no need for lock-free transaction */
spage_Saving_hashmap.end_tran (thread_p);
/* Delete the current node from save entry list */
if (current->prev == NULL)
{
head->first = current->next;
/* There is no more entry. Delete save head and go to next entry */
if (head->first == NULL)
{
int success = 0;
if (!spage_Saving_hashmap.erase_locked (thread_p, head->vpid, head))
{
/* we don't have clear operations on this hash table, this shouldn't happen */
pthread_mutex_unlock (&head->mutex);
assert_release (false);
return;
}
free_and_init (current);
continue;
}
}
else
{
current->prev->next = current->next;
}
if (current->next != NULL)
{
current->next->prev = current->prev;
}
/* If there is saved space, decrease total saved space held by head */
if (current->saved > 0)
{
head->total_saved -= current->saved;
/* Total saved space should be 0 or positive */
if (head->total_saved < 0)
{
/* defense code */
head->total_saved = 0;
}
}
free_and_init (current);
pthread_mutex_unlock (&head->mutex);
}
}
/*
* spage_save_space () - Save some space for recovery (undo) purposes
* return:
*
* page_header_p(in): Pointer to header of slotted page
* page_p(in): Pointer to slotted page
* space(in): Amount of space to save
*
* Note: The current transaction saving information is kept on the page only if
* the page is not held by the other transaction. When a transaction
* has occupied the page, the following transactions which try to save
* some space to the page should be make a hash entry.
*
* The head of the save entries holds the total saved space of its list
* except the saved space stored by the page header.
*
*/
static int
spage_save_space (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p, int space)
{
SPAGE_SAVE_HEAD *head_p;
SPAGE_SAVE_ENTRY *entry_p;
VPID *vpid_p;
TRANID tranid;
LOG_TDES *tdes;
bool found;
assert (page_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
if (space == 0 || log_is_in_crash_recovery ())
{
return NO_ERROR;
}
assert (!VACUUM_IS_THREAD_VACUUM_MASTER (thread_p));
if (VACUUM_IS_THREAD_VACUUM_WORKER (thread_p))
{
/* Vacuum workers do not rollback their heap changes and don't need to keep track of saved space. */
assert (!LOG_FIND_CURRENT_TDES (thread_p)->is_under_sysop () || space < 0);
return NO_ERROR;
}
tranid = logtb_find_current_tranid (thread_p);
/*
* increase saved space when the transaction is active.
*/
if (space < 0 || !logtb_is_active (thread_p, tranid))
{
return NO_ERROR;
}
vpid_p = pgbuf_get_vpid_ptr (page_p);
/* retrieve a hash entry for specified VPID */
(void) spage_Saving_hashmap.find_or_insert (thread_p, *vpid_p, head_p);
if (head_p == NULL)
{
assert (false);
return ER_FAILED;
}
if (head_p->first == NULL)
{
entry_p = (SPAGE_SAVE_ENTRY *) malloc (sizeof (*entry_p));
if (entry_p == NULL)
{
pthread_mutex_unlock (&head_p->mutex);
return ER_FAILED;
}
/*
* Form the head and the first entry with information of the page
* header, modify the header with current transaction saving, and
* add first entry into hash
*/
head_p->total_saved = space;
head_p->first = entry_p;
entry_p->tranid = tranid;
entry_p->saved = space;
entry_p->next = NULL;
entry_p->prev = NULL;
entry_p->head = head_p;
/*
* Add this entry to the save entry list of this transaction.
* It will be used to release the save entries when the transaction
* is completed.
*/
tdes = LOG_FIND_TDES (LOG_FIND_THREAD_TRAN_INDEX (thread_p));
if (tdes != NULL)
{
entry_p->tran_next_save = ((SPAGE_SAVE_ENTRY *) (tdes->first_save_entry));
tdes->first_save_entry = (void *) entry_p;
}
else
{
entry_p->tran_next_save = NULL;
}
/* all done, unlock entry */
pthread_mutex_unlock (&head_p->mutex);
return NO_ERROR;
}
/*
* Check if the current transaction is in the list. If it is, adjust the
* total saved space on the head entry. otherwise, create a new entry.
*/
found = false;
for (entry_p = head_p->first; entry_p != NULL; entry_p = entry_p->next)
{
if (tranid == entry_p->tranid)
{
found = true;
break;
}
}
if (found == true)
{
head_p->total_saved += space;
entry_p->saved += space;
}
else
{
/* Current transaction is not in the list */
/* Need to allocate an entry */
entry_p = (SPAGE_SAVE_ENTRY *) malloc (sizeof (*entry_p));
if (entry_p == NULL)
{
pthread_mutex_unlock (&head_p->mutex);
return ER_FAILED;
}
head_p->total_saved += space;
entry_p->tranid = tranid;
entry_p->saved = space;
entry_p->head = head_p;
entry_p->prev = NULL;
entry_p->next = head_p->first;
if (head_p->first != NULL)
{
head_p->first->prev = entry_p;
}
head_p->first = entry_p;
/*
* Add this entry to the save entry list of this transaction.
* It will be used to release the save entries when the transaction
* is completed.
*/
tdes = LOG_FIND_TDES (LOG_FIND_THREAD_TRAN_INDEX (thread_p));
if (tdes != NULL)
{
entry_p->tran_next_save = (SPAGE_SAVE_ENTRY *) (tdes->first_save_entry);
tdes->first_save_entry = (void *) entry_p;
}
else
{
assert (false);
entry_p->tran_next_save = NULL;
}
}
assert (head_p->total_saved >= 0);
assert (entry_p->saved >= 0);
pthread_mutex_unlock (&head_p->mutex);
return NO_ERROR;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* spage_get_saved_spaces_by_other_trans () - Find the total saved space by
* other transactions
* return:
*
* page_header_p(in): Pointer to header of slotted page
* page_p(in): Pointer to slotted page
*/
static int
spage_get_saved_spaces_by_other_trans (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p)
{
int saved_by_other_trans = 0;
spage_get_saved_spaces (thread_p, page_header_p, page_p, &saved_by_other_trans);
assert (saved_by_other_trans >= 0);
return saved_by_other_trans;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* spage_get_total_saved_spaces () - Find the total saved space
* return:
*
* page_header_p(in): Pointer to header of slotted page
* page_p(in): Pointer to slotted page
*/
static int
spage_get_total_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p)
{
int total_saved = 0;
int dummy;
if (page_header_p->is_saving)
{
total_saved = spage_get_saved_spaces (thread_p, page_header_p, page_p, &dummy);
}
return total_saved;
}
/*
* spage_get_saved_spaces () - Find the total saved space and the space by
other transactions
* return:
*
* page_header_p(in): Pointer to header of slotted page
* page_p(in): Pointer to slotted page
* saved_by_other_trans(in/out) : The other transaction's saved space will
* be returned
*/
static int
spage_get_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p,
int *saved_by_other_trans)
{
SPAGE_SAVE_HEAD *head_p;
SPAGE_SAVE_ENTRY *entry_p;
VPID *vpid_p;
TRANID tranid;
int total_saved, my_saved_space;
assert (page_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
/*
* If we are recovering, no other transaction should exist.
*/
if (log_is_in_crash_recovery ())
{
if (saved_by_other_trans != NULL)
{
*saved_by_other_trans = 0;
}
return 0;
}
tranid = logtb_find_current_tranid (thread_p);
vpid_p = pgbuf_get_vpid_ptr (page_p);
my_saved_space = 0;
total_saved = 0;
/*
* Get the saved space held by the head of the save entries. This is
* the aggregate value of spaces saved on all entries.
*/
head_p = spage_Saving_hashmap.find (thread_p, *vpid_p);
if (head_p != NULL)
{
entry_p = head_p->first;
while (entry_p != NULL)
{
if (entry_p->tranid == tranid)
{
my_saved_space += entry_p->saved;
break;
}
entry_p = entry_p->next;
}
total_saved += head_p->total_saved;
/* done with entry, unlock mutex */
pthread_mutex_unlock (&head_p->mutex);
}
/* Saved space should be 0 or positive. */
assert (my_saved_space >= 0);
assert (total_saved >= 0);
assert (total_saved >= my_saved_space);
if (saved_by_other_trans != NULL)
{
*saved_by_other_trans = total_saved - my_saved_space;
assert (*saved_by_other_trans >= 0);
}
return total_saved;
}
/*
* spage_dump_saved_spaces_by_other_trans () - Dump the savings associated with
* the given page that are part of
* the hash table
* return: void
*
* fp(in/out):
* vpid_p(in): Volume and page identifier
*/
static void
spage_dump_saved_spaces_by_other_trans (THREAD_ENTRY * thread_p, FILE * fp, VPID * vpid_p)
{
SPAGE_SAVE_ENTRY *entry_p;
SPAGE_SAVE_HEAD *head_p;
head_p = spage_Saving_hashmap.find (thread_p, *vpid_p);
if (head_p != NULL)
{
fprintf (fp, "Other savings of VPID = %d|%d total_saved = %d\n", head_p->vpid.volid, head_p->vpid.pageid,
head_p->total_saved);
/* Go over the linked list of entries */
entry_p = head_p->first;
while (entry_p != NULL)
{
fprintf (fp, " Tranid = %d save = %d\n", entry_p->tranid, entry_p->saved);
entry_p = entry_p->next;
}
/* unlock entry */
pthread_mutex_unlock (&head_p->mutex);
}
}
/*
* spage_boot () - Initialize the slotted page module. The save_space hash table
* is initialized
* return:
*/
void
spage_boot (THREAD_ENTRY * thread_p)
{
assert (sizeof (SPAGE_HEADER) % DOUBLE_ALIGNMENT == 0);
assert (sizeof (SPAGE_SLOT) == INT_ALIGNMENT);
spage_User_page_size = DB_PAGESIZE;
spage_Saving_hashmap.init (spage_saving_Ts, THREAD_TS_SPAGE_SAVING, 4547, 100, 100, spage_Saving_entry_descriptor);
}
/*
* spage_finalize () - Terminate the slotted page module
* return: void
*
* Note: Any memory allocated for the page space saving hash table is
* deallocated.
*/
void
spage_finalize (THREAD_ENTRY * thread_p)
{
/* destroy everything */
spage_Saving_hashmap.destroy ();
}
/*
* spage_max_record_size () - Find the maximum record length that can be stored in
* a slotted page
* return: Max length for a new record
*/
int
spage_max_record_size (void)
{
return SPAGE_DB_PAGESIZE - sizeof (SPAGE_HEADER) - sizeof (SPAGE_SLOT);
}
/*
* spage_number_of_records () - Return the total number of records in the slotted page
* return: Number of records (PGNSLOTS)
*
* page_p(in): Pointer to slotted page
*/
PGNSLOTS
spage_number_of_records (PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
return page_header_p->num_records;
}
/*
* spage_number_of_slots () - Return the total number of slots in the slotted page
* return: Number of slots (PGNSLOTS)
*
* page_p(in): Pointer to slotted page
*/
PGNSLOTS
spage_number_of_slots (PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
return page_header_p->num_slots;
}
/*
* spage_get_free_space () - Returns total free area
* return: Total free space
*
* page_p(in): Pointer to slotted page
*/
int
spage_get_free_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
int free_space;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
free_space = (page_header_p->total_free - spage_get_total_saved_spaces (thread_p, page_header_p, page_p));
if (free_space < 0)
{
free_space = 0;
}
return free_space;
}
/*
* spage_get_free_space_without_saving () - Returns total free area without saving
* return: Total free space
*
* page_p(in): Pointer to slotted page
* need_update(out): Value of need_update_best_hint
*/
int
spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool * need_update)
{
SPAGE_HEADER *page_header_p;
int free_space;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (need_update != NULL)
{
*need_update = page_header_p->need_update_best_hint;
}
free_space = page_header_p->total_free;
if (free_space < 0)
{ /* defense code */
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
free_space = 0;
}
return free_space;
}
/*
* spage_set_need_update_best_hint () - Set need_update_best_hint on slotted page header
* return: void
*
* page_p(in): Pointer to slotted page
* need_update(in): Value of need_update_best_hint
*
* NOTE: We will set this value as TRUE when we failed updating best page hints
* on heap header. See heap_stats_update function.
*/
void
spage_set_need_update_best_hint (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool need_update)
{
SPAGE_HEADER *page_header_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
page_header_p->need_update_best_hint = need_update;
}
/*
* spage_max_space_for_new_record () - Find the maximum free space for a new
* insertion
* return: Maximum free length for an insertion
* page_p(in): Pointer to slotted page
*
* Note: The function subtract any pointer array information that may be
* needed for a new insertion.
*/
int
spage_max_space_for_new_record (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
int total_free;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
total_free = (page_header_p->total_free - spage_get_total_saved_spaces (thread_p, page_header_p, page_p));
if ((page_header_p->anchor_type != ANCHORED_DONT_REUSE_SLOTS)
&& (page_header_p->num_slots > page_header_p->num_records))
{
if (total_free < 0)
{
total_free = 0;
}
}
else
{
total_free -= SSIZEOF (SPAGE_SLOT);
if (total_free < SSIZEOF (SPAGE_SLOT))
{
total_free = 0;
}
}
total_free = DB_ALIGN_BELOW (total_free, page_header_p->alignment);
if (total_free < 0)
{
total_free = 0;
}
return total_free;
}
/*
* spage_collect_statistics () - Collect statistical information of the page
* return: none
*
* page_p(in): Pointer to slotted page
* npages(out): the number of pages
* nrecords(out): the number of records
* rec_length(out): total length of records
*/
void
spage_collect_statistics (PAGE_PTR page_p, int *npages, int *nrecords, int *rec_length)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
PGNSLOTS i, last_slot_id;
int pages, records, length;
assert (page_p != NULL);
assert (npages != NULL && nrecords != NULL && rec_length != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
last_slot_id = page_header_p->num_slots - 1;
slot_p = spage_find_slot (page_p, page_header_p, last_slot_id, false);
pages = 1;
records = 0;
length = 0;
for (i = last_slot_id; i > 0; slot_p++, i--)
{
if (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET)
{
assert (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE);
continue;
}
if (slot_p->record_type == REC_BIGONE)
{
pages += 2;
length += (SPAGE_DB_PAGESIZE * 2); /* Assume two pages */
}
if (slot_p->record_type != REC_NEWHOME)
{
records++;
}
if (slot_p->record_type == REC_HOME || slot_p->record_type == REC_NEWHOME)
{
length += slot_p->record_length;
}
}
*npages = pages;
*nrecords = records;
*rec_length = length;
}
/*
* spage_initialize () - Initialize a slotted page
* return: void
*
* page_p(in): Pointer to slotted page
* slots_type(in): Flag which indicates the type of slots
* alignment(in): page alignment type
* safeguard_rvspace(in): Save space during updates. for transaction recovery
*
* Note: A slotted page must be initialized before records are inserted on the
* page. The alignment indicates the valid offset where the records
* should be stored. This is a requirement for peeking records on pages
* according to alignment restrictions.
* A slotted page can optionally be initialized with recovery safeguard
* space in mind. In this case when records are removed or shrunk, the
* space is saved for possible undoes.
*/
void
spage_initialize (THREAD_ENTRY * thread_p, PAGE_PTR page_p, INT16 slot_type, unsigned short alignment, bool is_saving)
{
SPAGE_HEADER *page_header_p;
assert (page_p != NULL);
assert (spage_is_valid_anchor_type (slot_type));
assert (alignment == CHAR_ALIGNMENT || alignment == SHORT_ALIGNMENT || alignment == INT_ALIGNMENT
|| alignment == LONG_ALIGNMENT || alignment == FLOAT_ALIGNMENT || alignment == DOUBLE_ALIGNMENT);
page_header_p = (SPAGE_HEADER *) page_p;
page_header_p->num_slots = 0;
page_header_p->num_records = 0;
page_header_p->is_saving = is_saving;
page_header_p->flags = SPAGE_HEADER_FLAG_NONE;
page_header_p->need_update_best_hint = 0;
page_header_p->reserved_bits = 0;
page_header_p->reserved1 = 0;
page_header_p->anchor_type = slot_type;
page_header_p->total_free = DB_ALIGN (SPAGE_DB_PAGESIZE - sizeof (SPAGE_HEADER), alignment);
page_header_p->cont_free = page_header_p->total_free;
page_header_p->offset_to_free_area = DB_ALIGN (sizeof (SPAGE_HEADER), alignment);
page_header_p->alignment = alignment;
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
}
/*
* spage_compare_slot_offset () - Compare the location (offset) of slots
* return: s1 - s2
* s1(in): slot 1
* s2(in): slot 2
*/
static int
spage_compare_slot_offset (const void *arg1, const void *arg2)
{
SPAGE_SLOT **s1, **s2;
assert (arg1 != NULL);
assert (arg2 != NULL);
s1 = (SPAGE_SLOT **) arg1;
s2 = (SPAGE_SLOT **) arg2;
return ((*s1)->offset_to_record - (*s2)->offset_to_record);
}
/*
* spage_is_slotted_page_type () - Whether or not a slotted page type
* return: yes - true, no - false
*
* ptype(in): page type
*/
static bool
spage_is_slotted_page_type (PAGE_TYPE ptype)
{
switch (ptype)
{
case PAGE_HEAP:
case PAGE_BTREE:
case PAGE_EHASH:
case PAGE_CATALOG:
return true;
default:
return false;
}
}
/*
* spage_compact () - Compact an slotted page
* return:
*
* page_p(in): Pointer to slotted page
*
* Note: Only the records are compacted, the slots are not compacted.
*/
int
spage_compact (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
SPAGE_SLOT **slot_array = NULL;
int to_offset;
int i, j;
PAGE_TYPE ptype;
assert (page_p != NULL);
ptype = pgbuf_get_page_ptype (thread_p, page_p);
assert_release (spage_is_slotted_page_type (ptype));
page_header_p = (SPAGE_HEADER *) page_p;
spage_verify_header (page_p);
if (page_header_p->num_records > 0)
{
slot_array = (SPAGE_SLOT **) calloc ((unsigned int) (page_header_p->num_slots), sizeof (SPAGE_SLOT *));
if (slot_array == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(page_header_p->num_slots) * sizeof (SPAGE_SLOT *));
return ER_FAILED;
}
/* Create an array of sorted offsets... actually pointers to slots */
slot_p = spage_find_slot (page_p, page_header_p, 0, false);
for (j = 0, i = 0; i < page_header_p->num_slots; slot_p--, i++)
{
if (slot_p->record_type > REC_4BIT_USED_TYPE_MAX)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return ER_FAILED;
}
if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
{
slot_array[j++] = slot_p;
}
}
if (page_header_p->num_records != j)
{
VPID vpid;
pgbuf_get_vpid (page_p, &vpid);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_WRONG_NUM_SLOTS, 4, vpid.pageid,
fileio_get_volume_label (vpid.volid, PEEK), j, page_header_p->num_records);
assert (false);
free_and_init (slot_array);
/* will exit here */
logpb_fatal_error_exit_immediately_wo_flush (NULL, ARG_FILE_LINE, "spage_compact");
return ER_FAILED;
}
qsort ((void *) slot_array, page_header_p->num_records, sizeof (SPAGE_SLOT *), spage_compare_slot_offset);
/* Now start compacting the page */
to_offset = sizeof (SPAGE_HEADER);
for (i = 0; i < page_header_p->num_records; i++)
{
/* Make sure that the offset is aligned */
to_offset = DB_ALIGN (to_offset, page_header_p->alignment);
if (to_offset == slot_array[i]->offset_to_record)
{
/* Record slot is already in place */
to_offset += slot_array[i]->record_length;
}
else
{
/* Move the record */
if ((unsigned int) to_offset + slot_array[i]->record_length > (unsigned int) SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return ER_FAILED;
}
memmove ((char *) page_p + to_offset, (char *) page_p + slot_array[i]->offset_to_record,
slot_array[i]->record_length);
slot_array[i]->offset_to_record = to_offset;
ASSERT_ALIGN ((char *) page_p + slot_array[i]->offset_to_record, page_header_p->alignment);
to_offset += slot_array[i]->record_length;
}
}
free_and_init (slot_array);
}
else
{
to_offset = sizeof (SPAGE_HEADER);
}
/* Make sure that the next inserted record will be aligned */
to_offset = DB_ALIGN (to_offset, page_header_p->alignment);
page_header_p->total_free = (SPAGE_DB_PAGESIZE - to_offset - (page_header_p->num_slots * sizeof (SPAGE_SLOT)));
page_header_p->cont_free = page_header_p->total_free;
page_header_p->offset_to_free_area = to_offset;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
spage_verify_header (page_p);
/* The page is set dirty somewhere else */
return NO_ERROR;
}
/*
* spage_find_free_slot () -
* return: void
*
* page_p(in): Pointer to slotted page
* out_slot_p(out):
* start_slot(in):
*/
PGSLOTID
spage_find_free_slot (PAGE_PTR page_p, SPAGE_SLOT ** out_slot_p, PGSLOTID start_slot)
{
PGSLOTID slot_id;
SPAGE_SLOT *slot_p;
SPAGE_HEADER *page_header_p;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, 0, false);
assert (slot_p != NULL);
if (page_header_p->num_slots == page_header_p->num_records)
{
slot_id = page_header_p->num_slots;
slot_p -= slot_id;
}
else
{
for (slot_id = start_slot, slot_p -= slot_id; slot_id < page_header_p->num_slots; slot_p--, slot_id++)
{
if (slot_p->record_type == REC_DELETED_WILL_REUSE)
{
break; /* found free slot */
}
}
}
if (out_slot_p != NULL)
{
*out_slot_p = slot_p;
}
if (spage_find_slot (page_p, page_header_p, slot_id, false) != slot_p)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
return slot_id;
}
/*
* spage_check_space () -
* return: void
*
* page_p(in): Pointer to slotted page
* page_header_p(in):
* space(in):
*/
static int
spage_check_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space)
{
if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
else if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_ERROR;
}
return SP_SUCCESS;
}
/*
* spage_set_slot () -
* return:
*
* slot_p(in): Pointer to slotted page array pointer
* offset(in):
* length(in):
* type(in):
*/
static void
spage_set_slot (SPAGE_SLOT * slot_p, int offset, int length, INT16 type)
{
assert (REC_UNKNOWN <= type && type <= REC_4BIT_USED_TYPE_MAX);
slot_p->offset_to_record = offset;
slot_p->record_length = length;
slot_p->record_type = type;
}
/*
* spage_find_empty_slot () - Find a free area/slot where a record of
* the given length can be inserted onto the given slotted page
* return: either SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* record_length(in): Length of area/record
* record_type(in): Type of record to be inserted
* out_slot_p(out): Pointer to slotted page array pointer
* out_space_p(out): Space used/defined
* out_slot_id_p(out): Allocated slot or NULL_SLOTID
*
* Note: If there is not enough space on the page, an error condition is
* returned and out_slot_id_p is set to NULL_SLOTID.
*/
static int
spage_find_empty_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, int record_length, INT16 record_type,
SPAGE_SLOT ** out_slot_p, int *out_space_p, PGSLOTID * out_slot_id_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
PGSLOTID slot_id;
int waste, space, status;
assert (page_p != NULL);
assert (out_slot_p != NULL);
assert (out_space_p != NULL);
assert (out_slot_id_p != NULL);
*out_slot_p = NULL;
*out_space_p = 0;
*out_slot_id_p = NULL_SLOTID;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
/* Calculate the wasted space that this record will introduce. We need to take in consideration the wasted space when
* there is space saved */
waste = DB_WASTED_ALIGN (record_length, page_header_p->alignment);
space = record_length + waste;
/* Quickly check for available space. We may need to check again if a slot is created (instead of reused) */
if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
/* Find a free slot. Try to reuse an unused slotid, instead of allocating a new one */
slot_id = spage_find_free_slot (page_p, &slot_p, 0);
if (slot_id == SP_ERROR)
{
return SP_ERROR; /* this will not happen */
}
/* Make sure that there is enough space for the record and the slot */
if (slot_id > page_header_p->num_slots)
{ /* defense code */
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
if (slot_id == page_header_p->num_slots)
{
/* We are allocating a new slotid. Check for available space again */
space += sizeof (SPAGE_SLOT);
status = spage_check_space (thread_p, page_p, page_header_p, space);
if (status != SP_SUCCESS)
{
return status;
}
/* Adjust the number of slots */
page_header_p->num_slots++;
}
else
{
/* We already know that there is total space available since the slot is reused and the space was checked above */
if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_ERROR;
}
}
/* Now separate an empty area for the record */
spage_set_slot (slot_p, page_header_p->offset_to_free_area, record_length, record_type);
/* Adjust the header */
page_header_p->num_records++;
page_header_p->total_free -= space;
page_header_p->cont_free -= space;
page_header_p->offset_to_free_area += (record_length + waste);
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
spage_verify_header (page_p);
*out_slot_p = slot_p;
*out_space_p = space;
*out_slot_id_p = slot_id;
/* The page is set dirty somewhere else */
return SP_SUCCESS;
}
/*
* spage_shift_slot_up() -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* slot_p(in/out): Pointer to slotted page pointer array
*/
static void
spage_shift_slot_up (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
{
SPAGE_SLOT *last_slot_p;
assert (page_p != NULL);
assert (slot_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
last_slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots, false);
if (page_header_p->anchor_type == UNANCHORED_ANY_SEQUENCE)
{
spage_set_slot (last_slot_p, slot_p->offset_to_record, slot_p->record_length, slot_p->record_type);
}
else
{
if (last_slot_p < slot_p)
{
memmove (last_slot_p, last_slot_p + 1, sizeof (SPAGE_SLOT) * (slot_p - last_slot_p));
}
}
spage_set_slot (slot_p, SPAGE_EMPTY_OFFSET, 0, REC_UNKNOWN);
SPAGE_VERIFY_HEADER (page_header_p);
}
/*
* spage_shift_slot_down () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* slot_p(in/out): Pointer to slotted page pointer array
*/
static void
spage_shift_slot_down (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
{
SPAGE_SLOT *last_slot_p;
assert (page_p != NULL);
assert (slot_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
last_slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots - 1, false);
if (page_header_p->anchor_type == UNANCHORED_ANY_SEQUENCE)
{
spage_set_slot (slot_p, last_slot_p->offset_to_record, last_slot_p->record_length, last_slot_p->record_type);
}
else
{
if (last_slot_p < slot_p)
{
memmove (last_slot_p + 1, last_slot_p, sizeof (SPAGE_SLOT) * (slot_p - last_slot_p));
}
}
spage_set_slot (last_slot_p, SPAGE_EMPTY_OFFSET, 0, REC_UNKNOWN);
SPAGE_VERIFY_HEADER (page_header_p);
}
/*
* spage_add_new_slot () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* out_space_p(in/out):
*/
static int
spage_add_new_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int *out_space_p)
{
SPAGE_SLOT *last_slot_p;
int status;
/*
* New one slot is are being allocated.
*/
SPAGE_VERIFY_HEADER (page_header_p);
*out_space_p += sizeof (SPAGE_SLOT);
status = spage_check_space (thread_p, page_p, page_header_p, *out_space_p);
if (status != SP_SUCCESS)
{
return status;
}
/* Adjust the space for creation of new slot. The space for the record is adjusted later on */
page_header_p->num_slots++;
last_slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots - 1, false);
spage_set_slot (last_slot_p, SPAGE_EMPTY_OFFSET, 0, REC_UNKNOWN);
spage_verify_header (page_p);
return SP_SUCCESS;
}
/*
* spage_take_slot_in_use () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* slot_id(in):
* slot_p(in/out):
* out_space_p(in/out):
*/
static int
spage_take_slot_in_use (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, PGSLOTID slot_id,
SPAGE_SLOT * slot_p, int *out_space_p)
{
int status;
SPAGE_VERIFY_HEADER (page_header_p);
/*
* An already defined slot. The slotid can be used in the following
* cases:
* 1) The slot is marked as deleted. (Reuse)
* 2) The type of slotted page is unanchored and there is space to define
* a new slot for the shift operation
*/
if (slot_p->record_type == REC_DELETED_WILL_REUSE)
{
/* Make sure that there is enough space for the record. There is not need for slot space. */
status = spage_check_space (thread_p, page_p, page_header_p, *out_space_p);
if (status != SP_SUCCESS)
{
return status;
}
}
else
{
/* Slot is in use. The pointer array must be shifted (i.e., addresses of records are modified). */
if (page_header_p->anchor_type == ANCHORED || page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_BAD_INSERTION_SLOT, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
/* Make sure that there is enough space for the record and the slot */
*out_space_p += sizeof (SPAGE_SLOT);
status = spage_check_space (thread_p, page_p, page_header_p, *out_space_p);
if (status != SP_SUCCESS)
{
return status;
}
spage_shift_slot_up (page_p, page_header_p, slot_p);
/* Adjust the header for the newly created slot */
page_header_p->num_slots += 1;
}
spage_verify_header (page_p);
return SP_SUCCESS;
}
/*
* spage_find_empty_slot_at () - Find a free area where a record of
* the given length can be inserted onto the given slotted page
* return: either SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Requested slot_id
* record_length(in): Length of area/record
* record_type(in): Type of record to be inserted
* out_slot_p(out): Pointer to slotted page array pointer
*
*/
static int
spage_find_empty_slot_at (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_length,
INT16 record_type, SPAGE_SLOT ** out_slot_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int waste, space, status;
assert (page_p != NULL);
assert (out_slot_p != NULL);
*out_slot_p = NULL;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
/* Calculate the wasted space that this record will introduce. We need to take in consideration the wasted space when
* there is space saved */
waste = DB_WASTED_ALIGN (record_length, page_header_p->alignment);
space = record_length + waste;
if (slot_id > page_header_p->num_slots)
{ /* defense code */
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
if (slot_id == page_header_p->num_slots)
{
assert (log_is_in_crash_recovery () || !(page_header_p->is_saving && !logtb_is_current_active (thread_p)));
status = spage_add_new_slot (thread_p, page_p, page_header_p, &space);
}
else
{
status = spage_take_slot_in_use (thread_p, page_p, page_header_p, slot_id, slot_p, &space);
}
if (status != SP_SUCCESS)
{
return status;
}
/* Now separate an empty area for the record */
spage_set_slot (slot_p, page_header_p->offset_to_free_area, record_length, record_type);
/* Adjust the header */
page_header_p->num_records++;
page_header_p->total_free -= space;
page_header_p->cont_free -= space;
page_header_p->offset_to_free_area += (record_length + waste);
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
spage_verify_header (page_p);
*out_slot_p = slot_p;
/* The page is set dirty somewhere else */
return SP_SUCCESS;
}
/*
* spage_check_record_for_insert () -
* return:
*
* record_descriptor_p(in):
*/
static int
spage_check_record_for_insert (RECDES * record_descriptor_p)
{
if (record_descriptor_p->length > spage_max_record_size ())
{
return SP_DOESNT_FIT;
}
if (record_descriptor_p->type == REC_MARKDELETED || record_descriptor_p->type == REC_DELETED_WILL_REUSE)
{
record_descriptor_p->type = REC_HOME;
}
return SP_SUCCESS;
}
/*
* spage_insert () - Insert a record
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* record_descriptor_p(in): Pointer to a record descriptor
* out_slot_id_p(out): Slot identifier
*/
int
spage_insert (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_descriptor_p, PGSLOTID * out_slot_id_p)
{
SPAGE_SLOT *slot_p;
int used_space;
int status;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
assert (out_slot_id_p != NULL);
status =
spage_find_slot_for_insert (thread_p, page_p, record_descriptor_p, out_slot_id_p, (void **) &slot_p, &used_space);
if (status == SP_SUCCESS)
{
status = spage_insert_data (thread_p, page_p, record_descriptor_p, slot_p);
}
return status;
}
/*
* spage_find_slot_for_insert () - Find a slot id and related information in the
* given page
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* record_descriptor_p(in): Pointer to a record descriptor
* out_slot_id_p(out): Slot identifier
* out_slot_p(out): Pointer to slotted array
* out_used_space_p(out): Pointer to int
*/
STATIC_INLINE int
spage_find_slot_for_insert (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_descriptor_p,
PGSLOTID * out_slot_id_p, void **out_slot_p, int *out_used_space_p)
{
SPAGE_SLOT *slot_p;
int status;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
assert (out_slot_id_p != NULL);
assert (out_slot_p != NULL);
assert (out_used_space_p != NULL);
*out_slot_id_p = NULL_SLOTID;
status = spage_check_record_for_insert (record_descriptor_p);
if (status != SP_SUCCESS)
{
return status;
}
status =
spage_find_empty_slot (thread_p, page_p, record_descriptor_p->length, record_descriptor_p->type, &slot_p,
out_used_space_p, out_slot_id_p);
*out_slot_p = (void *) slot_p;
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return status;
}
/*
* spage_insert_data () - Copy the contents of a record into the given page/slot
* return: either of SP_ERROR, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* record_descriptor_p(in): Pointer to a record descriptor
* slot_p(in): Pointer to slotted array
*/
static int
spage_insert_data (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_descriptor_p, void *slot_p)
{
SPAGE_SLOT *tmp_slot_p;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
assert (slot_p != NULL);
if (record_descriptor_p->length < 0)
{
assert (false);
return SP_ERROR;
}
tmp_slot_p = (SPAGE_SLOT *) slot_p;
if (record_descriptor_p->type != REC_ASSIGN_ADDRESS)
{
if ((unsigned int) tmp_slot_p->offset_to_record + record_descriptor_p->length > (unsigned int) SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + tmp_slot_p->offset_to_record), record_descriptor_p->data, record_descriptor_p->length);
}
else
{
if (tmp_slot_p->offset_to_record + SSIZEOF (TRANID) > (unsigned int) SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
*((TRANID *) (page_p + tmp_slot_p->offset_to_record)) = logtb_find_current_tranid (thread_p);
}
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
/*
* spage_insert_at () - Insert a record onto the given slotted page at the given
* slot_id
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot_id for newly inserted record
* record_descriptor_p(in): Pointer to a record descriptor
*
* Note: The records on this page must be UNANCHORED, otherwise, an error is
* set and an indication of an error is returned. If the record does not
* fit on the page, such effect is returned.
*/
int
spage_insert_at (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES * record_descriptor_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int status;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
if (record_descriptor_p->length < 0)
{
assert (false);
return SP_ERROR;
}
status = spage_check_record_for_insert (record_descriptor_p);
if (status != SP_SUCCESS)
{
return status;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (slot_id > page_header_p->num_slots)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
status =
spage_find_empty_slot_at (thread_p, page_p, slot_id, record_descriptor_p->length, record_descriptor_p->type,
&slot_p);
if (status == SP_SUCCESS)
{
status = spage_insert_data (thread_p, page_p, record_descriptor_p, slot_p);
}
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return status;
}
/*
* spage_insert_for_recovery () - Insert a record onto the given slotted page at
* the given slot_id (only for recovery)
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot_id for insertion
* record_descriptor_p(in): Pointer to a record descriptor
*
* Note: If there is a record located at this slot and the page type of the
* page is anchored the slot record will be replaced by the new record.
* Otherwise, the slots will be moved.
*/
int
spage_insert_for_recovery (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES * record_descriptor_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int status;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
if (record_descriptor_p->length < 0)
{
assert (false);
return SP_ERROR;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (page_header_p->anchor_type != ANCHORED && page_header_p->anchor_type != ANCHORED_DONT_REUSE_SLOTS)
{
return spage_insert_at (thread_p, page_p, slot_id, record_descriptor_p);
}
status = spage_check_record_for_insert (record_descriptor_p);
if (status != SP_SUCCESS)
{
return status;
}
/* If there is a record located at the given slot, the record is removed */
if (slot_id < page_header_p->num_slots)
{
slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
assert (page_header_p->anchor_type == ANCHORED || page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS);
assert (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET);
slot_p->record_type = REC_DELETED_WILL_REUSE;
}
status =
spage_find_empty_slot_at (thread_p, page_p, slot_id, record_descriptor_p->length, record_descriptor_p->type,
&slot_p);
if (status == SP_SUCCESS)
{
if (record_descriptor_p->type != REC_ASSIGN_ADDRESS)
{
if (SPAGE_OVERFLOW (slot_p->offset_to_record + record_descriptor_p->length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + slot_p->offset_to_record), record_descriptor_p->data, record_descriptor_p->length);
}
SPAGE_VERIFY_HEADER (page_header_p);
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
}
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return status;
}
/*
* spage_is_record_located_at_end () -
* return:
*
* page_header_p(in): Pointer to header of slotted page
* slot_p(in):
*/
static bool
spage_is_record_located_at_end (SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
{
int waste;
waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
assert ((int) slot_p->offset_to_record + (int) slot_p->record_length + waste <= page_header_p->offset_to_free_area);
return ((int) slot_p->offset_to_record + (int) slot_p->record_length + waste) == page_header_p->offset_to_free_area;
}
/*
* spage_reduce_a_slot () -
* return:
*
* page_p(in): Pointer to slotted page
*/
static void
spage_reduce_a_slot (PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots - 1, false);
spage_set_slot (slot_p, SPAGE_EMPTY_OFFSET, 0, REC_UNKNOWN);
page_header_p->num_slots--;
page_header_p->total_free += sizeof (SPAGE_SLOT);
page_header_p->cont_free += sizeof (SPAGE_SLOT);
spage_verify_header (page_p);
}
/*
* spage_delete () - Delete the record located at given slot on the given page
* return: slot_id on success and NULL_SLOTID on failure
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to delete
*/
PGSLOTID
spage_delete (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int waste;
int free_space;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
assert (spage_is_valid_anchor_type (page_header_p->anchor_type));
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return NULL_SLOTID;
}
page_header_p->num_records--;
waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
free_space = slot_p->record_length + waste;
page_header_p->total_free += free_space;
/* If it is the last slot in the page, the contiguous free space can be adjusted. Avoid future compaction as much as
* possible */
if (spage_is_record_located_at_end (page_header_p, slot_p))
{
page_header_p->cont_free += free_space;
page_header_p->offset_to_free_area -= free_space;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
}
switch (page_header_p->anchor_type)
{
case ANCHORED:
slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
slot_p->record_type = REC_DELETED_WILL_REUSE;
break;
case ANCHORED_DONT_REUSE_SLOTS:
slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
slot_p->record_type = REC_MARKDELETED;
break;
case UNANCHORED_ANY_SEQUENCE:
case UNANCHORED_KEEP_SEQUENCE:
assert (page_header_p->is_saving == false);
spage_shift_slot_down (page_p, page_header_p, slot_p);
spage_reduce_a_slot (page_p);
free_space += sizeof (SPAGE_SLOT);
break;
default:
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL_SLOTID;
}
/* Indicate that we are savings */
if (page_header_p->is_saving && spage_save_space (thread_p, page_header_p, page_p, free_space) != NO_ERROR)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL_SLOTID;
}
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
spage_verify_header (page_p);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return slot_id;
}
/*
* spage_delete_for_recovery () - Delete the record located at given slot on the given page
* (only for recovery)
* return: slot_id on success and NULL_SLOTID on failure
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to delete
*
* Note: The slot is always reused even in ANCHORED_DONT_REUSE_SLOTS pages
* since the record was never made permanent.
*/
PGSLOTID
spage_delete_for_recovery (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
if (spage_delete (thread_p, page_p, slot_id) != slot_id)
{
return NULL_SLOTID;
}
/* Set the slot as deleted with reuse since the address was never permanent */
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS)
{
slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
if (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET && slot_p->record_type == REC_MARKDELETED)
{
slot_p->record_type = REC_DELETED_WILL_REUSE;
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
}
}
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return slot_id;
}
/*
* spage_check_updatable () -
* return:
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to delete
* record_descriptor_p(in):
* out_slot_p(out):
* out_space_p(out):
* out_old_waste_p(out):
* out_new_waste_p(out):
*/
static int
spage_check_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_descriptor_length,
SPAGE_SLOT ** out_slot_p, int *out_space_p, int *out_old_waste_p, int *out_new_waste_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int new_waste, old_waste;
int space;
assert (page_p != NULL);
if (record_descriptor_length < 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
if (record_descriptor_length > spage_max_record_size ())
{
return SP_DOESNT_FIT;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
new_waste = DB_WASTED_ALIGN (record_descriptor_length, page_header_p->alignment);
space = record_descriptor_length + new_waste - slot_p->record_length - old_waste;
if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
if (out_slot_p)
{
*out_slot_p = slot_p;
}
if (out_space_p)
{
*out_space_p = space;
}
if (out_old_waste_p)
{
*out_old_waste_p = old_waste;
}
if (out_new_waste_p)
{
*out_new_waste_p = new_waste;
}
return SP_SUCCESS;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* spage_check_mvcc_updatable () - check whether is enough free area to update
* a record in MVCC
* return:
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to logical delete
* mvcc_delete_record_length(in): the length of delete record
* mvcc_insert_record_length(in): the length of insert record
*/
static int
spage_check_mvcc_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int mvcc_delete_record_length,
int mvcc_insert_record_length)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int new_waste, old_waste;
int space;
int insert_record_space;
int max_record_size;
assert (page_p != NULL);
if (mvcc_delete_record_length < 0 || mvcc_insert_record_length < 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
max_record_size = spage_max_record_size ();
if (mvcc_delete_record_length > max_record_size || mvcc_insert_record_length > max_record_size)
{
return SP_DOESNT_FIT;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
new_waste = DB_WASTED_ALIGN (mvcc_delete_record_length, page_header_p->alignment);
space = mvcc_delete_record_length + new_waste - slot_p->record_length - old_waste;
if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
/* delete record fit into page_p check whether both delete and insert record fit into page_p */
new_waste = DB_WASTED_ALIGN (mvcc_insert_record_length, page_header_p->alignment);
insert_record_space = (mvcc_insert_record_length + new_waste);
space += insert_record_space;
if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
/* Find a free slot. Try to reuse an unused slotid, instead of allocating a new one */
slot_id = spage_find_free_slot (page_p, &slot_p, 0);
if (slot_id == SP_ERROR)
{
return SP_ERROR; /* this will not happen */
}
/* Make sure that there is enough space for the record and the slot */
if (slot_id > page_header_p->num_slots)
{ /* defense code */
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
if (slot_id == page_header_p->num_slots)
{
/* check whether is enough space for old record, new record and the slot */
insert_record_space += sizeof (SPAGE_SLOT);
space += sizeof (SPAGE_SLOT);
if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
else if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, insert_record_space) == false)
{
return SP_ERROR;
}
}
else
{
/* We already know that there is total space available since the slot is reused and the space was checked above */
if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, insert_record_space) == false)
{
return SP_ERROR;
}
}
return SP_SUCCESS;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* spage_update_record_in_place () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* slot_p(in/out): Pointer to slotted page pointer array
* record_descriptor_p(in/out):
* space(in):
*/
static int
spage_update_record_in_place (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p,
const RECDES * record_descriptor_p, int space)
{
bool is_located_end;
SPAGE_VERIFY_HEADER (page_header_p);
if (record_descriptor_p->length < 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
/* Update the record in place. Same area */
is_located_end = spage_is_record_located_at_end (page_header_p, slot_p);
slot_p->record_length = record_descriptor_p->length;
if (SPAGE_OVERFLOW (slot_p->offset_to_record + record_descriptor_p->length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + slot_p->offset_to_record), record_descriptor_p->data, record_descriptor_p->length);
page_header_p->total_free -= space;
/* If the record was located at the end, we can execute a simple compaction */
if (is_located_end)
{
page_header_p->cont_free -= space;
page_header_p->offset_to_free_area += space;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
SPAGE_VERIFY_HEADER (page_header_p);
}
spage_verify_header (page_p);
return SP_SUCCESS;
}
/*
* spage_update_record_after_compact () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* slot_p(in/out): Pointer to slotted page pointer array
* record_descriptor_p(in/out):
* space(in):
* old_waste(in):
* new_waste(in):
*/
static int
spage_update_record_after_compact (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
SPAGE_SLOT * slot_p, const RECDES * record_descriptor_p, int space, int old_waste,
int new_waste)
{
int old_offset;
SPAGE_VERIFY_HEADER (page_header_p);
if (record_descriptor_p->length < 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
/*
* If record does not fit in the contiguous free area, compress the page
* leaving the desired record at the end of the free area.
*
* If the record is at the end and there is free space. Do a simple
* compaction
*/
if (spage_is_record_located_at_end (page_header_p, slot_p) && space <= page_header_p->cont_free)
{
old_waste += slot_p->record_length;
spage_add_contiguous_free_space (page_p, old_waste);
space = record_descriptor_p->length + new_waste;
old_waste = 0;
}
else if (record_descriptor_p->length + new_waste > page_header_p->cont_free)
{
/*
* Full compaction: eliminate record from compaction (like a quick
* delete). Compaction always finish with the correct amount of free
* space.
*/
old_offset = slot_p->offset_to_record;
slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
page_header_p->total_free += (old_waste + slot_p->record_length);
page_header_p->num_records--;
if (spage_compact (thread_p, page_p) != NO_ERROR)
{
slot_p->offset_to_record = old_offset;
ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
page_header_p->total_free -= (old_waste + slot_p->record_length);
page_header_p->num_records++;
spage_verify_header (page_p);
return SP_ERROR;
}
page_header_p->num_records++;
space = record_descriptor_p->length + new_waste;
}
/* Now update the record */
spage_set_slot (slot_p, page_header_p->offset_to_free_area, record_descriptor_p->length, slot_p->record_type);
if (page_header_p->offset_to_free_area + record_descriptor_p->length > SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + page_header_p->offset_to_free_area), record_descriptor_p->data,
record_descriptor_p->length);
/* Adjust the header */
page_header_p->total_free -= space;
page_header_p->cont_free -= (record_descriptor_p->length + new_waste);
page_header_p->offset_to_free_area += (record_descriptor_p->length + new_waste);
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
spage_verify_header (page_p);
return SP_SUCCESS;
}
/*
* spage_update () - Update the record located at the given slot with the data
* described by the given record descriptor
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to update
* record_descriptor_p(in): Pointer to a record descriptor
*
* Note: This function do not update the type of the record. If it is changed, it must be handled by the caller
*/
int
spage_update (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, const RECDES * record_descriptor_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int new_waste, old_waste;
int space;
int total_free_save;
int status;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
assert (pgbuf_get_latch_mode (page_p) == PGBUF_LATCH_WRITE);
if (record_descriptor_p->length < 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
total_free_save = page_header_p->total_free;
status =
spage_check_updatable (thread_p, page_p, slot_id, record_descriptor_p->length, &slot_p, &space, &old_waste,
&new_waste);
if (status != SP_SUCCESS)
{
return status;
}
/* If the new representation fits onto the area of the old representation, execute the update in place */
if (record_descriptor_p->length <= (int) slot_p->record_length)
{
status = spage_update_record_in_place (page_p, page_header_p, slot_p, record_descriptor_p, space);
}
else
{
status =
spage_update_record_after_compact (thread_p, page_p, page_header_p, slot_p, record_descriptor_p, space,
old_waste, new_waste);
}
if (status != SP_SUCCESS)
{
return status;
}
if (page_header_p->is_saving
&& spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
SPAGE_VERIFY_HEADER (page_header_p);
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
/*
* spage_is_updatable () - Find if there is enough area to update the record with
* the given data
* return: true if there is enough area to update, or false
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to update
* record_descriptor_length(in): record descriptor length
*/
bool
spage_is_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_descriptor_length)
{
if (spage_check_updatable (thread_p, page_p, slot_id, record_descriptor_length, NULL, NULL, NULL, NULL) != SP_SUCCESS)
{
return false;
}
return true;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* spage_is_mvcc_updatable () - check whether is enough free area to update
* a record in MVCC
* return: true if there is enough area to update in MVCC, or false
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to logical delete
* mvcc_delete_record_length(in): the length of delete record
* mvcc_insert_record_length(in): the length of insert record
*/
bool
spage_is_mvcc_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int delete_record_length,
int insert_record_length)
{
if (spage_check_mvcc_updatable (thread_p, page_p, slot_id, delete_record_length, insert_record_length) != SP_SUCCESS)
{
return false;
}
return true;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* spage_update_record_type () - Update the type of the record located at the
* given slot
* return: void
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to update
* type(in): record type
*/
void
spage_update_record_type (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, INT16 record_type)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
assert (pgbuf_get_latch_mode (page_p) == PGBUF_LATCH_WRITE);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
assert (REC_UNKNOWN <= record_type && record_type <= REC_4BIT_USED_TYPE_MAX);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return;
}
slot_p->record_type = record_type;
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
}
/*
* spage_reclaim () - Reclaim all slots of marked deleted slots of anchored with
* don't reuse slots pages
* return: true if anything was reclaimed and false if nothing was reclaimed
*
* page_p(in): Pointer to slotted page
*
* Note: This function is intended to be run when there are no more references
* of the marked deleted slots, and thus they can be reused.
*/
bool
spage_reclaim (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p, *first_slot_p;
PGSLOTID slot_id;
bool is_reclaim = false;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (page_header_p->num_slots > 0)
{
first_slot_p = spage_find_slot (page_p, page_header_p, 0, false);
/* Start backwards so we can reuse space easily */
for (slot_id = page_header_p->num_slots - 1; slot_id >= 0; slot_id--)
{
slot_p = first_slot_p - slot_id;
if (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET
&& (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE))
{
assert (page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS);
assert (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET);
if ((slot_id + 1) == page_header_p->num_slots)
{
spage_reduce_a_slot (page_p);
}
else
{
slot_p->record_type = REC_DELETED_WILL_REUSE;
}
is_reclaim = true;
}
}
}
if (is_reclaim == true)
{
assert (page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS);
if (page_header_p->num_slots == 0)
{
/* Initialize the page to avoid future compactions */
spage_initialize (thread_p, page_p, page_header_p->anchor_type, page_header_p->alignment,
page_header_p->is_saving);
}
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
}
SPAGE_VERIFY_HEADER (page_header_p);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return is_reclaim;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* spage_split () - Split the record stored at given slot_id at offset location
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to update
* offset(in): Location of split must be > 0 and smaller than record length
* new_slotid(out): new slot id
*/
int
spage_split (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset, PGSLOTID * out_new_slot_id_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
SPAGE_SLOT *new_slot_p;
char *copyarea;
int remain_length;
int total_free_save;
int old_waste, remain_waste, new_waste;
int space;
int status;
assert (page_p != NULL);
assert (out_new_slot_id_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
if (offset < 0 || offset > (int) slot_p->record_length)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_SPLIT_WRONG_OFFSET, 3, offset, slot_p->record_length, slot_id);
/* Offset is wrong */
*out_new_slot_id_p = NULL_SLOTID;
return SP_ERROR;
}
status = spage_find_empty_slot (thread_p, page_p, 0, slot_p->record_type, &new_slot_p, &new_waste, out_new_slot_id_p);
if (status != SP_SUCCESS)
{
return status;
}
/* Do we need to worry about wasted space */
remain_length = slot_p->record_length - offset;
remain_waste = DB_WASTED_ALIGN (offset, page_header_p->alignment);
if (remain_waste == 0)
{
/* We are not wasting any space due to alignments. We do not need to move any data, just modify the length and
* offset of the slots. */
new_slot_p->offset_to_record = slot_p->offset_to_record + offset;
ASSERT_ALIGN ((char *) page_p + new_slot_p->offset_to_record, page_header_p->alignment);
new_slot_p->record_length = remain_length;
slot_p->record_length = offset;
}
else
{
/*
* We must move the second portion of the record to an offset that
* is aligned according to the page alignment method. In fact we
* can moved it to the location (offset) returned by sp_empty, if
* there is enough contiguous space, otherwise, we need to compact
* the page.
*/
total_free_save = page_header_p->total_free;
old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
remain_waste = DB_WASTED_ALIGN (offset, page_header_p->alignment);
new_waste = DB_WASTED_ALIGN (remain_length, page_header_p->alignment);
/*
* Difference in space:
* newlength1 + new_waste1 : sptr->record_length - offset + new_waste1
* + newlength2 + new_waste2 : + offset + new_waste2
* - oldlength - oldwaste : - sptr->record_length - old_waste
* --------------------------------------------------------------------
* new_waste1 + newwaste2 - oldwaste
*/
space = remain_waste + new_waste - old_waste;
if (space > 0 && spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
(void) spage_delete_for_recovery (thread_p, page_p, *out_new_slot_id_p);
*out_new_slot_id_p = NULL_SLOTID;
return SP_DOESNT_FIT;
}
if (remain_length > page_header_p->cont_free)
{
/*
* Need to compact the page, before the second part is moved
* to an alignment position.
*
* Save the second portion
*/
copyarea = (char *) malloc (remain_length);
if (copyarea == NULL)
{
(void) spage_delete_for_recovery (thread_p, page_p, *out_new_slot_id_p);
*out_new_slot_id_p = NULL_SLOTID;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
if (SPAGE_OVERFLOW (slot_p->offset_to_record + offset + remain_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (copyarea, (char *) page_p + slot_p->offset_to_record + offset, remain_length);
/* For now indicate that it has an empty slot */
new_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
new_slot_p->record_length = remain_length;
/* New length for first part of split. */
slot_p->record_length = offset;
/* Adjust some of the space for the compaction, then return the space back. That is, second part is gone for
* now. */
page_header_p->total_free += (space + remain_length + new_waste);
page_header_p->num_records--;
if (spage_compact (thread_p, page_p) != NO_ERROR)
{
slot_p->record_length += remain_length;
page_header_p->total_free -= (space + remain_length + new_waste);
(void) spage_delete_for_recovery (thread_p, page_p, *out_new_slot_id_p);
*out_new_slot_id_p = NULL_SLOTID;
free_and_init (copyarea);
spage_verify_header (page_p);
return SP_ERROR;
}
page_header_p->num_records++;
/* Now update the record */
new_slot_p->offset_to_record = page_header_p->offset_to_free_area;
ASSERT_ALIGN ((char *) page_p + new_slot_p->offset_to_record, page_header_p->alignment);
new_slot_p->record_length = remain_length;
if (SPAGE_OVERFLOW (new_slot_p->offset_to_record + remain_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + new_slot_p->offset_to_record), copyarea, remain_length);
/* Adjust the header */
spage_reduce_contiguous_free_space (page_p, remain_length + new_waste);
free_and_init (copyarea);
}
else
{
/* We can just move the second part to the end of the page */
if (SPAGE_OVERFLOW (new_slot_p->offset_to_record + remain_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + new_slot_p->offset_to_record), (char *) page_p + slot_p->offset_to_record + offset,
remain_length);
new_slot_p->record_length = remain_length;
slot_p->record_length = offset;
/* Adjust the header */
spage_reduce_contiguous_free_space (page_p, space);
}
if (page_header_p->is_saving
&& spage_save_space (thread_p, page_header_p, page_p,
page_header_p->total_free - total_free_save) != NO_ERROR)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
}
spage_verify_header (page_p);
/* set page dirty */
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
/*
* spage_take_out () - REMOVE A PORTION OF A RECORD
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of desired record
* takeout_offset(in): Location where to remove a portion of the data
* takeout_length(in): Length of data to remove starting at takeout_offset
*/
int
spage_take_out (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int takeout_offset, int takeout_length)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int new_waste, old_waste;
int total_free_save;
int mayshift_left;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
if ((takeout_offset + takeout_length) > (int) slot_p->record_length)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_TAKEOUT_WRONG_OFFSET, 4, takeout_offset, takeout_length,
slot_p->record_length, slot_id);
return SP_ERROR;
}
total_free_save = page_header_p->total_free;
old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
new_waste = DB_WASTED_ALIGN (slot_p->record_length - takeout_offset, page_header_p->alignment);
/*
* How to shift: The left portion to the right or
* the right portion to the left ?
*
* We shift the left portion to the right only when the left portion is
* smaller than the right portion and we will end up with the record
* aligned without moving the right portion (is left aligned when we
* shifted "takeout_length" ?).
*/
/* Check alignment of second part */
mayshift_left = DB_WASTED_ALIGN (slot_p->offset_to_record + takeout_length, page_header_p->alignment);
if (mayshift_left == 0 && (takeout_offset < ((int) slot_p->record_length - takeout_offset - takeout_length)))
{
/*
* Move left part to right since we can archive alignment by moving left
* part "takeout_length" spaces and the left part is smaller than right
* part.
*/
if (takeout_offset == 0)
{
/* Don't need to move anything we are chopping the record from the left */
;
}
else
{
if (SPAGE_OVERFLOW (slot_p->offset_to_record + takeout_length + takeout_offset))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memmove ((char *) page_p + slot_p->offset_to_record + takeout_length,
(char *) page_p + slot_p->offset_to_record, takeout_offset);
}
slot_p->offset_to_record += takeout_length;
}
else
{
/* Move right part "takeout_length" positions to the left */
if (((int) slot_p->record_length - takeout_offset - takeout_length) > 0)
{
/* We are removing a portion of the record from the middle. That is, we remove a portion of the record and
* glue the remaining two pieces */
if (SPAGE_OVERFLOW (slot_p->offset_to_record + takeout_offset
+ (slot_p->record_length - takeout_offset - takeout_length)))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memmove ((char *) page_p + slot_p->offset_to_record + takeout_offset,
(char *) page_p + slot_p->offset_to_record + takeout_offset + takeout_length,
slot_p->record_length - takeout_offset - takeout_length);
}
else
{
/* We are truncating the record */
;
}
if (spage_is_record_located_at_end (page_header_p, slot_p))
{
/*
* The record is located just before the contiguous free area. That is,
* at the end of the page.
*
* Do a simple compaction
*/
page_header_p->cont_free += takeout_length + old_waste - new_waste;
page_header_p->offset_to_free_area -= takeout_length + old_waste - new_waste;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
}
}
slot_p->record_length -= takeout_length;
page_header_p->total_free += (takeout_length + old_waste - new_waste);
if (page_header_p->is_saving
&& spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
spage_verify_header (page_p);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
/*
* spage_append () - Append the data described by the given record descriptor
* into the record located at the given slot
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of desired record
* record_descriptor_p(in): Pointer to a record descriptor
*/
int
spage_append (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, const RECDES * record_descriptor_p)
{
return spage_put_helper (thread_p, page_p, slot_id, 0, record_descriptor_p, true);
}
/*
* spage_put () - Add the data described by the given record descriptor within
* the given offset of the record located at the given slot
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of desired record
* offset(in): Location where to add the portion of the data
* record_descriptor_p(in): Pointer to a record descriptor
*/
int
spage_put (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset, const RECDES * record_descriptor_p)
{
return spage_put_helper (thread_p, page_p, slot_id, offset, record_descriptor_p, false);
}
/*
* spage_put_helper () -
* return:
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of desired record
* offset(in):
* record_descriptor_p(in):
* is_append(in):
*/
static int
spage_put_helper (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset,
const RECDES * record_descriptor_p, bool is_append)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int old_offset;
int new_waste, old_waste;
int space;
int total_free_save;
char *copyarea;
assert (page_p != NULL);
assert (record_descriptor_p != NULL);
if (record_descriptor_p->length < 0)
{
assert (false);
return SP_ERROR;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
if ((record_descriptor_p->length + (int) slot_p->record_length) > spage_max_record_size ())
{
return SP_DOESNT_FIT;
}
total_free_save = page_header_p->total_free;
old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
new_waste = DB_WASTED_ALIGN (record_descriptor_p->length + slot_p->record_length, page_header_p->alignment);
space = record_descriptor_p->length + new_waste - old_waste;
if (space > 0 && spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
{
return SP_DOESNT_FIT;
}
if (spage_is_record_located_at_end (page_header_p, slot_p) && space <= page_header_p->cont_free)
{
/*
* The record is at the end of the page (just before contiguous free
* space), and there is space on the contiguous free are to put in the
* new data.
*/
spage_add_contiguous_free_space (page_p, old_waste);
if (!is_append)
{
/* Move anything after offset, so we can insert the desired data */
if (SPAGE_OVERFLOW (slot_p->offset_to_record + offset + record_descriptor_p->length
+ (slot_p->record_length - offset)))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memmove ((char *) page_p + slot_p->offset_to_record + offset + record_descriptor_p->length,
(char *) page_p + slot_p->offset_to_record + offset, slot_p->record_length - offset);
}
}
else if ((int) record_descriptor_p->length + (int) slot_p->record_length <= page_header_p->cont_free)
{
/* Move the old data to the end and remove wasted space from the old data, so we can append at the right place. */
if (is_append)
{
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area, (char *) page_p + slot_p->offset_to_record,
slot_p->record_length);
}
else
{
if (page_header_p->offset_to_free_area + offset > SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area, (char *) page_p + slot_p->offset_to_record,
offset);
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + offset + record_descriptor_p->length
+ (slot_p->record_length - offset)))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return (SP_ERROR);
}
memmove ((char *) page_p + page_header_p->offset_to_free_area + offset + record_descriptor_p->length,
(char *) page_p + slot_p->offset_to_record + offset, slot_p->record_length - offset);
}
slot_p->offset_to_record = page_header_p->offset_to_free_area;
ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
page_header_p->offset_to_free_area += slot_p->record_length; /* Don't increase waste here */
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
page_header_p->cont_free = page_header_p->cont_free - slot_p->record_length + old_waste;
if (is_append)
{
page_header_p->total_free = page_header_p->total_free - slot_p->record_length + old_waste;
}
else
{
page_header_p->total_free += old_waste;
}
SPAGE_VERIFY_HEADER (page_header_p);
}
else
{
/*
* We need to compress the data leaving the desired record at the end.
* Eliminate the old data from compaction (like a quick delete), by
* saving the data in memory. Then, after the compaction we place the
* data on the contiguous free space. We remove the old_waste space
* since we are appending.
*/
copyarea = (char *) malloc (slot_p->record_length);
if (copyarea == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
if (SPAGE_OVERFLOW (slot_p->offset_to_record + slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (copyarea, (char *) page_p + slot_p->offset_to_record, slot_p->record_length);
/* For now indicate that it has an empty slot */
old_offset = slot_p->offset_to_record;
slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
page_header_p->total_free += (slot_p->record_length + old_waste);
page_header_p->num_records--;
if (spage_compact (thread_p, page_p) != NO_ERROR)
{
slot_p->offset_to_record = old_offset;
ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
page_header_p->total_free -= (old_waste + slot_p->record_length);
page_header_p->num_records++;
free_and_init (copyarea);
spage_verify_header (page_p);
return SP_ERROR;
}
page_header_p->num_records++;
/* Move the old data to the end */
if (is_append)
{
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area, copyarea, slot_p->record_length);
}
else
{
if (page_header_p->offset_to_free_area + offset > SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area, copyarea, offset);
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + offset + +record_descriptor_p->length
+ (slot_p->record_length - offset)))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area + offset + record_descriptor_p->length,
copyarea + offset, slot_p->record_length - offset);
}
free_and_init (copyarea);
slot_p->offset_to_record = page_header_p->offset_to_free_area;
ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
spage_reduce_contiguous_free_space (page_p, slot_p->record_length);
}
/* Now perform the put operation. */
if (is_append)
{
if (page_header_p->offset_to_free_area + record_descriptor_p->length > SPAGE_DB_PAGESIZE)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + page_header_p->offset_to_free_area), record_descriptor_p->data,
record_descriptor_p->length);
}
else
{
if (SPAGE_OVERFLOW (slot_p->offset_to_record + offset + record_descriptor_p->length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + slot_p->offset_to_record + offset), record_descriptor_p->data,
record_descriptor_p->length);
}
slot_p->record_length += record_descriptor_p->length;
/* Note that we have already eliminated the old waste, so do not take it in consideration right now. */
spage_reduce_contiguous_free_space (page_p, record_descriptor_p->length + new_waste);
if (page_header_p->is_saving
&& (spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR))
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
spage_verify_header (page_p);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
/*
* spage_overwrite () - Overwrite a portion of the record stored at given slot_id
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record to overwrite
* overwrite_offset(in): Offset on the record to start the overwrite process
* record_descriptor_p(in): New replacement data
*
* Note: overwrite_offset + record_descriptor_p->length must be <= length of record stored
* on slot.
* If this is not the case, you must use a combination of overwrite and
* append.
*/
int
spage_overwrite (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int overwrite_offset,
const RECDES * record_descriptor_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
assert (record_descriptor_p != NULL);
if (record_descriptor_p->length < 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
if ((overwrite_offset + record_descriptor_p->length) > (int) slot_p->record_length)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_OVERWRITE_WRONG_OFFSET, 4, overwrite_offset,
record_descriptor_p->length, slot_p->record_length, slot_id);
return SP_ERROR;
}
if (SPAGE_OVERFLOW (slot_p->offset_to_record + overwrite_offset + record_descriptor_p->length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + slot_p->offset_to_record + overwrite_offset), record_descriptor_p->data,
record_descriptor_p->length);
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
/*
* spage_merge () - Merge the record of the second slot onto the record of the
* first slot
* return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slotid1(in): Slot identifier of first slot
* slotid2(in): Slot identifier of second slot
*
* Note: Then the second slot is removed.
*/
int
spage_merge (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID first_slot_id, PGSLOTID second_slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *first_slot_p;
SPAGE_SLOT *second_slot_p;
int first_old_offset, second_old_offset;
int new_waste, first_old_waste, second_old_waste;
int total_free_save;
char *copyarea;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
/* Find the slots */
first_slot_p = spage_find_slot (page_p, page_header_p, first_slot_id, true);
if (first_slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, first_slot_id,
pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
second_slot_p = spage_find_slot (page_p, page_header_p, second_slot_id, true);
if (second_slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, second_slot_id,
pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
total_free_save = page_header_p->total_free;
first_old_waste = DB_WASTED_ALIGN (first_slot_p->record_length, page_header_p->alignment);
second_old_waste = DB_WASTED_ALIGN (second_slot_p->record_length, page_header_p->alignment);
new_waste = DB_WASTED_ALIGN (first_slot_p->record_length + second_slot_p->record_length, page_header_p->alignment);
if (spage_is_record_located_at_end (page_header_p, first_slot_p)
&& (int) second_slot_p->record_length <= page_header_p->cont_free)
{
/*
* The first record is at the end of the page (just before contiguous free
* space), and there is space on the contiguous free area to append the
* second record.
*
* Remove the wasted space from the free spaces, so we can append at
* the right place.
*/
spage_add_contiguous_free_space (page_p, first_old_waste);
first_old_waste = 0;
}
else if ((int) first_slot_p->record_length + (int) second_slot_p->record_length <= page_header_p->cont_free)
{
/* Move the first data to the end and remove wasted space from the first record, so we can append at the right
* place. */
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + first_slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area, (char *) page_p + first_slot_p->offset_to_record,
first_slot_p->record_length);
first_slot_p->offset_to_record = page_header_p->offset_to_free_area;
ASSERT_ALIGN ((char *) page_p + first_slot_p->offset_to_record, page_header_p->alignment);
page_header_p->offset_to_free_area += first_slot_p->record_length;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
/* Don't increase waste here */
page_header_p->total_free -= (first_slot_p->record_length - first_old_waste);
page_header_p->cont_free -= (first_slot_p->record_length - first_old_waste);
first_old_waste = 0;
SPAGE_VERIFY_HEADER (page_header_p);
}
else
{
/*
* We need to compress the page leaving the desired record at end.
* We eliminate the data of both records (like quick deletes), by
* saving their data in memory. Then, after the compaction we restore
* the data on the contiguous space.
*/
copyarea = (char *) malloc (first_slot_p->record_length + second_slot_p->record_length);
if (copyarea == NULL)
{
return SP_ERROR;
}
if (SPAGE_OVERFLOW (first_slot_p->offset_to_record + first_slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (copyarea, (char *) page_p + first_slot_p->offset_to_record, first_slot_p->record_length);
if (SPAGE_OVERFLOW (second_slot_p->offset_to_record + second_slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (copyarea + first_slot_p->record_length, (char *) page_p + second_slot_p->offset_to_record,
second_slot_p->record_length);
/* Now indicate empty slots. */
first_old_offset = first_slot_p->offset_to_record;
second_old_offset = second_slot_p->offset_to_record;
first_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
second_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
page_header_p->total_free += (first_slot_p->record_length + second_slot_p->record_length + first_old_waste
+ second_old_waste);
page_header_p->num_records -= 2;
if (spage_compact (thread_p, page_p) != NO_ERROR)
{
first_slot_p->offset_to_record = first_old_offset;
ASSERT_ALIGN ((char *) page_p + first_slot_p->offset_to_record, page_header_p->alignment);
second_slot_p->offset_to_record = second_old_offset;
ASSERT_ALIGN ((char *) page_p + second_slot_p->offset_to_record, page_header_p->alignment);
page_header_p->total_free -= (first_slot_p->record_length + second_slot_p->record_length + first_old_waste
+ second_old_waste);
page_header_p->num_records += 2;
free_and_init (copyarea);
spage_verify_header (page_p);
return SP_ERROR;
}
page_header_p->num_records += 2;
/* Move the old data to the end */
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + first_slot_p->record_length
+ second_slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy ((char *) page_p + page_header_p->offset_to_free_area, copyarea,
first_slot_p->record_length + second_slot_p->record_length);
free_and_init (copyarea);
first_slot_p->offset_to_record = page_header_p->offset_to_free_area;
ASSERT_ALIGN ((char *) page_p + first_slot_p->offset_to_record, page_header_p->alignment);
first_slot_p->record_length += second_slot_p->record_length;
second_slot_p->record_length = 0;
second_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
spage_reduce_contiguous_free_space (page_p, first_slot_p->record_length);
first_old_waste = 0;
second_old_waste = 0;
}
/* Now perform the append operation if needed */
if (second_slot_p->record_length != 0)
{
if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + second_slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return SP_ERROR;
}
memcpy (((char *) page_p + page_header_p->offset_to_free_area), (char *) page_p + second_slot_p->offset_to_record,
second_slot_p->record_length);
first_slot_p->record_length += second_slot_p->record_length;
second_slot_p->record_length = 0;
}
/* Note that we have already eliminated the old waste, so do not take it in consideration right now. */
spage_reduce_contiguous_free_space (page_p, new_waste - first_old_waste - second_old_waste);
(void) spage_delete (thread_p, page_p, second_slot_id);
if (page_header_p->is_saving
&& spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return SP_ERROR;
}
pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
spage_verify_header (page_p);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif /* SPAGE_DEBUG */
return SP_SUCCESS;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* spage_search_record () -
* return:
*
* page_p(in): Pointer to slotted page
* out_slot_id_p(in/out): Slot identifier of desired record
* record_descriptor_p(in/out):
* is_peeking(in):
* direction(in):
*/
static SCAN_CODE
spage_search_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p, int is_peeking,
int direction, bool skip_empty)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
PGSLOTID slot_id;
assert (page_p != NULL);
assert (out_slot_id_p != NULL);
assert (record_descriptor_p != NULL);
slot_id = *out_slot_id_p;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (slot_id < 0 || slot_id > page_header_p->num_slots)
{
if (direction == SPAGE_SEARCH_NEXT)
{
slot_id = 0;
}
else
{
slot_id = page_header_p->num_slots - 1;
}
}
else
{
slot_id += direction;
}
slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
while (slot_id >= 0 && slot_id < page_header_p->num_slots && slot_p->offset_to_record == SPAGE_EMPTY_OFFSET)
{
if (skip_empty)
{
slot_id += direction;
slot_p -= direction;
}
else
{
record_descriptor_p->data = NULL;
*out_slot_id_p = slot_id;
return S_SUCCESS;
}
}
SPAGE_VERIFY_HEADER (page_header_p);
if (slot_id >= 0 && slot_id < page_header_p->num_slots)
{
*out_slot_id_p = slot_id;
return spage_get_record_data (page_p, slot_p, record_descriptor_p, is_peeking);
}
else
{
/* There is not anymore records */
*out_slot_id_p = -1;
record_descriptor_p->length = 0;
return S_END;
}
}
/*
* spage_next_record () - Get next record
* return: Either of S_SUCCESS, S_DOESNT_FIT, S_END
* page_p(in): Pointer to slotted page
* out_slot_id_p(in/out): Slot identifier of current record
* record_descriptor_p(out): Pointer to a record descriptor
* is_peeking(in): Indicates whether the record is going to be copied
* (like a copy) or peeked (read at the buffer)
*
* Note: When is_peeking is PEEK, the next available record is peeked onto the
* page. The address of the record descriptor is set to the portion of
* the buffer where the record is stored. Peeking a record should be
* executed with caution since the slotted module may decide to move
* the record around. In general, no other operation should be executed
* on the page until the peeking of the record is done. The page should
* be fixed and locked to avoid any funny behavior. RECORD should NEVER
* be MODIFIED DIRECTLY. Only reads should be performed, otherwise
* header information and other records may be corrupted.
*
* When is_peeking is COPY, the next available record is read
* onto the area pointed by the record descriptor. If the record does not
* fit in such an area, the length of the record is returned as a
* negative value in record_descriptor_p->length and an error is
* indicated in the return value.
*
* If the current value of out_slot_id_p is negative, the first record on the
* page is retrieved.
*/
SCAN_CODE
spage_next_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p, int is_peeking)
{
return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_NEXT, true);
}
/*
* spage_previous_record () - Get previous record
* return: Either of S_SUCCESS, S_DOESNT_FIT, S_END
* page_p(in): Pointer to slotted page
* slot_id(out): Slot identifier of current record
* record_descriptor_p(out): Pointer to a record descriptor
* is_peeking(in): Indicates whether the record is going to be copied
* (like a copy) or peeked (read at the buffer)
*
* Note: When is_peeking is PEEK, the previous available record is peeked onto
* the page. The address of the record descriptor is set to the portion
* of the buffer where the record is stored. Peeking a record should be
* executed with caution since the slotted module may decide to move
* the record around. In general, no other operation should be executed
* on the page until the peeking of the record is done. The page should
* be fixed and locked to avoid any funny behavior. RECORD should NEVER
* be MODIFIED DIRECTLY. Only reads should be performed, otherwise
* header information and other records may be corrupted.
*
* When is_peeking is COPY, the previous available record is
* read onto the area pointed by the record descriptor. If the record
* does not fit in such an area, the length of the record is returned
* as a negative value in record_descriptor_p->length and an error is
* indicated in the return value.
*
* If the current value of slot_id is negative, the first record on the
* page is retrieved.
*/
SCAN_CODE
spage_previous_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p, int is_peeking)
{
return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_PREV, true);
}
/*
* spage_get_record () - Get specified record
* return: Either of S_SUCCESS, S_DOESNT_FIT, S_END
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of current record
* record_descriptor_p(out): Pointer to a record descriptor
* is_peeking(in): Indicates whether the record is going to be copied
* (like a copy) or peeked (read at the buffer)
*
* Note: When is_peeking is PEEK, the desired available record is peeked onto
* the page. The address of the record descriptor is set to the portion
* of the buffer where the record is stored. Peeking a record should be
* executed with caution since the slotted module may decide to move
* the record around. In general, no other operation should be executed
* on the page until the peeking of the record is done. The page should
* be fixed and locked to avoid any funny behavior. RECORD should NEVER
* be MODIFIED DIRECTLY. Only reads should be performed, otherwise
* header information and other records may be corrupted.
*
* When is_peeking is COPY, the desired available record is
* read onto the area pointed by the record descriptor. If the record
* does not fit in such an area, the length of the record is returned
* as a negative value in record_descriptor_p->length and an error is
* indicated in the return value.
*/
SCAN_CODE
spage_get_record (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES * record_descriptor_p,
int is_peeking)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *sptr;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
assert (record_descriptor_p != NULL);
sptr = spage_find_slot (page_p, page_header_p, slot_id, true);
if (sptr == NULL)
{
record_descriptor_p->length = 0;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return S_DOESNT_EXIST;
}
return spage_get_record_data (page_p, sptr, record_descriptor_p, is_peeking);
}
/*
* spage_get_record_data () -
* return:
*
* page_p(in): Pointer to slotted page
* slot_p(in/out): Pointer to slotted page pointer array
* record_descriptor_p(in/out):
* is_peeking(in):
*/
static SCAN_CODE
spage_get_record_data (PAGE_PTR page_p, SPAGE_SLOT * slot_p, RECDES * record_descriptor_p, bool is_peeking)
{
assert (page_p != NULL);
assert (slot_p != NULL);
assert (record_descriptor_p != NULL);
/*
* If peeking, the address of the data in the descriptor is set to the
* address of the record in the buffer. Otherwise, the record is copied
* onto the area specified by the descriptor
*/
if (is_peeking == PEEK)
{
record_descriptor_p->area_size = -1;
record_descriptor_p->data = (char *) page_p + slot_p->offset_to_record;
}
else
{
/* copy the record */
if (record_descriptor_p->area_size < 0 || record_descriptor_p->area_size < (int) slot_p->record_length)
{
/*
* DOES NOT FIT
* Give a hint to the user of the needed length. Hint is given as a
* negative value
*/
/* do not use unary minus because slot_p->record_length is unsigned */
record_descriptor_p->length = (slot_p->record_length * -1);
return S_DOESNT_FIT;
}
if (SPAGE_OVERFLOW (slot_p->offset_to_record + slot_p->record_length))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
assert_release (false);
return S_ERROR;
}
memcpy (record_descriptor_p->data, (char *) page_p + slot_p->offset_to_record, slot_p->record_length);
}
record_descriptor_p->length = slot_p->record_length;
record_descriptor_p->type = slot_p->record_type;
return S_SUCCESS;
}
/*
* spage_get_record_length () - Find the length of the record associated with
* the given slot on the given page
* return: Length of the record or -1 in case of error
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record
*/
int
spage_get_record_length (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return -1;
}
return slot_p->record_length;
}
/*
* spage_get_space_for_record () -
* return:
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record
*/
int
spage_get_space_for_record (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return -1;
}
return (slot_p->record_length + DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment) + SPAGE_SLOT_SIZE);
}
/*
* spage_get_record_type () - Find the type of the record associated with the given slot
* on the given page
* return: record type, or -1 if the given slot is invalid
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record
*/
INT16
spage_get_record_type (PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL || slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE)
{
return REC_UNKNOWN;
}
return slot_p->record_type;
}
/*
* spage_mark_deleted_slot_as_reusable () - Marks the slot of a previously
* deleted record as reusable
* return: SP_ERROR or SP_SUCCESS
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier
*/
int
spage_mark_deleted_slot_as_reusable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p = NULL;
SPAGE_SLOT *slot_p = NULL;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (0 > slot_id || slot_id >= page_header_p->num_slots)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
if (slot_p && slot_p->offset_to_record == SPAGE_EMPTY_OFFSET
&& (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE))
{
; /* OK */
}
else
{
/*
* If the function is called in the scenarios it was designed for, this
* should not happen. The slot to be set as reusable should always
* exist and should not point to a valid record.
*/
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_BAD_INSERTION_SLOT, 3, slot_id,
pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
return SP_ERROR;
}
slot_p->record_type = REC_DELETED_WILL_REUSE;
SPAGE_VERIFY_HEADER (page_header_p);
return SP_SUCCESS;
}
/*
* spage_is_slot_exist () - Find if there is a valid record in given slot
* return: true/false
*
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of record
*/
bool
spage_is_slot_exist (PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL || slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE)
{
return false;
}
else
{
return true;
}
}
/*
* spage_record_type_string () -
* return:
*
* record_type(in):
*/
static const char *
spage_record_type_string (INT16 record_type)
{
assert (REC_UNKNOWN <= record_type && record_type <= REC_4BIT_USED_TYPE_MAX);
switch (record_type)
{
case REC_HOME:
return "HOME";
case REC_NEWHOME:
return "NEWHOME";
case REC_RELOCATION:
return "RELOCATION";
case REC_BIGONE:
return "BIGONE";
case REC_MARKDELETED:
return "MARKDELETED";
case REC_DELETED_WILL_REUSE:
return "DELETED_WILL_REUSE";
case REC_ASSIGN_ADDRESS:
return "ASSIGN_ADDRESS";
default:
assert (false);
return "UNKNOWN";
}
}
/*
* spage_anchor_flag_string () -
* return:
*
* anchor_type(in):
*/
const char *
spage_anchor_flag_string (const INT16 anchor_type)
{
assert (ANCHORED <= anchor_type && anchor_type <= UNANCHORED_KEEP_SEQUENCE);
switch (anchor_type)
{
case ANCHORED:
return "ANCHORED";
case ANCHORED_DONT_REUSE_SLOTS:
return "ANCHORED_DONT_REUSE_SLOTS";
case UNANCHORED_ANY_SEQUENCE:
return "UNANCHORED_ANY_SEQUENCE";
case UNANCHORED_KEEP_SEQUENCE:
return "UNANCHORED_KEEP_SEQUENCE";
default:
assert (false);
return "UNKNOWN";
}
}
/*
* spage_alignment_string () -
* return:
*
* alignment(in):
*/
const char *
spage_alignment_string (unsigned short alignment)
{
switch (alignment)
{
case CHAR_ALIGNMENT:
return "CHAR";
case SHORT_ALIGNMENT:
return "SHORT";
case INT_ALIGNMENT:
return "INT";
case DOUBLE_ALIGNMENT:
return "DOUBLE";
default:
return "UNKNOWN";
}
}
/*
* spage_dump_header () - Dump an slotted page header
* return: void
* page_header_p(in): Pointer to header of slotted page
*
* Note: This function is used for debugging purposes.
*/
static void
spage_dump_header (FILE * fp, const SPAGE_HEADER * page_header_p)
{
char buffer[1024];
spage_dump_header_to_string (buffer, sizeof (buffer), page_header_p);
(void) fprintf (fp, "%s", buffer);
}
/*
* spage_dump_header_to_string () -
* return: void
*
* buffer(out): char buffer pointer
* size(in): buffer size
* page_header_p(in): Pointer to header of slotted page
*
* Note:
*/
static void
spage_dump_header_to_string (char *buffer, int size, const SPAGE_HEADER * page_header_p)
{
int n = 0;
/* Dump header information */
n +=
snprintf (buffer + n, size - n, "NUM SLOTS = %d, NUM RECS = %d, TYPE OF SLOTS = %s,\n", page_header_p->num_slots,
page_header_p->num_records, spage_anchor_flag_string (page_header_p->anchor_type));
n += snprintf (buffer + n, size - n, "ALIGNMENT-TO = %s\n", spage_alignment_string (page_header_p->alignment));
n +=
snprintf (buffer + n, size - n,
"TOTAL FREE AREA = %d, CONTIGUOUS FREE AREA = %d,"
" FREE SPACE OFFSET = %d, NEED UPDATE BEST HINT = %d\n", page_header_p->total_free,
page_header_p->cont_free, page_header_p->offset_to_free_area, page_header_p->need_update_best_hint);
n += snprintf (buffer + n, size - n, "IS_SAVING = %d\n", page_header_p->is_saving);
}
/*
* spage_dump_slots () - Dump the slotted page array
* return: void
*
* slot_p(in): Pointer to slotted page pointer array
* num_slots(in): Number of slots
* alignment(in): Alignment for records
*
* Note: The content of the record is not dumped by this function.
* This function is used for debugging purposes.
*/
static void
spage_dump_slots (FILE * fp, const SPAGE_SLOT * slot_p, PGNSLOTS num_slots, unsigned short alignment)
{
int i;
unsigned int waste;
assert (slot_p != NULL);
for (i = 0; i < num_slots; slot_p--, i++)
{
(void) fprintf (fp, "\nSlot-id = %2d, offset = %4d, type = %s", i, slot_p->offset_to_record,
spage_record_type_string (slot_p->record_type));
if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
{
waste = DB_WASTED_ALIGN (slot_p->record_length, alignment);
(void) fprintf (fp, ", length = %4d, waste = %u", slot_p->record_length, waste);
}
(void) fprintf (fp, "\n");
}
}
/*
* spage_dump_record () -
* return:
*
* fp(in/out):
* page_p(in): Pointer to slotted page
* slot_id(in): Slot identifier of desired record
* slot_p(in): Pointer to slotted page pointer array
*/
static void
spage_dump_record (FILE * fp, PAGE_PTR page_p, PGSLOTID slot_id, SPAGE_SLOT * slot_p)
{
VFID *vfid;
OID *oid;
char *record_p;
int i;
if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
{
(void) fprintf (fp, "\nSlot-id = %2d\n", slot_id);
switch (slot_p->record_type)
{
case REC_BIGONE:
vfid = (VFID *) (page_p + slot_p->offset_to_record);
fprintf (fp, "VFID = %d|%d\n", vfid->volid, vfid->fileid);
break;
case REC_RELOCATION:
oid = (OID *) (page_p + slot_p->offset_to_record);
fprintf (fp, "OID = %d|%d|%d\n", oid->volid, oid->pageid, oid->slotid);
break;
default:
record_p = (char *) page_p + slot_p->offset_to_record;
for (i = 0; i < (int) slot_p->record_length; i++)
{
(void) fprintf (fp, "%02X ", (unsigned char) (*record_p++));
if (i % 20 == 19)
{
fputc ('\n', fp);
}
else if (i % 10 == 9)
{
fputc (' ', fp);
}
}
(void) fprintf (fp, "\n");
}
}
else
{
assert (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE);
(void) fprintf (fp, "\nSlot-id = %2d has been deleted\n", slot_id);
}
}
/*
* spage_dump () - Dump an slotted page
* return: void
* pgptr(in): Pointer to slotted page
* isrecord_printed(in): If true, records are printed in ascii format,
* otherwise, the records are not printed.
*
* Note: The records are printed only when the value of isrecord_printed is
* true. This function is used for debugging purposes.
*/
void
spage_dump (THREAD_ENTRY * thread_p, FILE * fp, PAGE_PTR page_p, int is_record_printed)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int i;
assert (page_p != NULL);
(void) fprintf (fp, "\n*** Dumping pageid = %d of volume = %s ***\n", pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
page_header_p = (SPAGE_HEADER *) page_p;
spage_dump_header (fp, page_header_p);
/* Dump each slot and its corresponding record */
slot_p = spage_find_slot (page_p, page_header_p, 0, false);
spage_dump_slots (fp, slot_p, page_header_p->num_slots, page_header_p->alignment);
if (is_record_printed)
{
(void) fprintf (fp, "\nRecords in ascii follow ...\n");
for (i = 0; i < page_header_p->num_slots; slot_p--, i++)
{
spage_dump_record (fp, page_p, i, slot_p);
}
}
spage_dump_saved_spaces_by_other_trans (thread_p, fp, pgbuf_get_vpid_ptr (page_p));
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif
}
#if !defined(NDEBUG)
/*
* spage_check_num_slots () - Check consistency of page. This function is used for
* debugging purposes
* return: true/false
* ppage_p(in): Pointer to slotted page
*/
bool
spage_check_num_slots (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
int i, nrecs;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, 0, false);
nrecs = 0;
for (i = 0; i < page_header_p->num_slots; slot_p--, i++)
{
if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
{
nrecs++;
}
}
assert (page_header_p->num_records == nrecs);
return (page_header_p->num_records == nrecs) ? true : false;
}
#endif
/*
* spage_check () - Check consistency of page. This function is used for
* debugging purposes
* return: void
* pgptr(in): Pointer to slotted page
*/
int
spage_check (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
char err_msg[1024];
int used_length = 0;
int i, nrecs;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, 0, false);
used_length = (sizeof (SPAGE_HEADER) + sizeof (SPAGE_SLOT) * page_header_p->num_slots);
nrecs = 0;
for (i = 0; i < page_header_p->num_slots; slot_p--, i++)
{
if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
{
used_length += DB_ALIGN (slot_p->record_length, page_header_p->alignment);
nrecs++;
}
}
assert (page_header_p->num_records == nrecs);
if (used_length + page_header_p->total_free > SPAGE_DB_PAGESIZE)
{
snprintf (err_msg, sizeof (err_msg),
"spage_check: Inconsistent page = %d of volume = %s.\n"
"(Used_space + tfree > SPAGE_DB_PAGESIZE\n (%d + %d) > %d \n %d > %d\n",
pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p), used_length,
page_header_p->total_free, SPAGE_DB_PAGESIZE, used_length + page_header_p->total_free,
SPAGE_DB_PAGESIZE);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_INVALID_HEADER, 3, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p), err_msg);
assert (false);
return ER_SP_INVALID_HEADER;
}
if ((page_header_p->cont_free + page_header_p->offset_to_free_area +
SSIZEOF (SPAGE_SLOT) * page_header_p->num_slots) > SPAGE_DB_PAGESIZE)
{
snprintf (err_msg, sizeof (err_msg),
"spage_check: Inconsistent page = %d of volume = %s.\n"
" (cfree + foffset + SIZEOF(SPAGE_SLOT) * nslots) > "
" SPAGE_DB_PAGESIZE\n (%d + %d + (%zu * %d)) > %d\n %zu > %d\n", pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p), page_header_p->cont_free, page_header_p->offset_to_free_area,
sizeof (SPAGE_SLOT), page_header_p->num_slots, SPAGE_DB_PAGESIZE,
(page_header_p->cont_free + page_header_p->offset_to_free_area
+ sizeof (SPAGE_SLOT) * page_header_p->num_slots), SPAGE_DB_PAGESIZE);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_INVALID_HEADER, 3, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p), err_msg);
assert (false);
return ER_SP_INVALID_HEADER;
}
if (page_header_p->cont_free <= (int) -(page_header_p->alignment - 1))
{
snprintf (err_msg, sizeof (err_msg),
"spage_check: Cfree %d is inconsistent in page = %d of volume = %s. Cannot be < -%d\n",
page_header_p->cont_free, pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p),
page_header_p->alignment);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_INVALID_HEADER, 3, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p), err_msg);
assert (false);
return ER_SP_INVALID_HEADER;
}
/* Update any savings, before we check for any incosistencies */
if (page_header_p->is_saving)
{
int other_saved_spaces = 0;
int total_saved = spage_get_saved_spaces (thread_p, page_header_p, page_p,
&other_saved_spaces);
if (other_saved_spaces < 0)
{
snprintf (err_msg, sizeof (err_msg),
"spage_check: Other savings of %d is inconsistent in page = %d of volume = %s.\n",
other_saved_spaces, pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_INVALID_HEADER, 3, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p), err_msg);
assert (false);
return ER_SP_INVALID_HEADER;
}
if (total_saved < 0)
{
snprintf (err_msg, sizeof (err_msg),
"spage_check: Total savings of %d is inconsistent in page = %d of volume = %s. Cannot be < 0\n",
total_saved, pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_INVALID_HEADER, 3, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p), err_msg);
assert (false);
return ER_SP_INVALID_HEADER;
}
}
return NO_ERROR;
}
/*
* spage_check_slot_owner () -
* return:
*
* page_p(in):
* slot_id(in):
*/
int
spage_check_slot_owner (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
TRANID tranid;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
tranid = logtb_find_current_tranid (thread_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
if (slot_p == NULL)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return 0;
}
return (*(TRANID *) (page_p + slot_p->offset_to_record) == tranid);
}
/*
* spage_is_unknown_slot () -
* return:
*
* slot_id(in): Slot identifier of desired record
* page_header_p(in): Pointer to header of slotted page
* slot_p(in): Pointer to slotted page pointer array
*/
STATIC_INLINE bool
spage_is_unknown_slot (PGSLOTID slot_id, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
{
assert (slot_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
const int offset = slot_p->offset_to_record;
const int num_slots = page_header_p->num_slots;
if (unlikely (slot_id < 0 || slot_id >= num_slots))
{
#if defined (NDEBUG)
assert_release (slot_id >= 0 && slot_id < num_slots);
#else
er_log_debug (ARG_FILE_LINE, "Invalid ID : id=%d, num_slots=%d\n", slot_id, num_slots);
#endif
return true;
}
if (unlikely (offset == SPAGE_EMPTY_OFFSET || offset < (int) sizeof (SPAGE_HEADER)))
{
#if defined (NDEBUG)
assert_release (offset != SPAGE_EMPTY_OFFSET && offset >= (int) sizeof (SPAGE_HEADER));
#else
er_log_debug (ARG_FILE_LINE, "Offset violates header boundary : offset=%d, size of SPAGE_HEADER=%zu\n", offset,
sizeof (SPAGE_HEADER));
#endif
return true;
}
const unsigned int total_slots_size = (unsigned int) num_slots * sizeof (SPAGE_SLOT);
if (unlikely
(total_slots_size > (unsigned int) SPAGE_DB_PAGESIZE || offset > (int) (SPAGE_DB_PAGESIZE - total_slots_size)))
{
#if defined (NDEBUG)
assert_release (total_slots_size <= (unsigned int) SPAGE_DB_PAGESIZE
&& offset <= (int) (SPAGE_DB_PAGESIZE - total_slots_size));
#else
er_log_debug (ARG_FILE_LINE,
"Offset violates slot array boundary : offset=%d, total_slots_size=%u, SPAGE_DB_PAGESIZE=%d\n",
offset, total_slots_size, SPAGE_DB_PAGESIZE);
#endif
return true;
}
return false;
}
/*
* spage_find_slot () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* slot_id(in): Slot identifier of desired record
* is_unknown_slot_check(in):
*/
STATIC_INLINE SPAGE_SLOT *
spage_find_slot (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, PGSLOTID slot_id, bool is_unknown_slot_check)
{
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = (SPAGE_SLOT *) (page_p + SPAGE_DB_PAGESIZE - sizeof (SPAGE_SLOT));
slot_p -= slot_id;
if (is_unknown_slot_check)
{
if (spage_is_unknown_slot (slot_id, page_header_p, slot_p))
{
return NULL;
}
}
return slot_p;
}
/*
* spage_has_enough_total_space () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* space(in):
*/
static bool
spage_has_enough_total_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space)
{
TRANID tranid;
int total_saved = 0;
assert (page_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
if (space <= 0)
{
return true;
}
if (page_header_p->is_saving)
{
tranid = logtb_find_current_tranid (thread_p);
if (logtb_is_active (thread_p, tranid))
{
total_saved = spage_get_total_saved_spaces (thread_p, page_header_p, page_p);
}
assert (total_saved >= 0);
return (space <= (page_header_p->total_free - total_saved));
}
else
{
return (space <= page_header_p->total_free);
}
}
/*
* spage_has_enough_contiguous_space () -
* return:
*
* page_p(in): Pointer to slotted page
* page_header_p(in): Pointer to header of slotted page
* space(in)
*/
static bool
spage_has_enough_contiguous_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space)
{
assert (page_p != NULL);
SPAGE_VERIFY_HEADER (page_header_p);
return (space <= page_header_p->cont_free || spage_compact (thread_p, page_p) == NO_ERROR);
}
/*
* spage_add_contiguous_free_space () -
* return:
*
* page_p(in): Pointer to slotted page
* space(in):
*/
static void
spage_add_contiguous_free_space (PAGE_PTR page_p, int space)
{
SPAGE_HEADER *page_header_p;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
page_header_p->total_free += space;
page_header_p->cont_free += space;
page_header_p->offset_to_free_area -= space;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
spage_verify_header (page_p);
}
/*
* spage_next_record_dont_skip_empty () - Get next slot without skipping
* empty records.
*
* return :
* page_p (in) :
* out_slot_id_p (in) :
* record_descriptor_p (in) :
* is_peeking (in) :
*/
SCAN_CODE
spage_next_record_dont_skip_empty (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p,
int is_peeking)
{
return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_NEXT, false);
}
/*
* spage_previous_record_dont_skip_empty () - Get previous slot without
* skipping empty records.
*
* return :
* page_p (in) :
* out_slot_id_p (in) :
* record_descriptor_p (in) :
* is_peeking (in) :
*/
SCAN_CODE
spage_previous_record_dont_skip_empty (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p,
int is_peeking)
{
return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_PREV, false);
}
/*
* spage_get_page_header_info () - Obtain page information for spage_header.
*
* return :
* page_p (in) :
* page_header_info (in) :
*/
SCAN_CODE
spage_get_page_header_info (PAGE_PTR page_p, DB_VALUE ** page_header_info)
{
SPAGE_HEADER *page_header_p;
if (page_p == NULL || page_header_info == NULL)
{
return S_SUCCESS;
}
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
db_make_int (page_header_info[HEAP_PAGE_INFO_NUM_SLOTS], page_header_p->num_slots);
db_make_int (page_header_info[HEAP_PAGE_INFO_NUM_RECORDS], page_header_p->num_records);
db_make_int (page_header_info[HEAP_PAGE_INFO_ANCHOR_TYPE], page_header_p->anchor_type);
db_make_int (page_header_info[HEAP_PAGE_INFO_ALIGNMENT], page_header_p->alignment);
db_make_int (page_header_info[HEAP_PAGE_INFO_TOTAL_FREE], page_header_p->total_free);
db_make_int (page_header_info[HEAP_PAGE_INFO_CONT_FREE], page_header_p->cont_free);
db_make_int (page_header_info[HEAP_PAGE_INFO_OFFSET_TO_FREE_AREA], page_header_p->offset_to_free_area);
db_make_int (page_header_info[HEAP_PAGE_INFO_IS_SAVING], page_header_p->is_saving);
db_make_int (page_header_info[HEAP_PAGE_INFO_UPDATE_BEST], page_header_p->need_update_best_hint);
return S_SUCCESS;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* spage_get_record_offset () - Find the offset of the record associated with
* the given slot on the given page
*
* return : Record offset.
* page_p (in): Pointer to slotted page.
* slotid (in): Slot identifier of record.
*/
int
spage_get_record_offset (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p;
SPAGE_SLOT *slot_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
if (slot_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_UNKNOWN_SLOTID, 3, slot_id, pgbuf_get_page_id (page_p),
pgbuf_get_volume_label (page_p));
return -1;
}
return slot_p->offset_to_record;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* spage_get_slot () - Looks for the slot with slot_id identifier in page_p
* and returns a SPAGE_SLOT. Does not check for unknown
* slots.
*
* return : SPAGE_SLOT.
* page_p (in) : Pointer to slotted page.
* slot_id (in) : Slot identifier.
*/
SPAGE_SLOT *
spage_get_slot (PAGE_PTR page_p, PGSLOTID slot_id)
{
SPAGE_HEADER *page_header_p = NULL;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
if (slot_id < 0 || slot_id >= page_header_p->num_slots)
{
return NULL;
}
return spage_find_slot (page_p, page_header_p, slot_id, false);
}
/*
* spage_vacuum_slot () - Vacuum the slot of an invisible object version.
*
* return : void.
* thread_p (in) : Thread entry.
* page_p (in) : Page pointer.
* slotid (in) : Record slot id.
* reusable (in) : True if slots are reusable, false if they are
* referable.
*
* NOTE: Vacuuming the slot can be done in two ways depending on reusable:
* 1. Reusable = false: Replace slot with
* REC_MARKDELETED (deleted slot, but cannot be reused since there
* may still be references pointing to this slot).
* 2. Reusable = true: Slot is always replaced with
* REC_DELETED_WILL_REUSE. Since there are no references to this
* slot and this version is invisible, there is no point in keeping
* links to newer versions.
*/
void
spage_vacuum_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slotid, bool reusable)
{
SPAGE_HEADER *page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_SLOT *slot_p = NULL;
int waste;
int free_space;
SPAGE_VERIFY_HEADER (page_header_p);
assert (spage_is_valid_anchor_type (page_header_p->anchor_type));
slot_p = spage_find_slot (page_p, page_header_p, slotid, false);
if (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE)
{
/* Vacuum error log */
vacuum_er_log_error (VACUUM_ER_LOG_ERROR, "Object (%d, %d, %d) was already vacuumed",
pgbuf_get_vpid_ptr (page_p)->volid, pgbuf_get_vpid_ptr (page_p)->pageid, slotid);
}
/* Slot is deleted */
page_header_p->num_records--;
waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
free_space = slot_p->record_length + waste;
page_header_p->total_free += free_space;
slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
if (reusable)
{
slot_p->record_type = REC_DELETED_WILL_REUSE;
}
else
{
slot_p->record_type = REC_MARKDELETED;
}
SPAGE_VERIFY_HEADER (page_header_p);
#ifdef SPAGE_DEBUG
spage_check (thread_p, page_p);
#endif
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* spage_reduce_contiguous_free_space () -
* return:
*
* page_p(in): Pointer to slotted page
* space(in):
*/
static void
spage_reduce_contiguous_free_space (PAGE_PTR page_p, int space)
{
SPAGE_HEADER *page_header_p;
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
page_header_p->total_free -= space;
page_header_p->cont_free -= space;
page_header_p->offset_to_free_area += space;
ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
spage_verify_header (page_p);
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* spage_header_start_scan () -
* return: NO_ERROR, or ER_code
*
* thread_p(in):
* show_type(in):
* arg_values(in):
* arg_cnt(in):
* ptr(out): allocate new context. should free by end_scan() function
*/
int
spage_header_start_scan (THREAD_ENTRY * thread_p, int show_type, DB_VALUE ** arg_values, int arg_cnt, void **ptr)
{
int error = NO_ERROR;
DB_VALUE *arg_val0, *arg_val1;
SPAGE_HEADER_CONTEXT *ctx;
PAGE_PTR pgptr = NULL;
PAGE_TYPE ptype;
*ptr = NULL;
arg_val0 = arg_values[0];
arg_val1 = arg_values[1];
assert (arg_val0 != NULL && DB_VALUE_TYPE (arg_val0) == DB_TYPE_INTEGER);
assert (arg_val1 != NULL && DB_VALUE_TYPE (arg_val1) == DB_TYPE_INTEGER);
ctx = (SPAGE_HEADER_CONTEXT *) db_private_alloc (thread_p, sizeof (SPAGE_HEADER_CONTEXT));
if (ctx == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit_on_error;
}
ctx->vpid.volid = db_get_int (arg_val0);
ctx->vpid.pageid = db_get_int (arg_val1);
error = pgbuf_fix_if_not_deallocated (thread_p, &ctx->vpid, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH, &pgptr);
if (error != NO_ERROR)
{
ASSERT_ERROR ();
goto exit_on_error;
}
if (pgptr == NULL)
{
/* page deallocated. */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DIAG_PAGE_NOT_FOUND, 2, ctx->vpid.pageid, ctx->vpid.volid);
error = ER_DIAG_PAGE_NOT_FOUND;
goto exit_on_error;
}
ptype = pgbuf_get_page_ptype (thread_p, pgptr);
if (!spage_is_slotted_page_type (ptype))
{
pgbuf_unfix_and_init (thread_p, pgptr);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DIAG_NOT_SPAGE, 2, ctx->vpid.pageid, ctx->vpid.volid);
error = ER_DIAG_NOT_SPAGE;
goto exit_on_error;
}
memcpy (&ctx->header, pgptr, sizeof (ctx->header));
pgbuf_unfix_and_init (thread_p, pgptr);
*ptr = ctx;
return NO_ERROR;
exit_on_error:
if (ctx != NULL)
{
db_private_free_and_init (thread_p, ctx);
}
return error;
}
/*
* spage_header_next_scan () -
* return: NO_ERROR, or ER_code
*
* thread_p(in):
* cursor(in):
* out_values(in):
* out_cnt(in):
* ptr(in): context pointer
*/
SCAN_CODE
spage_header_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt, void *ptr)
{
int idx = 0;
SPAGE_HEADER_CONTEXT *ctx = (SPAGE_HEADER_CONTEXT *) ptr;
SPAGE_HEADER *header = (SPAGE_HEADER *) (&ctx->header);
if (cursor >= 1)
{
return S_END;
}
db_make_int (out_values[idx], ctx->vpid.volid);
idx++;
db_make_int (out_values[idx], ctx->vpid.pageid);
idx++;
db_make_int (out_values[idx], header->num_slots);
idx++;
db_make_int (out_values[idx], header->num_records);
idx++;
db_make_string (out_values[idx], spage_anchor_flag_string (header->anchor_type));
idx++;
db_make_string (out_values[idx], spage_alignment_string (header->alignment));
idx++;
db_make_int (out_values[idx], header->total_free);
idx++;
db_make_int (out_values[idx], header->cont_free);
idx++;
db_make_int (out_values[idx], header->offset_to_free_area);
idx++;
db_make_int (out_values[idx], header->need_update_best_hint);
idx++;
db_make_int (out_values[idx], header->is_saving);
idx++;
db_make_int (out_values[idx], header->flags);
idx++;
assert (idx == out_cnt);
return S_SUCCESS;
}
/*
* spage_header_end_scan () - free the context
* return: NO_ERROR, or ER_code
*
* thread_p(in):
* ptr(in): context pointer
*/
int
spage_header_end_scan (THREAD_ENTRY * thread_p, void **ptr)
{
if (*ptr != NULL)
{
db_private_free_and_init (thread_p, *ptr);
}
return NO_ERROR;
}
/*
* spage_slots_start_scan () -
* return: NO_ERROR, or ER_code
*
* thread_p(in):
* show_type(in):
* arg_values(in):
* arg_cnt(in):
* ptr(out): allocate new context. should free by end_scan() function
*/
int
spage_slots_start_scan (THREAD_ENTRY * thread_p, int show_type, DB_VALUE ** arg_values, int arg_cnt, void **ptr)
{
int error = NO_ERROR;
SPAGE_HEADER *header = NULL;
DB_VALUE *arg_val0, *arg_val1;
SPAGE_SLOTS_CONTEXT *ctx;
PAGE_PTR pgptr = NULL;
PAGE_TYPE ptype;
*ptr = NULL;
arg_val0 = arg_values[0];
arg_val1 = arg_values[1];
assert (arg_val0 != NULL && DB_VALUE_TYPE (arg_val0) == DB_TYPE_INTEGER);
assert (arg_val1 != NULL && DB_VALUE_TYPE (arg_val1) == DB_TYPE_INTEGER);
ctx = (SPAGE_SLOTS_CONTEXT *) db_private_alloc (thread_p, sizeof (SPAGE_SLOTS_CONTEXT));
if (ctx == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit_on_error;
}
memset (ctx, 0, sizeof (SPAGE_SLOTS_CONTEXT));
ctx->vpid.volid = db_get_int (arg_val0);
ctx->vpid.pageid = db_get_int (arg_val1);
error = pgbuf_fix_if_not_deallocated (thread_p, &ctx->vpid, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH, &pgptr);
if (error != NO_ERROR)
{
ASSERT_ERROR ();
goto exit_on_error;
}
if (pgptr == NULL)
{
/* page deallocated. */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DIAG_PAGE_NOT_FOUND, 2, ctx->vpid.pageid, ctx->vpid.volid);
error = ER_DIAG_PAGE_NOT_FOUND;
goto exit_on_error;
}
ctx->pgptr = (PAGE_PTR) db_private_alloc (thread_p, SPAGE_DB_PAGESIZE);
if (ctx->pgptr == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit_on_error;
}
ptype = pgbuf_get_page_ptype (thread_p, pgptr);
if (!spage_is_slotted_page_type (ptype))
{
pgbuf_unfix_and_init (thread_p, pgptr);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DIAG_NOT_SPAGE, 2, ctx->vpid.pageid, ctx->vpid.volid);
error = ER_DIAG_NOT_SPAGE;
goto exit_on_error;
}
memcpy (ctx->pgptr, pgptr, SPAGE_DB_PAGESIZE);
pgbuf_unfix_and_init (thread_p, pgptr);
header = (SPAGE_HEADER *) ctx->pgptr;
ctx->slot = spage_find_slot ((PAGE_PTR) header, header, 0, false);
*ptr = ctx;
return NO_ERROR;
exit_on_error:
if (ctx != NULL)
{
if (ctx->pgptr != NULL)
{
db_private_free (thread_p, ctx->pgptr);
}
db_private_free_and_init (thread_p, ctx);
}
return error;
}
/*
* spage_slots_next_scan () -
* return: NO_ERROR, or ER_code
*
* thread_p(in):
* cursor(in):
* out_values(in):
* out_cnt(in):
* ptr(in): context pointer
*/
SCAN_CODE
spage_slots_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt, void *ptr)
{
int idx = 0;
SPAGE_SLOTS_CONTEXT *ctx = (SPAGE_SLOTS_CONTEXT *) ptr;
SPAGE_HEADER *header = (SPAGE_HEADER *) ctx->pgptr;
if (cursor >= header->num_slots)
{
return S_END;
}
/*
* In the case of read a arbitrary specified page as slotted page,
* num_slots of SPAGE_HEADER is meaningless data.
* num_slots maybe too big to cause slot header address out of the page range.
*/
if ((char *) ctx->slot < ((char *) header) + sizeof (SPAGE_HEADER))
{
return S_END;
}
db_make_int (out_values[idx], ctx->vpid.volid);
idx++;
db_make_int (out_values[idx], ctx->vpid.pageid);
idx++;
db_make_int (out_values[idx], cursor);
idx++;
db_make_int (out_values[idx], ctx->slot->offset_to_record);
idx++;
db_make_string (out_values[idx], spage_record_type_string (ctx->slot->record_type));
idx++;
db_make_int (out_values[idx], ctx->slot->record_length);
idx++;
db_make_int (out_values[idx], DB_WASTED_ALIGN (ctx->slot->record_length, header->alignment));
idx++;
assert (idx == out_cnt);
ctx->slot--;
return S_SUCCESS;
}
/*
* spage_slots_end_scan () - free the context
* return: NO_ERROR, or ER_code
*
* thread_p(in):
* ptr(in): context pointer
*/
int
spage_slots_end_scan (THREAD_ENTRY * thread_p, void **ptr)
{
SPAGE_SLOTS_CONTEXT *ctx = (SPAGE_SLOTS_CONTEXT *) * ptr;
if (ctx != NULL)
{
if (ctx->pgptr != NULL)
{
db_private_free (thread_p, ctx->pgptr);
}
db_private_free (thread_p, ctx);
*ptr = NULL;
}
return NO_ERROR;
}
/*
* spage_need_compact () - Checks if the page needs compact
* return: true if page needs compact, false otherwise
*
* thread_p(in):
* page_p(in):
*/
bool
spage_need_compact (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
{
SPAGE_HEADER *page_header_p;
assert (page_p != NULL);
page_header_p = (SPAGE_HEADER *) page_p;
SPAGE_VERIFY_HEADER (page_header_p);
/* estimated gain after compact is >= 5% of page */
if (page_header_p->total_free - page_header_p->cont_free >= SPAGE_DB_PAGESIZE / 20)
{
return true;
}
return false;
}