CUBRID Engine  latest
slotted_page.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * slotted_page.c - slotted page management module (at the server)
21  */
22 
23 #ident "$Id$"
24 
25 #include "config.h"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 
32 #include "slotted_page.h"
33 
34 #include "storage_common.h"
35 #include "memory_alloc.h"
36 #include "error_manager.h"
37 #include "system_parameter.h"
38 #include "memory_hash.h"
39 #include "object_representation.h"
40 #include "page_buffer.h"
41 #include "porting_inline.hpp"
42 #include "log_manager.h"
43 #include "critical_section.h"
44 #include "lock_free.h"
45 #include "mvcc.h"
46 #if defined(SERVER_MODE)
47 #include "connection_error.h"
48 #endif /* SERVER_MODE */
49 #include "dbtype.h"
50 #include "thread_entry.hpp"
52 #include "thread_manager.hpp" // for thread_get_thread_entry_info
53 
54 #if !defined(SERVER_MODE)
55 #define pthread_mutex_init(a, b)
56 #define pthread_mutex_destroy(a)
57 #define pthread_mutex_lock(a) 0
58 #define pthread_mutex_trylock(a) 0
59 #define pthread_mutex_unlock(a)
60 static int rv;
61 #endif /* not SERVER_MODE */
62 
63 #define SPAGE_SEARCH_NEXT 1
64 #define SPAGE_SEARCH_PREV -1
65 
67 
68 #define SPAGE_DB_PAGESIZE \
69  (spage_User_page_size != 0 ? assert (spage_User_page_size == db_page_size ()), spage_User_page_size : db_page_size ())
70 
71 #define SPAGE_VERIFY_HEADER(sphdr) \
72  do { \
73  assert ((sphdr) != NULL); \
74  assert ((sphdr)->total_free >= 0); \
75  assert ((sphdr)->cont_free >= 0); \
76  assert ((sphdr)->cont_free <= (sphdr)->total_free); \
77  assert ((sphdr)->offset_to_free_area < SPAGE_DB_PAGESIZE); \
78  assert ((sphdr)->num_records >= 0); \
79  assert ((sphdr)->num_slots >= 0); \
80  assert ((sphdr)->num_records <= (sphdr)->num_slots); \
81  } while (0)
82 
83 enum
84 {
85  SPAGE_EMPTY_OFFSET = 0 /* uninitialized offset */
86 };
87 
90 
92 { /* A hash entry to save space for future undos */
93  TRANID tranid; /* Transaction identifier */
94  int saved; /* Amount of space saved */
95  SPAGE_SAVE_ENTRY *next; /* Next save */
96  SPAGE_SAVE_ENTRY *prev; /* Previous save */
97  SPAGE_SAVE_ENTRY *tran_next_save; /* Next save stored by the same transaction */
98  SPAGE_SAVE_HEAD *head; /* Head of the save list */
99 };
100 
102 { /* Head of a saving space */
103  SPAGE_SAVE_HEAD *rstack; /* Pointer for retired stack of freelist */
104  SPAGE_SAVE_HEAD *next; /* Next entry in hash bucket */
105  pthread_mutex_t mutex; /* Mutex protecting "total_saved" and "first" */
106  UINT64 del_id; /* delete transaction ID (for lock free) */
107 
108  VPID vpid; /* Page and volume where the space is saved */
109  int total_saved; /* Total saved space by all transactions */
110  SPAGE_SAVE_ENTRY *first; /* First saving space entry */
111 
112  // *INDENT-OFF*
113  spage_save_head ();
114  ~spage_save_head ();
115  // *INDENT-ON*
116 };
117 
118 #define SPAGE_OVERFLOW(offset) ((int) (offset) > SPAGE_DB_PAGESIZE)
119 
120 /*
121  * Savings hash table
122  */
123 static void *spage_save_head_alloc (void);
124 static int spage_save_head_free (void *entry_p);
125 static int spage_save_head_init (void *entry_p);
126 static int spage_save_head_uninit (void *entry_p);
127 
129  /* signature of SPAGE_SAVE_HEAD */
130  offsetof (SPAGE_SAVE_HEAD, rstack),
131  offsetof (SPAGE_SAVE_HEAD, next),
132  offsetof (SPAGE_SAVE_HEAD, del_id),
133  offsetof (SPAGE_SAVE_HEAD, vpid),
134  offsetof (SPAGE_SAVE_HEAD, mutex),
135 
136  /* using mutex? */
138 
139  /* function callbacks */
147  NULL /* no inserts */
148 };
149 
150 // *INDENT-OFF*
152 // *INDENT-ON*
153 
155 
156 /* context for slotted page header scan */
159 {
162 };
163 
164 /* context for slotted page slots scan */
167 {
168  VPID vpid; /* vpid of specified page */
169  PAGE_PTR pgptr; /* The page load by pgbuf_fix */
170  SPAGE_SLOT *slot; /* Iterator about slot in the page */
171 };
172 
173 static int spage_save_space (THREAD_ENTRY * thread_p, SPAGE_HEADER * sphdr, PAGE_PTR pgptr, int save);
174 static int spage_get_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p,
175  int *other_saved_spaces);
176 static int spage_get_saved_spaces_by_other_trans (THREAD_ENTRY * thread_p, SPAGE_HEADER * sphdr, PAGE_PTR pgptr);
177 static int spage_get_total_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p);
178 static void spage_dump_saved_spaces_by_other_trans (THREAD_ENTRY * thread_p, FILE * fp, VPID * vpid);
179 static int spage_compare_slot_offset (const void *arg1, const void *arg2);
180 static bool spage_is_slotted_page_type (PAGE_TYPE ptype);
181 
182 static int spage_check_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space);
183 static void spage_set_slot (SPAGE_SLOT * slot_p, int offset, int length, INT16 type);
184 static int spage_find_empty_slot (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, int length, INT16 type, SPAGE_SLOT ** sptr,
185  int *space, PGSLOTID * slotid);
186 static int spage_find_empty_slot_at (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, PGSLOTID slotid, int length, INT16 type,
187  SPAGE_SLOT ** sptr);
188 static void spage_shift_slot_up (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p);
189 static void spage_shift_slot_down (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p);
190 static int spage_add_new_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
191  int *out_space_p);
192 static int spage_take_slot_in_use (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
193  PGSLOTID slot_id, SPAGE_SLOT * slot_p, int *out_space_p);
194 
195 static int spage_check_record_for_insert (RECDES * record_descriptor_p);
196 static int spage_insert_data (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, RECDES * recdes, void *slotptr);
197 static bool spage_is_record_located_at_end (SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p);
198 static void spage_reduce_a_slot (PAGE_PTR page_p);
199 
200 static int spage_check_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id,
201  int record_descriptor_length, SPAGE_SLOT ** out_slot_p, int *out_space_p,
202  int *out_old_waste_p, int *out_new_waste_p);
203 static int spage_check_mvcc_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id,
204  int mvcc_delete_record_length, int mvcc_insert_record_length);
205 static int spage_update_record_in_place (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p,
206  const RECDES * record_descriptor_p, int space);
207 static int spage_update_record_after_compact (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p,
208  SPAGE_SLOT * slot_p, const RECDES * record_descriptor_p, int space,
209  int old_waste, int new_waste);
210 
211 static SCAN_CODE spage_search_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p,
212  int is_peeking, int direction, bool skip_empty);
213 
214 static const char *spage_record_type_string (INT16 record_type);
215 
216 static void spage_dump_header (FILE * fp, const SPAGE_HEADER * sphdr);
217 static void spage_dump_header_to_string (char *buffer, int size, const SPAGE_HEADER * page_header_p);
218 static void spage_dump_slots (FILE * fp, const SPAGE_SLOT * sptr, PGNSLOTS nslots, unsigned short alignment);
219 static void spage_dump_record (FILE * Fp, PAGE_PTR page_p, PGSLOTID slot_id, SPAGE_SLOT * slot_p);
220 
221 static INLINE bool spage_is_unknown_slot (PGSLOTID slotid, SPAGE_HEADER * sphdr, SPAGE_SLOT * sptr)
223 static INLINE SPAGE_SLOT *spage_find_slot (PAGE_PTR pgptr, SPAGE_HEADER * sphdr, PGSLOTID slotid,
224  bool is_unknown_slot_check) __attribute__ ((ALWAYS_INLINE));
225 static INLINE int spage_find_slot_for_insert (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, RECDES * recdes,
226  PGSLOTID * slotid, void **slotptr, int *used_space)
228 static SCAN_CODE spage_get_record_data (PAGE_PTR pgptr, SPAGE_SLOT * sptr, RECDES * recdes, bool ispeeking);
229 static bool spage_has_enough_total_space (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, SPAGE_HEADER * sphdr, int space);
230 static bool spage_has_enough_contiguous_space (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, SPAGE_HEADER * sphdr,
231  int space);
232 static int spage_put_helper (THREAD_ENTRY * thread_p, PAGE_PTR pgptr, PGSLOTID slotid, int offset,
233  const RECDES * recdes, bool is_append);
234 static void spage_add_contiguous_free_space (PAGE_PTR pgptr, int space);
235 static void spage_reduce_contiguous_free_space (PAGE_PTR pgptr, int space);
237 
238 // *INDENT-OFF*
240 {
242 }
243 
245 {
247 }
248 // *INDENT-ON*
249 
250 /*
251  * spage_save_head_alloc () - callback for allocation of a SPAGE_SAVE_HEAD
252  * object
253  * returns: pointer to new SPAGE_SAVE_HEAD or NULL on error
254  */
255 static void *
257 {
258  SPAGE_SAVE_HEAD *entry_p;
259 
260  entry_p = (SPAGE_SAVE_HEAD *) malloc (sizeof (SPAGE_SAVE_HEAD));
261  if (entry_p != NULL)
262  {
263  pthread_mutex_init (&entry_p->mutex, NULL);
264  }
265 
266  return (void *) entry_p;
267 }
268 
269 /*
270  * spage_save_head_free () - callback for deallocation of a SPAGE_SAVE_HEAD
271  * returns: error code or NO_ERROR
272  * entry_p(in): entry to deallocate
273  */
274 static int
275 spage_save_head_free (void *entry_p)
276 {
277  free (entry_p);
278  return NO_ERROR;
279 }
280 
281 /*
282  * spage_save_head_init () - initialization callback for SPAGE_SAVE_HEAD
283  * returns: error code or NO_ERROR
284  * entry_p(in): entry to initialize
285  */
286 static int
287 spage_save_head_init (void *entry_p)
288 {
289  SPAGE_SAVE_HEAD *head_p = (SPAGE_SAVE_HEAD *) entry_p;
290 
291  if (head_p != NULL)
292  {
293  head_p->vpid.pageid = NULL_PAGEID;
294  head_p->vpid.volid = NULL_PAGEID;
295  head_p->first = NULL;
296  head_p->total_saved = 0;
297  }
298 
299  return NO_ERROR;
300 }
301 
302 /*
303  * spage_save_head_uninit () - uninitialization callback for SPAGE_SAVE_HEAD
304  * returns: error code or NO_ERROR
305  * entry_p(in): entry to uninitializa
306  */
307 static int
308 spage_save_head_uninit (void *entry_p)
309 {
310  SPAGE_SAVE_HEAD *head_p = (SPAGE_SAVE_HEAD *) entry_p;
311  SPAGE_SAVE_ENTRY *list, *next;
312 
313  if (head_p != NULL)
314  {
315  list = head_p->first;
316 
317  while (list)
318  {
319  next = list->next;
320  free (list);
321  list = next;
322  }
323  }
324 
325  return NO_ERROR;
326 }
327 
328 /*
329  * spage_verify_header () -
330  * return:
331  *
332  * page_p(in): Pointer to slotted page
333  */
334 STATIC_INLINE void
336 {
337  char header_info[1024];
338  SPAGE_HEADER *sphdr;
339  VPID vpid;
340 
341  sphdr = (SPAGE_HEADER *) page_p;
342  if (sphdr == NULL || sphdr->total_free < 0 || sphdr->cont_free < 0 || sphdr->cont_free > sphdr->total_free
344  || (PTR_ALIGN (page_p + sphdr->offset_to_free_area, sphdr->alignment) != page_p + sphdr->offset_to_free_area)
345  || sphdr->num_records < 0 || sphdr->num_slots < 0 || sphdr->num_records > sphdr->num_slots)
346  {
347  spage_dump_header_to_string (header_info, sizeof (header_info), sphdr);
348 
349  pgbuf_get_vpid (page_p, &vpid);
351  fileio_get_volume_label (vpid.volid, PEEK), header_info);
352  assert (false);
353  }
354 }
355 
356 /*
357  * spage_is_valid_anchor_type ()
358  * return: whether the given number represents a valid anchor type constant
359  * for a slotted page
360  *
361  * anchor_type(in): the anchor type constant
362  */
363 bool
364 spage_is_valid_anchor_type (const INT16 anchor_type)
365 {
366  assert (ANCHORED <= anchor_type && anchor_type <= UNANCHORED_KEEP_SEQUENCE);
367 
368  return (anchor_type == ANCHORED || anchor_type == ANCHORED_DONT_REUSE_SLOTS || anchor_type == UNANCHORED_ANY_SEQUENCE
369  || anchor_type == UNANCHORED_KEEP_SEQUENCE);
370 }
371 
372 /*
373  * spage_free_saved_spaces () - Release the savings of transaction
374  * return: void
375  *
376  * first_save_entry(in): first save entry to be released
377  *
378  * Note: This function could be called once a transaction has finished.
379  * This is optional, it does not need to be done.
380  */
381 void
382 spage_free_saved_spaces (THREAD_ENTRY * thread_p, void *first_save_entry)
383 {
384  SPAGE_SAVE_ENTRY *entry, *current;
386  int rv;
387 
388  assert (first_save_entry != NULL);
389 
390  entry = (SPAGE_SAVE_ENTRY *) first_save_entry;
391  while (entry != NULL)
392  {
393  /* we are about to access a lock-free pointer; make sure it's retained until we're done with it */
394  spage_Saving_hashmap.start_tran (thread_p);
395 
396  current = entry;
397  head = entry->head;
398  entry = entry->tran_next_save;
399 
400  assert (current->tranid == logtb_find_current_tranid (thread_p));
401 
402  /* since we only remove hash entries when they are empty AND each transaction uses only one thread (and thus
403  * cannot call this function concurrently), we can assume that the entry we just fetched is valid and in the hash
404  * table */
405  rv = pthread_mutex_lock (&head->mutex);
406 
407  /* mutex acquired, no need for lock-free transaction */
408  spage_Saving_hashmap.end_tran (thread_p);
409 
410  /* Delete the current node from save entry list */
411  if (current->prev == NULL)
412  {
413  head->first = current->next;
414 
415  /* There is no more entry. Delete save head and go to next entry */
416  if (head->first == NULL)
417  {
418  int success = 0;
419 
420  if (!spage_Saving_hashmap.erase_locked (thread_p, head->vpid, head))
421  {
422  /* we don't have clear operations on this hash table, this shouldn't happen */
423  pthread_mutex_unlock (&head->mutex);
424  assert_release (false);
425  return;
426  }
427 
428  free_and_init (current);
429  continue;
430  }
431  }
432  else
433  {
434  current->prev->next = current->next;
435  }
436 
437  if (current->next != NULL)
438  {
439  current->next->prev = current->prev;
440  }
441 
442  /* If there is saved space, decrease total saved space held by head */
443  if (current->saved > 0)
444  {
445  head->total_saved -= current->saved;
446 
447  /* Total saved space should be 0 or positive */
448  if (head->total_saved < 0)
449  {
450  /* defense code */
451  head->total_saved = 0;
452  }
453  }
454  free_and_init (current);
455  pthread_mutex_unlock (&head->mutex);
456  }
457 }
458 
459 /*
460  * spage_save_space () - Save some space for recovery (undo) purposes
461  * return:
462  *
463  * page_header_p(in): Pointer to header of slotted page
464  * page_p(in): Pointer to slotted page
465  * space(in): Amount of space to save
466  *
467  * Note: The current transaction saving information is kept on the page only if
468  * the page is not held by the other transaction. When a transaction
469  * has occupied the page, the following transactions which try to save
470  * some space to the page should be make a hash entry.
471  *
472  * The head of the save entries holds the total saved space of its list
473  * except the saved space stored by the page header.
474  *
475  */
476 static int
477 spage_save_space (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p, int space)
478 {
479  SPAGE_SAVE_HEAD *head_p;
480  SPAGE_SAVE_ENTRY *entry_p;
481  VPID *vpid_p;
482  TRANID tranid;
483  LOG_TDES *tdes;
484  bool found;
485 
486  assert (page_p != NULL);
487  SPAGE_VERIFY_HEADER (page_header_p);
488 
489  if (space == 0 || log_is_in_crash_recovery ())
490  {
491  return NO_ERROR;
492  }
493 
495  if (VACUUM_IS_THREAD_VACUUM_WORKER (thread_p))
496  {
497  /* Vacuum workers do not rollback their heap changes and don't need to keep track of saved space. */
498  assert (!LOG_FIND_CURRENT_TDES (thread_p)->is_under_sysop () || space < 0);
499  return NO_ERROR;
500  }
501 
502  tranid = logtb_find_current_tranid (thread_p);
503 
504  /*
505  * increase saved space when the transaction is active.
506  */
507  if (space < 0 || !logtb_is_active (thread_p, tranid))
508  {
509  return NO_ERROR;
510  }
511 
512  vpid_p = pgbuf_get_vpid_ptr (page_p);
513 
514  /* retrieve a hash entry for specified VPID */
515  (void) spage_Saving_hashmap.find_or_insert (thread_p, *vpid_p, head_p);
516  if (head_p == NULL)
517  {
518  assert (false);
519  return ER_FAILED;
520  }
521 
522  if (head_p->first == NULL)
523  {
524  entry_p = (SPAGE_SAVE_ENTRY *) malloc (sizeof (*entry_p));
525  if (entry_p == NULL)
526  {
527  pthread_mutex_unlock (&head_p->mutex);
528  return ER_FAILED;
529  }
530 
531  /*
532  * Form the head and the first entry with information of the page
533  * header, modify the header with current transaction saving, and
534  * add first entry into hash
535  */
536 
537  head_p->total_saved = space;
538  head_p->first = entry_p;
539 
540  entry_p->tranid = tranid;
541  entry_p->saved = space;
542  entry_p->next = NULL;
543  entry_p->prev = NULL;
544  entry_p->head = head_p;
545 
546  /*
547  * Add this entry to the save entry list of this transaction.
548  * It will be used to release the save entries when the transaction
549  * is completed.
550  */
551  tdes = LOG_FIND_TDES (LOG_FIND_THREAD_TRAN_INDEX (thread_p));
552  if (tdes != NULL)
553  {
554  entry_p->tran_next_save = ((SPAGE_SAVE_ENTRY *) (tdes->first_save_entry));
555  tdes->first_save_entry = (void *) entry_p;
556  }
557  else
558  {
559  entry_p->tran_next_save = NULL;
560  }
561 
562  /* all done, unlock entry */
563  pthread_mutex_unlock (&head_p->mutex);
564  return NO_ERROR;
565  }
566 
567  /*
568  * Check if the current transaction is in the list. If it is, adjust the
569  * total saved space on the head entry. otherwise, create a new entry.
570  */
571  found = false;
572  for (entry_p = head_p->first; entry_p != NULL; entry_p = entry_p->next)
573  {
574  if (tranid == entry_p->tranid)
575  {
576  found = true;
577  break;
578  }
579  }
580 
581  if (found == true)
582  {
583  head_p->total_saved += space;
584  entry_p->saved += space;
585  }
586  else
587  {
588  /* Current transaction is not in the list */
589 
590  /* Need to allocate an entry */
591  entry_p = (SPAGE_SAVE_ENTRY *) malloc (sizeof (*entry_p));
592  if (entry_p == NULL)
593  {
594  pthread_mutex_unlock (&head_p->mutex);
595  return ER_FAILED;
596  }
597 
598  head_p->total_saved += space;
599 
600  entry_p->tranid = tranid;
601  entry_p->saved = space;
602  entry_p->head = head_p;
603  entry_p->prev = NULL;
604  entry_p->next = head_p->first;
605 
606  if (head_p->first != NULL)
607  {
608  head_p->first->prev = entry_p;
609  }
610 
611  head_p->first = entry_p;
612 
613  /*
614  * Add this entry to the save entry list of this transaction.
615  * It will be used to release the save entries when the transaction
616  * is completed.
617  */
618  tdes = LOG_FIND_TDES (LOG_FIND_THREAD_TRAN_INDEX (thread_p));
619  if (tdes != NULL)
620  {
621  entry_p->tran_next_save = (SPAGE_SAVE_ENTRY *) (tdes->first_save_entry);
622  tdes->first_save_entry = (void *) entry_p;
623  }
624  else
625  {
626  assert (false);
627  entry_p->tran_next_save = NULL;
628  }
629 
630  }
631  assert (head_p->total_saved >= 0);
632  assert (entry_p->saved >= 0);
633 
634  pthread_mutex_unlock (&head_p->mutex);
635 
636  return NO_ERROR;
637 }
638 
639 /*
640  * spage_get_saved_spaces_by_other_trans () - Find the total saved space by
641  * other transactions
642  * return:
643  *
644  * page_header_p(in): Pointer to header of slotted page
645  * page_p(in): Pointer to slotted page
646  */
647 static int
649 {
650  int saved_by_other_trans = 0;
651 
652  spage_get_saved_spaces (thread_p, page_header_p, page_p, &saved_by_other_trans);
653  assert (saved_by_other_trans >= 0);
654 
655  return saved_by_other_trans;
656 }
657 
658 /*
659  * spage_get_total_saved_spaces () - Find the total saved space
660  * return:
661  *
662  * page_header_p(in): Pointer to header of slotted page
663  * page_p(in): Pointer to slotted page
664  */
665 static int
666 spage_get_total_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p)
667 {
668  int total_saved = 0;
669  int dummy;
670 
671  if (page_header_p->is_saving)
672  {
673  total_saved = spage_get_saved_spaces (thread_p, page_header_p, page_p, &dummy);
674  }
675 
676  return total_saved;
677 }
678 
679 /*
680  * spage_get_saved_spaces () - Find the total saved space and the space by
681  other transactions
682  * return:
683  *
684  * page_header_p(in): Pointer to header of slotted page
685  * page_p(in): Pointer to slotted page
686  * saved_by_other_trans(in/out) : The other transaction's saved space will
687  * be returned
688  */
689 static int
690 spage_get_saved_spaces (THREAD_ENTRY * thread_p, SPAGE_HEADER * page_header_p, PAGE_PTR page_p,
691  int *saved_by_other_trans)
692 {
693  SPAGE_SAVE_HEAD *head_p;
694  SPAGE_SAVE_ENTRY *entry_p;
695  VPID *vpid_p;
696  TRANID tranid;
697  int total_saved, my_saved_space;
698 
699  assert (page_p != NULL);
700  SPAGE_VERIFY_HEADER (page_header_p);
701 
702  /*
703  * If we are recovering, no other transaction should exist.
704  */
706  {
707  if (saved_by_other_trans != NULL)
708  {
709  *saved_by_other_trans = 0;
710  }
711  return 0;
712  }
713 
714  tranid = logtb_find_current_tranid (thread_p);
715  vpid_p = pgbuf_get_vpid_ptr (page_p);
716 
717  my_saved_space = 0;
718  total_saved = 0;
719 
720  /*
721  * Get the saved space held by the head of the save entries. This is
722  * the aggregate value of spaces saved on all entries.
723  */
724  head_p = spage_Saving_hashmap.find (thread_p, *vpid_p);
725  if (head_p != NULL)
726  {
727  entry_p = head_p->first;
728  while (entry_p != NULL)
729  {
730  if (entry_p->tranid == tranid)
731  {
732  my_saved_space += entry_p->saved;
733  break;
734  }
735  entry_p = entry_p->next;
736  }
737  total_saved += head_p->total_saved;
738 
739  /* done with entry, unlock mutex */
740  pthread_mutex_unlock (&head_p->mutex);
741  }
742 
743  /* Saved space should be 0 or positive. */
744  assert (my_saved_space >= 0);
745  assert (total_saved >= 0);
746  assert (total_saved >= my_saved_space);
747 
748  if (saved_by_other_trans != NULL)
749  {
750  *saved_by_other_trans = total_saved - my_saved_space;
751  assert (*saved_by_other_trans >= 0);
752  }
753 
754  return total_saved;
755 }
756 
757 /*
758  * spage_dump_saved_spaces_by_other_trans () - Dump the savings associated with
759  * the given page that are part of
760  * the hash table
761  * return: void
762  *
763  * fp(in/out):
764  * vpid_p(in): Volume and page identifier
765  */
766 static void
768 {
769  SPAGE_SAVE_ENTRY *entry_p;
770  SPAGE_SAVE_HEAD *head_p;
771 
772  head_p = spage_Saving_hashmap.find (thread_p, *vpid_p);
773  if (head_p != NULL)
774  {
775  fprintf (fp, "Other savings of VPID = %d|%d total_saved = %d\n", head_p->vpid.volid, head_p->vpid.pageid,
776  head_p->total_saved);
777 
778  /* Go over the linked list of entries */
779  entry_p = head_p->first;
780 
781  while (entry_p != NULL)
782  {
783  fprintf (fp, " Tranid = %d save = %d\n", entry_p->tranid, entry_p->saved);
784  entry_p = entry_p->next;
785  }
786 
787  /* unlock entry */
788  pthread_mutex_unlock (&head_p->mutex);
789  }
790 }
791 
792 /*
793  * spage_boot () - Initialize the slotted page module. The save_space hash table
794  * is initialized
795  * return:
796  */
797 void
799 {
800  assert (sizeof (SPAGE_HEADER) % DOUBLE_ALIGNMENT == 0);
801  assert (sizeof (SPAGE_SLOT) == INT_ALIGNMENT);
802 
804 
805  spage_Saving_hashmap.init (spage_saving_Ts, THREAD_TS_SPAGE_SAVING, 4547, 100, 100, spage_Saving_entry_descriptor);
806 }
807 
808 /*
809  * spage_finalize () - Terminate the slotted page module
810  * return: void
811  *
812  * Note: Any memory allocated for the page space saving hash table is
813  * deallocated.
814  */
815 void
817 {
818  /* destroy everything */
819  spage_Saving_hashmap.destroy ();
820 }
821 
822 /*
823  * spage_slot_size () - Find the overhead used to store one slotted record
824  * return: overhead of slot
825  */
826 int
828 {
829  return sizeof (SPAGE_SLOT);
830 }
831 
832 /*
833  * spage_header_size () - Find the overhead used by the page header
834  * return: overhead of slot
835  */
836 int
838 {
839  return sizeof (SPAGE_HEADER);
840 }
841 
842 /*
843  * spage_max_record_size () - Find the maximum record length that can be stored in
844  * a slotted page
845  * return: Max length for a new record
846  */
847 int
849 {
850  return SPAGE_DB_PAGESIZE - sizeof (SPAGE_HEADER) - sizeof (SPAGE_SLOT);
851 }
852 
853 /*
854  * spage_number_of_records () - Return the total number of records in the slotted page
855  * return: Number of records (PGNSLOTS)
856  *
857  * page_p(in): Pointer to slotted page
858  */
859 PGNSLOTS
861 {
862  SPAGE_HEADER *page_header_p;
863 
864  assert (page_p != NULL);
865 
866  page_header_p = (SPAGE_HEADER *) page_p;
867  SPAGE_VERIFY_HEADER (page_header_p);
868 
869  return page_header_p->num_records;
870 }
871 
872 /*
873  * spage_number_of_slots () - Return the total number of slots in the slotted page
874  * return: Number of slots (PGNSLOTS)
875  *
876  * page_p(in): Pointer to slotted page
877  */
878 PGNSLOTS
880 {
881  SPAGE_HEADER *page_header_p;
882 
883  assert (page_p != NULL);
884 
885  page_header_p = (SPAGE_HEADER *) page_p;
886  SPAGE_VERIFY_HEADER (page_header_p);
887 
888  return page_header_p->num_slots;
889 }
890 
891 /*
892  * spage_get_free_space () - Returns total free area
893  * return: Total free space
894  *
895  * page_p(in): Pointer to slotted page
896  */
897 int
899 {
900  SPAGE_HEADER *page_header_p;
901  int free_space;
902 
903  assert (page_p != NULL);
904 
905  page_header_p = (SPAGE_HEADER *) page_p;
906  SPAGE_VERIFY_HEADER (page_header_p);
907 
908  free_space = (page_header_p->total_free - spage_get_total_saved_spaces (thread_p, page_header_p, page_p));
909  if (free_space < 0)
910  {
911  free_space = 0;
912  }
913 
914  return free_space;
915 }
916 
917 /*
918  * spage_get_free_space_without_saving () - Returns total free area without saving
919  * return: Total free space
920  *
921  * page_p(in): Pointer to slotted page
922  * need_update(out): Value of need_update_best_hint
923  */
924 int
925 spage_get_free_space_without_saving (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool * need_update)
926 {
927  SPAGE_HEADER *page_header_p;
928  int free_space;
929 
930  assert (page_p != NULL);
931 
932  page_header_p = (SPAGE_HEADER *) page_p;
933  SPAGE_VERIFY_HEADER (page_header_p);
934 
935  if (need_update != NULL)
936  {
937  *need_update = page_header_p->need_update_best_hint;
938  }
939 
940  free_space = page_header_p->total_free;
941  if (free_space < 0)
942  { /* defense code */
943  assert (false);
945  free_space = 0;
946  }
947 
948  return free_space;
949 }
950 
951 /*
952  * spage_set_need_update_best_hint () - Set need_update_best_hint on slotted page header
953  * return: void
954  *
955  * page_p(in): Pointer to slotted page
956  * need_update(in): Value of need_update_best_hint
957  *
958  * NOTE: We will set this value as TRUE when we failed updating best page hints
959  * on heap header. See heap_stats_update function.
960  */
961 void
962 spage_set_need_update_best_hint (THREAD_ENTRY * thread_p, PAGE_PTR page_p, bool need_update)
963 {
964  SPAGE_HEADER *page_header_p;
965 
966  assert (page_p != NULL);
967 
968  page_header_p = (SPAGE_HEADER *) page_p;
969  SPAGE_VERIFY_HEADER (page_header_p);
970 
971  page_header_p->need_update_best_hint = need_update;
972 }
973 
974 /*
975  * spage_max_space_for_new_record () - Find the maximum free space for a new
976  * insertion
977  * return: Maximum free length for an insertion
978  * page_p(in): Pointer to slotted page
979  *
980  * Note: The function subtract any pointer array information that may be
981  * needed for a new insertion.
982  */
983 int
985 {
986  SPAGE_HEADER *page_header_p;
987  int total_free;
988 
989  assert (page_p != NULL);
990 
991  page_header_p = (SPAGE_HEADER *) page_p;
992  SPAGE_VERIFY_HEADER (page_header_p);
993 
994  total_free = (page_header_p->total_free - spage_get_total_saved_spaces (thread_p, page_header_p, page_p));
995 
996  if ((page_header_p->anchor_type != ANCHORED_DONT_REUSE_SLOTS)
997  && (page_header_p->num_slots > page_header_p->num_records))
998  {
999  if (total_free < 0)
1000  {
1001  total_free = 0;
1002  }
1003  }
1004  else
1005  {
1006  total_free -= SSIZEOF (SPAGE_SLOT);
1007  if (total_free < SSIZEOF (SPAGE_SLOT))
1008  {
1009  total_free = 0;
1010  }
1011  }
1012 
1013  total_free = DB_ALIGN_BELOW (total_free, page_header_p->alignment);
1014  if (total_free < 0)
1015  {
1016  total_free = 0;
1017  }
1018 
1019  return total_free;
1020 }
1021 
1022 /*
1023  * spage_collect_statistics () - Collect statistical information of the page
1024  * return: none
1025  *
1026  * page_p(in): Pointer to slotted page
1027  * npages(out): the number of pages
1028  * nrecords(out): the number of records
1029  * rec_length(out): total length of records
1030  */
1031 void
1032 spage_collect_statistics (PAGE_PTR page_p, int *npages, int *nrecords, int *rec_length)
1033 {
1034  SPAGE_HEADER *page_header_p;
1035  SPAGE_SLOT *slot_p;
1036  PGNSLOTS i, last_slot_id;
1037  int pages, records, length;
1038 
1039  assert (page_p != NULL);
1040  assert (npages != NULL && nrecords != NULL && rec_length != NULL);
1041 
1042  page_header_p = (SPAGE_HEADER *) page_p;
1043  SPAGE_VERIFY_HEADER (page_header_p);
1044 
1045  last_slot_id = page_header_p->num_slots - 1;
1046  slot_p = spage_find_slot (page_p, page_header_p, last_slot_id, false);
1047 
1048  pages = 1;
1049  records = 0;
1050  length = 0;
1051 
1052  for (i = last_slot_id; i > 0; slot_p++, i--)
1053  {
1054  if (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET)
1055  {
1056  continue;
1057  }
1058 
1059  if (slot_p->record_type == REC_BIGONE)
1060  {
1061  pages += 2;
1062  length += (SPAGE_DB_PAGESIZE * 2); /* Assume two pages */
1063  }
1064 
1065  if (slot_p->record_type != REC_NEWHOME)
1066  {
1067  records++;
1068  }
1069 
1070  if (slot_p->record_type == REC_HOME || slot_p->record_type == REC_NEWHOME)
1071  {
1072  length += slot_p->record_length;
1073  }
1074  }
1075 
1076  *npages = pages;
1077  *nrecords = records;
1078  *rec_length = length;
1079 }
1080 
1081 /*
1082  * spage_initialize () - Initialize a slotted page
1083  * return: void
1084  *
1085  * page_p(in): Pointer to slotted page
1086  * slots_type(in): Flag which indicates the type of slots
1087  * alignment(in): page alignment type
1088  * safeguard_rvspace(in): Save space during updates. for transaction recovery
1089  *
1090  * Note: A slotted page must be initialized before records are inserted on the
1091  * page. The alignment indicates the valid offset where the records
1092  * should be stored. This is a requirement for peeking records on pages
1093  * according to alignment restrictions.
1094  * A slotted page can optionally be initialized with recovery safeguard
1095  * space in mind. In this case when records are removed or shrunk, the
1096  * space is saved for possible undoes.
1097  */
1098 void
1099 spage_initialize (THREAD_ENTRY * thread_p, PAGE_PTR page_p, INT16 slot_type, unsigned short alignment, bool is_saving)
1100 {
1101  SPAGE_HEADER *page_header_p;
1102 
1103  assert (page_p != NULL);
1104  assert (spage_is_valid_anchor_type (slot_type));
1105  assert (alignment == CHAR_ALIGNMENT || alignment == SHORT_ALIGNMENT || alignment == INT_ALIGNMENT
1106  || alignment == LONG_ALIGNMENT || alignment == FLOAT_ALIGNMENT || alignment == DOUBLE_ALIGNMENT);
1107 
1108  page_header_p = (SPAGE_HEADER *) page_p;
1109 
1110  page_header_p->num_slots = 0;
1111  page_header_p->num_records = 0;
1112  page_header_p->is_saving = is_saving;
1113  page_header_p->flags = SPAGE_HEADER_FLAG_NONE;
1114  page_header_p->need_update_best_hint = 0;
1115  page_header_p->reserved_bits = 0;
1116  page_header_p->reserved1 = 0;
1117 
1118  page_header_p->anchor_type = slot_type;
1119  page_header_p->total_free = DB_ALIGN (SPAGE_DB_PAGESIZE - sizeof (SPAGE_HEADER), alignment);
1120 
1121  page_header_p->cont_free = page_header_p->total_free;
1122  page_header_p->offset_to_free_area = DB_ALIGN (sizeof (SPAGE_HEADER), alignment);
1123 
1124  page_header_p->alignment = alignment;
1125  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
1126 }
1127 
1128 /*
1129  * spage_compare_slot_offset () - Compare the location (offset) of slots
1130  * return: s1 - s2
1131  * s1(in): slot 1
1132  * s2(in): slot 2
1133  */
1134 static int
1135 spage_compare_slot_offset (const void *arg1, const void *arg2)
1136 {
1137  SPAGE_SLOT **s1, **s2;
1138 
1139  assert (arg1 != NULL);
1140  assert (arg2 != NULL);
1141 
1142  s1 = (SPAGE_SLOT **) arg1;
1143  s2 = (SPAGE_SLOT **) arg2;
1144 
1145  return ((*s1)->offset_to_record - (*s2)->offset_to_record);
1146 }
1147 
1148 /*
1149  * spage_is_slotted_page_type () - Whether or not a slotted page type
1150  * return: yes - true, no - false
1151  *
1152  * ptype(in): page type
1153  */
1154 static bool
1156 {
1157  switch (ptype)
1158  {
1159  case PAGE_HEAP:
1160  case PAGE_BTREE:
1161  case PAGE_EHASH:
1162  case PAGE_CATALOG:
1163  return true;
1164 
1165  default:
1166  return false;
1167  }
1168 }
1169 
1170 /*
1171  * spage_compact () - Compact an slotted page
1172  * return:
1173  *
1174  * page_p(in): Pointer to slotted page
1175  *
1176  * Note: Only the records are compacted, the slots are not compacted.
1177  */
1178 int
1180 {
1181  SPAGE_HEADER *page_header_p;
1182  SPAGE_SLOT *slot_p;
1183  SPAGE_SLOT **slot_array = NULL;
1184  int to_offset;
1185  int i, j;
1186  PAGE_TYPE ptype;
1187 
1188  assert (page_p != NULL);
1189 
1190  ptype = pgbuf_get_page_ptype (thread_p, page_p);
1192 
1193  page_header_p = (SPAGE_HEADER *) page_p;
1194  spage_verify_header (page_p);
1195 
1196  if (page_header_p->num_records > 0)
1197  {
1198  slot_array = (SPAGE_SLOT **) calloc ((unsigned int) (page_header_p->num_slots), sizeof (SPAGE_SLOT *));
1199  if (slot_array == NULL)
1200  {
1202  (page_header_p->num_slots) * sizeof (SPAGE_SLOT *));
1203  return ER_FAILED;
1204  }
1205 
1206  /* Create an array of sorted offsets... actually pointers to slots */
1207 
1208  slot_p = spage_find_slot (page_p, page_header_p, 0, false);
1209  for (j = 0, i = 0; i < page_header_p->num_slots; slot_p--, i++)
1210  {
1211  if (slot_p->record_type > REC_4BIT_USED_TYPE_MAX)
1212  {
1214  assert_release (false);
1215  return ER_FAILED;
1216  }
1217  if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
1218  {
1219  slot_array[j++] = slot_p;
1220  }
1221  }
1222 
1223  if (page_header_p->num_records != j)
1224  {
1225  VPID vpid;
1226 
1227  pgbuf_get_vpid (page_p, &vpid);
1229  fileio_get_volume_label (vpid.volid, PEEK), j, page_header_p->num_records);
1230 
1231  assert (false);
1232 
1233  free_and_init (slot_array);
1234 
1235  /* will exit here */
1237  return ER_FAILED;
1238  }
1239 
1240  qsort ((void *) slot_array, page_header_p->num_records, sizeof (SPAGE_SLOT *), spage_compare_slot_offset);
1241 
1242  /* Now start compacting the page */
1243  to_offset = sizeof (SPAGE_HEADER);
1244  for (i = 0; i < page_header_p->num_records; i++)
1245  {
1246  /* Make sure that the offset is aligned */
1247  to_offset = DB_ALIGN (to_offset, page_header_p->alignment);
1248  if (to_offset == slot_array[i]->offset_to_record)
1249  {
1250  /* Record slot is already in place */
1251  to_offset += slot_array[i]->record_length;
1252  }
1253  else
1254  {
1255  /* Move the record */
1256  if ((unsigned int) to_offset + slot_array[i]->record_length > (unsigned int) SPAGE_DB_PAGESIZE)
1257  {
1259  assert_release (false);
1260  return ER_FAILED;
1261  }
1262  memmove ((char *) page_p + to_offset, (char *) page_p + slot_array[i]->offset_to_record,
1263  slot_array[i]->record_length);
1264  slot_array[i]->offset_to_record = to_offset;
1265  ASSERT_ALIGN ((char *) page_p + slot_array[i]->offset_to_record, page_header_p->alignment);
1266  to_offset += slot_array[i]->record_length;
1267  }
1268  }
1269  free_and_init (slot_array);
1270  }
1271  else
1272  {
1273  to_offset = sizeof (SPAGE_HEADER);
1274  }
1275 
1276  /* Make sure that the next inserted record will be aligned */
1277  to_offset = DB_ALIGN (to_offset, page_header_p->alignment);
1278  page_header_p->total_free = (SPAGE_DB_PAGESIZE - to_offset - (page_header_p->num_slots * sizeof (SPAGE_SLOT)));
1279  page_header_p->cont_free = page_header_p->total_free;
1280 
1281  page_header_p->offset_to_free_area = to_offset;
1282  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
1283 
1284  spage_verify_header (page_p);
1285 
1286  /* The page is set dirty somewhere else */
1287  return NO_ERROR;
1288 }
1289 
1290 /*
1291  * spage_find_free_slot () -
1292  * return: void
1293  *
1294  * page_p(in): Pointer to slotted page
1295  * out_slot_p(out):
1296  * start_slot(in):
1297  */
1298 PGSLOTID
1299 spage_find_free_slot (PAGE_PTR page_p, SPAGE_SLOT ** out_slot_p, PGSLOTID start_slot)
1300 {
1301  PGSLOTID slot_id;
1302  SPAGE_SLOT *slot_p;
1303  SPAGE_HEADER *page_header_p;
1304 
1305  page_header_p = (SPAGE_HEADER *) page_p;
1306  SPAGE_VERIFY_HEADER (page_header_p);
1307 
1308  slot_p = spage_find_slot (page_p, page_header_p, 0, false);
1309  assert (slot_p != NULL);
1310 
1311  if (page_header_p->num_slots == page_header_p->num_records)
1312  {
1313  slot_id = page_header_p->num_slots;
1314  slot_p -= slot_id;
1315  }
1316  else
1317  {
1318  for (slot_id = start_slot, slot_p -= slot_id; slot_id < page_header_p->num_slots; slot_p--, slot_id++)
1319  {
1320  if (slot_p->record_type == REC_DELETED_WILL_REUSE)
1321  {
1322  break; /* found free slot */
1323  }
1324  }
1325  }
1326 
1327  if (out_slot_p != NULL)
1328  {
1329  *out_slot_p = slot_p;
1330  }
1331 
1332  if (spage_find_slot (page_p, page_header_p, slot_id, false) != slot_p)
1333  {
1334  assert (false);
1335 
1337  return SP_ERROR;
1338  }
1339 
1340  return slot_id;
1341 }
1342 
1343 /*
1344  * spage_check_space () -
1345  * return: void
1346  *
1347  * page_p(in): Pointer to slotted page
1348  * page_header_p(in):
1349  * space(in):
1350  */
1351 static int
1352 spage_check_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space)
1353 {
1354  if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
1355  {
1356  return SP_DOESNT_FIT;
1357  }
1358  else if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, space) == false)
1359  {
1360  return SP_ERROR;
1361  }
1362 
1363  return SP_SUCCESS;
1364 }
1365 
1366 /*
1367  * spage_set_slot () -
1368  * return:
1369  *
1370  * slot_p(in): Pointer to slotted page array pointer
1371  * offset(in):
1372  * length(in):
1373  * type(in):
1374  */
1375 static void
1376 spage_set_slot (SPAGE_SLOT * slot_p, int offset, int length, INT16 type)
1377 {
1378  assert (REC_UNKNOWN <= type && type <= REC_4BIT_USED_TYPE_MAX);
1379 
1380  slot_p->offset_to_record = offset;
1381  slot_p->record_length = length;
1382  slot_p->record_type = type;
1383 }
1384 
1385 /*
1386  * spage_find_empty_slot () - Find a free area/slot where a record of
1387  * the given length can be inserted onto the given slotted page
1388  * return: either SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
1389  *
1390  * page_p(in): Pointer to slotted page
1391  * record_length(in): Length of area/record
1392  * record_type(in): Type of record to be inserted
1393  * out_slot_p(out): Pointer to slotted page array pointer
1394  * out_space_p(out): Space used/defined
1395  * out_slot_id_p(out): Allocated slot or NULL_SLOTID
1396  *
1397  * Note: If there is not enough space on the page, an error condition is
1398  * returned and out_slot_id_p is set to NULL_SLOTID.
1399  */
1400 static int
1401 spage_find_empty_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, int record_length, INT16 record_type,
1402  SPAGE_SLOT ** out_slot_p, int *out_space_p, PGSLOTID * out_slot_id_p)
1403 {
1404  SPAGE_HEADER *page_header_p;
1405  SPAGE_SLOT *slot_p;
1406  PGSLOTID slot_id;
1407  int waste, space, status;
1408 
1409  assert (page_p != NULL);
1410  assert (out_slot_p != NULL);
1411  assert (out_space_p != NULL);
1412  assert (out_slot_id_p != NULL);
1413 
1414  *out_slot_p = NULL;
1415  *out_space_p = 0;
1416  *out_slot_id_p = NULL_SLOTID;
1417 
1418  page_header_p = (SPAGE_HEADER *) page_p;
1419  SPAGE_VERIFY_HEADER (page_header_p);
1420 
1421  /* Calculate the wasted space that this record will introduce. We need to take in consideration the wasted space when
1422  * there is space saved */
1423  waste = DB_WASTED_ALIGN (record_length, page_header_p->alignment);
1424  space = record_length + waste;
1425 
1426  /* Quickly check for available space. We may need to check again if a slot is created (instead of reused) */
1427  if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
1428  {
1429  return SP_DOESNT_FIT;
1430  }
1431 
1432  /* Find a free slot. Try to reuse an unused slotid, instead of allocating a new one */
1433  slot_id = spage_find_free_slot (page_p, &slot_p, 0);
1434  if (slot_id == SP_ERROR)
1435  {
1436  return SP_ERROR; /* this will not happen */
1437  }
1438 
1439  /* Make sure that there is enough space for the record and the slot */
1440 
1441  if (slot_id > page_header_p->num_slots)
1442  { /* defense code */
1443  assert (false);
1445  return SP_ERROR;
1446  }
1447 
1448  if (slot_id == page_header_p->num_slots)
1449  {
1450  /* We are allocating a new slotid. Check for available space again */
1451  space += sizeof (SPAGE_SLOT);
1452 
1453  status = spage_check_space (thread_p, page_p, page_header_p, space);
1454  if (status != SP_SUCCESS)
1455  {
1456  return status;
1457  }
1458 
1459  /* Adjust the number of slots */
1460  page_header_p->num_slots++;
1461  }
1462  else
1463  {
1464  /* We already know that there is total space available since the slot is reused and the space was checked above */
1465  if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, space) == false)
1466  {
1467  return SP_ERROR;
1468  }
1469  }
1470 
1471  /* Now separate an empty area for the record */
1472  spage_set_slot (slot_p, page_header_p->offset_to_free_area, record_length, record_type);
1473 
1474  /* Adjust the header */
1475  page_header_p->num_records++;
1476  page_header_p->total_free -= space;
1477  page_header_p->cont_free -= space;
1478  page_header_p->offset_to_free_area += (record_length + waste);
1479 
1480  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
1481 
1482  spage_verify_header (page_p);
1483 
1484  *out_slot_p = slot_p;
1485  *out_space_p = space;
1486  *out_slot_id_p = slot_id;
1487 
1488  /* The page is set dirty somewhere else */
1489  return SP_SUCCESS;
1490 }
1491 
1492 /*
1493  * spage_shift_slot_up() -
1494  * return:
1495  *
1496  * page_p(in): Pointer to slotted page
1497  * page_header_p(in): Pointer to header of slotted page
1498  * slot_p(in/out): Pointer to slotted page pointer array
1499  */
1500 static void
1501 spage_shift_slot_up (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
1502 {
1503  SPAGE_SLOT *last_slot_p;
1504 
1505  assert (page_p != NULL);
1506  assert (slot_p != NULL);
1507  SPAGE_VERIFY_HEADER (page_header_p);
1508 
1509  last_slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots, false);
1510 
1511  if (page_header_p->anchor_type == UNANCHORED_ANY_SEQUENCE)
1512  {
1513  spage_set_slot (last_slot_p, slot_p->offset_to_record, slot_p->record_length, slot_p->record_type);
1514  }
1515  else
1516  {
1517  for (; last_slot_p < slot_p; last_slot_p++)
1518  {
1519  spage_set_slot (last_slot_p, (last_slot_p + 1)->offset_to_record, (last_slot_p + 1)->record_length,
1520  (last_slot_p + 1)->record_type);
1521  }
1522  assert (last_slot_p == slot_p);
1523  }
1524 
1526 
1527  SPAGE_VERIFY_HEADER (page_header_p);
1528 }
1529 
1530 /*
1531  * spage_shift_slot_down () -
1532  * return:
1533  *
1534  * page_p(in): Pointer to slotted page
1535  * page_header_p(in): Pointer to header of slotted page
1536  * slot_p(in/out): Pointer to slotted page pointer array
1537  */
1538 static void
1539 spage_shift_slot_down (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
1540 {
1541  SPAGE_SLOT *last_slot_p;
1542 
1543  assert (page_p != NULL);
1544  assert (slot_p != NULL);
1545  SPAGE_VERIFY_HEADER (page_header_p);
1546 
1547  last_slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots - 1, false);
1548 
1549  if (page_header_p->anchor_type == UNANCHORED_ANY_SEQUENCE)
1550  {
1551  spage_set_slot (slot_p, last_slot_p->offset_to_record, last_slot_p->record_length, last_slot_p->record_type);
1552  }
1553  else
1554  {
1555  for (; slot_p > last_slot_p; slot_p--)
1556  {
1557  spage_set_slot (slot_p, (slot_p - 1)->offset_to_record, (slot_p - 1)->record_length,
1558  (slot_p - 1)->record_type);
1559  }
1560  }
1561 
1562  spage_set_slot (last_slot_p, SPAGE_EMPTY_OFFSET, 0, REC_UNKNOWN);
1563 
1564  SPAGE_VERIFY_HEADER (page_header_p);
1565 }
1566 
1567 /*
1568  * spage_add_new_slot () -
1569  * return:
1570  *
1571  * page_p(in): Pointer to slotted page
1572  * page_header_p(in): Pointer to header of slotted page
1573  * out_space_p(in/out):
1574  */
1575 static int
1576 spage_add_new_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int *out_space_p)
1577 {
1578  SPAGE_SLOT *last_slot_p;
1579  int status;
1580 
1581  /*
1582  * New one slot is are being allocated.
1583  */
1584  SPAGE_VERIFY_HEADER (page_header_p);
1585 
1586  *out_space_p += sizeof (SPAGE_SLOT);
1587 
1588  status = spage_check_space (thread_p, page_p, page_header_p, *out_space_p);
1589  if (status != SP_SUCCESS)
1590  {
1591  return status;
1592  }
1593 
1594  /* Adjust the space for creation of new slot. The space for the record is adjusted later on */
1595  page_header_p->num_slots++;
1596 
1597  last_slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots - 1, false);
1598  spage_set_slot (last_slot_p, SPAGE_EMPTY_OFFSET, 0, REC_UNKNOWN);
1599 
1600  spage_verify_header (page_p);
1601 
1602  return SP_SUCCESS;
1603 }
1604 
1605 /*
1606  * spage_take_slot_in_use () -
1607  * return:
1608  *
1609  * page_p(in): Pointer to slotted page
1610  * page_header_p(in): Pointer to header of slotted page
1611  * slot_id(in):
1612  * slot_p(in/out):
1613  * out_space_p(in/out):
1614  */
1615 static int
1616 spage_take_slot_in_use (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, PGSLOTID slot_id,
1617  SPAGE_SLOT * slot_p, int *out_space_p)
1618 {
1619  int status;
1620 
1621  SPAGE_VERIFY_HEADER (page_header_p);
1622 
1623  /*
1624  * An already defined slot. The slotid can be used in the following
1625  * cases:
1626  * 1) The slot is marked as deleted. (Reuse)
1627  * 2) The type of slotted page is unanchored and there is space to define
1628  * a new slot for the shift operation
1629  */
1630  if (slot_p->record_type == REC_DELETED_WILL_REUSE)
1631  {
1632  /* Make sure that there is enough space for the record. There is not need for slot space. */
1633  status = spage_check_space (thread_p, page_p, page_header_p, *out_space_p);
1634  if (status != SP_SUCCESS)
1635  {
1636  return status;
1637  }
1638  }
1639  else
1640  {
1641  /* Slot is in use. The pointer array must be shifted (i.e., addresses of records are modified). */
1642  if (page_header_p->anchor_type == ANCHORED || page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS)
1643  {
1644  assert (false);
1646  pgbuf_get_volume_label (page_p));
1647  return SP_ERROR;
1648  }
1649 
1650  /* Make sure that there is enough space for the record and the slot */
1651 
1652  *out_space_p += sizeof (SPAGE_SLOT);
1653  status = spage_check_space (thread_p, page_p, page_header_p, *out_space_p);
1654  if (status != SP_SUCCESS)
1655  {
1656  return status;
1657  }
1658 
1659  spage_shift_slot_up (page_p, page_header_p, slot_p);
1660  /* Adjust the header for the newly created slot */
1661  page_header_p->num_slots += 1;
1662  }
1663 
1664  spage_verify_header (page_p);
1665 
1666  return SP_SUCCESS;
1667 }
1668 
1669 /*
1670  * spage_find_empty_slot_at () - Find a free area where a record of
1671  * the given length can be inserted onto the given slotted page
1672  * return: either SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
1673  *
1674  * page_p(in): Pointer to slotted page
1675  * slot_id(in): Requested slot_id
1676  * record_length(in): Length of area/record
1677  * record_type(in): Type of record to be inserted
1678  * out_slot_p(out): Pointer to slotted page array pointer
1679  *
1680  */
1681 static int
1682 spage_find_empty_slot_at (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_length,
1683  INT16 record_type, SPAGE_SLOT ** out_slot_p)
1684 {
1685  SPAGE_HEADER *page_header_p;
1686  SPAGE_SLOT *slot_p;
1687  int waste, space, status;
1688 
1689  assert (page_p != NULL);
1690  assert (out_slot_p != NULL);
1691 
1692  *out_slot_p = NULL;
1693 
1694  page_header_p = (SPAGE_HEADER *) page_p;
1695  SPAGE_VERIFY_HEADER (page_header_p);
1696 
1697  slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
1698 
1699  /* Calculate the wasted space that this record will introduce. We need to take in consideration the wasted space when
1700  * there is space saved */
1701  waste = DB_WASTED_ALIGN (record_length, page_header_p->alignment);
1702  space = record_length + waste;
1703 
1704  if (slot_id > page_header_p->num_slots)
1705  { /* defense code */
1706  assert (false);
1708  return SP_ERROR;
1709  }
1710 
1711  if (slot_id == page_header_p->num_slots)
1712  {
1713  assert (log_is_in_crash_recovery () || !(page_header_p->is_saving && !logtb_is_current_active (thread_p)));
1714 
1715  status = spage_add_new_slot (thread_p, page_p, page_header_p, &space);
1716  }
1717  else
1718  {
1719  status = spage_take_slot_in_use (thread_p, page_p, page_header_p, slot_id, slot_p, &space);
1720  }
1721 
1722  if (status != SP_SUCCESS)
1723  {
1724  return status;
1725  }
1726 
1727  /* Now separate an empty area for the record */
1728  spage_set_slot (slot_p, page_header_p->offset_to_free_area, record_length, record_type);
1729 
1730  /* Adjust the header */
1731  page_header_p->num_records++;
1732  page_header_p->total_free -= space;
1733  page_header_p->cont_free -= space;
1734  page_header_p->offset_to_free_area += (record_length + waste);
1735 
1736  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
1737 
1738  spage_verify_header (page_p);
1739 
1740  *out_slot_p = slot_p;
1741 
1742  /* The page is set dirty somewhere else */
1743  return SP_SUCCESS;
1744 }
1745 
1746 /*
1747  * spage_check_record_for_insert () -
1748  * return:
1749  *
1750  * record_descriptor_p(in):
1751  */
1752 static int
1753 spage_check_record_for_insert (RECDES * record_descriptor_p)
1754 {
1755  if (record_descriptor_p->length > spage_max_record_size ())
1756  {
1757  return SP_DOESNT_FIT;
1758  }
1759 
1760  if (record_descriptor_p->type == REC_MARKDELETED || record_descriptor_p->type == REC_DELETED_WILL_REUSE)
1761  {
1762  record_descriptor_p->type = REC_HOME;
1763  }
1764 
1765  return SP_SUCCESS;
1766 }
1767 
1768 /*
1769  * spage_insert () - Insert a record
1770  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
1771  *
1772  * page_p(in): Pointer to slotted page
1773  * record_descriptor_p(in): Pointer to a record descriptor
1774  * out_slot_id_p(out): Slot identifier
1775  */
1776 int
1777 spage_insert (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_descriptor_p, PGSLOTID * out_slot_id_p)
1778 {
1779  SPAGE_SLOT *slot_p;
1780  int used_space;
1781  int status;
1782 
1783  assert (page_p != NULL);
1784  assert (record_descriptor_p != NULL);
1785  assert (out_slot_id_p != NULL);
1786 
1787  status =
1788  spage_find_slot_for_insert (thread_p, page_p, record_descriptor_p, out_slot_id_p, (void **) &slot_p, &used_space);
1789  if (status == SP_SUCCESS)
1790  {
1791  status = spage_insert_data (thread_p, page_p, record_descriptor_p, slot_p);
1792  }
1793 
1794  return status;
1795 }
1796 
1797 /*
1798  * spage_find_slot_for_insert () - Find a slot id and related information in the
1799  * given page
1800  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
1801  *
1802  * page_p(in): Pointer to slotted page
1803  * record_descriptor_p(in): Pointer to a record descriptor
1804  * out_slot_id_p(out): Slot identifier
1805  * out_slot_p(out): Pointer to slotted array
1806  * out_used_space_p(out): Pointer to int
1807  */
1808 STATIC_INLINE int
1809 spage_find_slot_for_insert (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_descriptor_p,
1810  PGSLOTID * out_slot_id_p, void **out_slot_p, int *out_used_space_p)
1811 {
1812  SPAGE_SLOT *slot_p;
1813  int status;
1814 
1815  assert (page_p != NULL);
1816  assert (record_descriptor_p != NULL);
1817  assert (out_slot_id_p != NULL);
1818  assert (out_slot_p != NULL);
1819  assert (out_used_space_p != NULL);
1820 
1821  *out_slot_id_p = NULL_SLOTID;
1822  status = spage_check_record_for_insert (record_descriptor_p);
1823  if (status != SP_SUCCESS)
1824  {
1825  return status;
1826  }
1827 
1828  status =
1829  spage_find_empty_slot (thread_p, page_p, record_descriptor_p->length, record_descriptor_p->type, &slot_p,
1830  out_used_space_p, out_slot_id_p);
1831  *out_slot_p = (void *) slot_p;
1832 
1833 #ifdef SPAGE_DEBUG
1834  spage_check (thread_p, page_p);
1835 #endif /* SPAGE_DEBUG */
1836 
1837  return status;
1838 }
1839 
1840 /*
1841  * spage_insert_data () - Copy the contents of a record into the given page/slot
1842  * return: either of SP_ERROR, SP_SUCCESS
1843  *
1844  * page_p(in): Pointer to slotted page
1845  * record_descriptor_p(in): Pointer to a record descriptor
1846  * slot_p(in): Pointer to slotted array
1847  */
1848 static int
1849 spage_insert_data (THREAD_ENTRY * thread_p, PAGE_PTR page_p, RECDES * record_descriptor_p, void *slot_p)
1850 {
1851  SPAGE_SLOT *tmp_slot_p;
1852 
1853  assert (page_p != NULL);
1854  assert (record_descriptor_p != NULL);
1855  assert (slot_p != NULL);
1856 
1857  if (record_descriptor_p->length < 0)
1858  {
1859  assert (false);
1860  return SP_ERROR;
1861  }
1862 
1863  tmp_slot_p = (SPAGE_SLOT *) slot_p;
1864  if (record_descriptor_p->type != REC_ASSIGN_ADDRESS)
1865  {
1866  if ((unsigned int) tmp_slot_p->offset_to_record + record_descriptor_p->length > (unsigned int) SPAGE_DB_PAGESIZE)
1867  {
1869  assert_release (false);
1870  return SP_ERROR;
1871  }
1872 
1873  memcpy (((char *) page_p + tmp_slot_p->offset_to_record), record_descriptor_p->data, record_descriptor_p->length);
1874  }
1875  else
1876  {
1877  if (tmp_slot_p->offset_to_record + SSIZEOF (TRANID) > (unsigned int) SPAGE_DB_PAGESIZE)
1878  {
1880  assert_release (false);
1881  return SP_ERROR;
1882  }
1883 
1884  *((TRANID *) (page_p + tmp_slot_p->offset_to_record)) = logtb_find_current_tranid (thread_p);
1885  }
1886 
1887  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
1888 
1889 #ifdef SPAGE_DEBUG
1890  spage_check (thread_p, page_p);
1891 #endif /* SPAGE_DEBUG */
1892 
1893  return SP_SUCCESS;
1894 }
1895 
1896 /*
1897  * spage_insert_at () - Insert a record onto the given slotted page at the given
1898  * slot_id
1899  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
1900  *
1901  * page_p(in): Pointer to slotted page
1902  * slot_id(in): Slot_id for newly inserted record
1903  * record_descriptor_p(in): Pointer to a record descriptor
1904  *
1905  * Note: The records on this page must be UNANCHORED, otherwise, an error is
1906  * set and an indication of an error is returned. If the record does not
1907  * fit on the page, such effect is returned.
1908  */
1909 int
1910 spage_insert_at (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES * record_descriptor_p)
1911 {
1912  SPAGE_HEADER *page_header_p;
1913  SPAGE_SLOT *slot_p;
1914  int status;
1915 
1916  assert (page_p != NULL);
1917  assert (record_descriptor_p != NULL);
1918 
1919  if (record_descriptor_p->length < 0)
1920  {
1921  assert (false);
1922  return SP_ERROR;
1923  }
1924 
1925  status = spage_check_record_for_insert (record_descriptor_p);
1926  if (status != SP_SUCCESS)
1927  {
1928  return status;
1929  }
1930 
1931  page_header_p = (SPAGE_HEADER *) page_p;
1932  SPAGE_VERIFY_HEADER (page_header_p);
1933 
1934  if (slot_id > page_header_p->num_slots)
1935  {
1937  pgbuf_get_volume_label (page_p));
1938  return SP_ERROR;
1939  }
1940 
1941  status =
1942  spage_find_empty_slot_at (thread_p, page_p, slot_id, record_descriptor_p->length, record_descriptor_p->type,
1943  &slot_p);
1944  if (status == SP_SUCCESS)
1945  {
1946  status = spage_insert_data (thread_p, page_p, record_descriptor_p, slot_p);
1947  }
1948 
1949 #ifdef SPAGE_DEBUG
1950  spage_check (thread_p, page_p);
1951 #endif /* SPAGE_DEBUG */
1952 
1953  return status;
1954 }
1955 
1956 /*
1957  * spage_insert_for_recovery () - Insert a record onto the given slotted page at
1958  * the given slot_id (only for recovery)
1959  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
1960  *
1961  * page_p(in): Pointer to slotted page
1962  * slot_id(in): Slot_id for insertion
1963  * record_descriptor_p(in): Pointer to a record descriptor
1964  *
1965  * Note: If there is a record located at this slot and the page type of the
1966  * page is anchored the slot record will be replaced by the new record.
1967  * Otherwise, the slots will be moved.
1968  */
1969 int
1970 spage_insert_for_recovery (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES * record_descriptor_p)
1971 {
1972  SPAGE_HEADER *page_header_p;
1973  SPAGE_SLOT *slot_p;
1974  int status;
1975 
1976  assert (page_p != NULL);
1977  assert (record_descriptor_p != NULL);
1978 
1979  if (record_descriptor_p->length < 0)
1980  {
1981  assert (false);
1982  return SP_ERROR;
1983  }
1984 
1985  page_header_p = (SPAGE_HEADER *) page_p;
1986  SPAGE_VERIFY_HEADER (page_header_p);
1987 
1988  if (page_header_p->anchor_type != ANCHORED && page_header_p->anchor_type != ANCHORED_DONT_REUSE_SLOTS)
1989  {
1990  return spage_insert_at (thread_p, page_p, slot_id, record_descriptor_p);
1991  }
1992 
1993  status = spage_check_record_for_insert (record_descriptor_p);
1994  if (status != SP_SUCCESS)
1995  {
1996  return status;
1997  }
1998 
1999  /* If there is a record located at the given slot, the record is removed */
2000 
2001  if (slot_id < page_header_p->num_slots)
2002  {
2003  slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
2004 
2005  assert (page_header_p->anchor_type == ANCHORED || page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS);
2007 
2009  }
2010 
2011  status =
2012  spage_find_empty_slot_at (thread_p, page_p, slot_id, record_descriptor_p->length, record_descriptor_p->type,
2013  &slot_p);
2014  if (status == SP_SUCCESS)
2015  {
2016  if (record_descriptor_p->type != REC_ASSIGN_ADDRESS)
2017  {
2018  if (SPAGE_OVERFLOW (slot_p->offset_to_record + record_descriptor_p->length))
2019  {
2021  assert_release (false);
2022  return SP_ERROR;
2023  }
2024  memcpy (((char *) page_p + slot_p->offset_to_record), record_descriptor_p->data, record_descriptor_p->length);
2025  }
2026 
2027  SPAGE_VERIFY_HEADER (page_header_p);
2028 
2029  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2030  }
2031 
2032 #ifdef SPAGE_DEBUG
2033  spage_check (thread_p, page_p);
2034 #endif /* SPAGE_DEBUG */
2035 
2036  return status;
2037 }
2038 
2039 /*
2040  * spage_is_record_located_at_end () -
2041  * return:
2042  *
2043  * page_header_p(in): Pointer to header of slotted page
2044  * slot_p(in):
2045  */
2046 static bool
2048 {
2049  int waste;
2050 
2051  waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
2052 
2053  assert ((int) slot_p->offset_to_record + (int) slot_p->record_length + waste <= page_header_p->offset_to_free_area);
2054 
2055  return ((int) slot_p->offset_to_record + (int) slot_p->record_length + waste) == page_header_p->offset_to_free_area;
2056 }
2057 
2058 /*
2059  * spage_reduce_a_slot () -
2060  * return:
2061  *
2062  * page_p(in): Pointer to slotted page
2063  */
2064 static void
2066 {
2067  SPAGE_HEADER *page_header_p;
2068  SPAGE_SLOT *slot_p;
2069 
2070  page_header_p = (SPAGE_HEADER *) page_p;
2071 
2072  SPAGE_VERIFY_HEADER (page_header_p);
2073 
2074  slot_p = spage_find_slot (page_p, page_header_p, page_header_p->num_slots - 1, false);
2076 
2077  page_header_p->num_slots--;
2078  page_header_p->total_free += sizeof (SPAGE_SLOT);
2079  page_header_p->cont_free += sizeof (SPAGE_SLOT);
2080 
2081  spage_verify_header (page_p);
2082 }
2083 
2084 /*
2085  * spage_delete () - Delete the record located at given slot on the given page
2086  * return: slot_id on success and NULL_SLOTID on failure
2087  *
2088  * page_p(in): Pointer to slotted page
2089  * slot_id(in): Slot identifier of record to delete
2090  */
2091 PGSLOTID
2092 spage_delete (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
2093 {
2094  SPAGE_HEADER *page_header_p;
2095  SPAGE_SLOT *slot_p;
2096  int waste;
2097  int free_space;
2098 
2099  assert (page_p != NULL);
2100 
2101  page_header_p = (SPAGE_HEADER *) page_p;
2102  SPAGE_VERIFY_HEADER (page_header_p);
2103 
2104  assert (spage_is_valid_anchor_type (page_header_p->anchor_type));
2105 
2106  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
2107  if (slot_p == NULL)
2108  {
2110  pgbuf_get_volume_label (page_p));
2111  return NULL_SLOTID;
2112  }
2113 
2114  page_header_p->num_records--;
2115  waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
2116  free_space = slot_p->record_length + waste;
2117  page_header_p->total_free += free_space;
2118 
2119  /* If it is the last slot in the page, the contiguous free space can be adjusted. Avoid future compaction as much as
2120  * possible */
2121  if (spage_is_record_located_at_end (page_header_p, slot_p))
2122  {
2123  page_header_p->cont_free += free_space;
2124  page_header_p->offset_to_free_area -= free_space;
2125  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
2126  }
2127 
2128  switch (page_header_p->anchor_type)
2129  {
2130  case ANCHORED:
2133  break;
2136  slot_p->record_type = REC_MARKDELETED;
2137  break;
2140  assert (page_header_p->is_saving == false);
2141 
2142  spage_shift_slot_down (page_p, page_header_p, slot_p);
2143 
2144  spage_reduce_a_slot (page_p);
2145  free_space += sizeof (SPAGE_SLOT);
2146  break;
2147  default:
2148  assert (false);
2150  return NULL_SLOTID;
2151  }
2152 
2153  /* Indicate that we are savings */
2154  if (page_header_p->is_saving && spage_save_space (thread_p, page_header_p, page_p, free_space) != NO_ERROR)
2155  {
2156  assert (false);
2157 
2159  return NULL_SLOTID;
2160  }
2161 
2162  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2163 
2164  spage_verify_header (page_p);
2165 
2166 #ifdef SPAGE_DEBUG
2167  spage_check (thread_p, page_p);
2168 #endif /* SPAGE_DEBUG */
2169 
2170  return slot_id;
2171 }
2172 
2173 /*
2174  * spage_delete_for_recovery () - Delete the record located at given slot on the given page
2175  * (only for recovery)
2176  * return: slot_id on success and NULL_SLOTID on failure
2177  *
2178  * page_p(in): Pointer to slotted page
2179  * slot_id(in): Slot identifier of record to delete
2180  *
2181  * Note: The slot is always reused even in ANCHORED_DONT_REUSE_SLOTS pages
2182  * since the record was never made permanent.
2183  */
2184 PGSLOTID
2186 {
2187  SPAGE_HEADER *page_header_p;
2188  SPAGE_SLOT *slot_p;
2189 
2190  assert (page_p != NULL);
2191 
2192  if (spage_delete (thread_p, page_p, slot_id) != slot_id)
2193  {
2194  return NULL_SLOTID;
2195  }
2196 
2197  /* Set the slot as deleted with reuse since the address was never permanent */
2198  page_header_p = (SPAGE_HEADER *) page_p;
2199  SPAGE_VERIFY_HEADER (page_header_p);
2200 
2201  if (page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS)
2202  {
2203  slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
2204  if (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET && slot_p->record_type == REC_MARKDELETED)
2205  {
2207  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2208  }
2209  }
2210 
2211 #ifdef SPAGE_DEBUG
2212  spage_check (thread_p, page_p);
2213 #endif /* SPAGE_DEBUG */
2214 
2215  return slot_id;
2216 }
2217 
2218 /*
2219  * spage_check_updatable () -
2220  * return:
2221  *
2222  * page_p(in): Pointer to slotted page
2223  * slot_id(in): Slot identifier of record to delete
2224  * record_descriptor_p(in):
2225  * out_slot_p(out):
2226  * out_space_p(out):
2227  * out_old_waste_p(out):
2228  * out_new_waste_p(out):
2229  */
2230 static int
2231 spage_check_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_descriptor_length,
2232  SPAGE_SLOT ** out_slot_p, int *out_space_p, int *out_old_waste_p, int *out_new_waste_p)
2233 {
2234  SPAGE_HEADER *page_header_p;
2235  SPAGE_SLOT *slot_p;
2236  int new_waste, old_waste;
2237  int space;
2238 
2239  assert (page_p != NULL);
2240 
2241  if (record_descriptor_length < 0)
2242  {
2243  assert (false);
2245  return SP_ERROR;
2246  }
2247 
2248  if (record_descriptor_length > spage_max_record_size ())
2249  {
2250  return SP_DOESNT_FIT;
2251  }
2252 
2253  page_header_p = (SPAGE_HEADER *) page_p;
2254  SPAGE_VERIFY_HEADER (page_header_p);
2255 
2256  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
2257  if (slot_p == NULL)
2258  {
2260  pgbuf_get_volume_label (page_p));
2261  return SP_ERROR;
2262  }
2263 
2264  old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
2265  new_waste = DB_WASTED_ALIGN (record_descriptor_length, page_header_p->alignment);
2266  space = record_descriptor_length + new_waste - slot_p->record_length - old_waste;
2267 
2268  if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
2269  {
2270  return SP_DOESNT_FIT;
2271  }
2272 
2273  if (out_slot_p)
2274  {
2275  *out_slot_p = slot_p;
2276  }
2277 
2278  if (out_space_p)
2279  {
2280  *out_space_p = space;
2281  }
2282 
2283  if (out_old_waste_p)
2284  {
2285  *out_old_waste_p = old_waste;
2286  }
2287 
2288  if (out_new_waste_p)
2289  {
2290  *out_new_waste_p = new_waste;
2291  }
2292 
2293  return SP_SUCCESS;
2294 }
2295 
2296 /*
2297 * spage_check_mvcc_updatable () - check whether is enough free area to update
2298 * a record in MVCC
2299  * return:
2300  *
2301  * page_p(in): Pointer to slotted page
2302  * slot_id(in): Slot identifier of record to logical delete
2303  * mvcc_delete_record_length(in): the length of delete record
2304  * mvcc_insert_record_length(in): the length of insert record
2305  */
2306 static int
2307 spage_check_mvcc_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int mvcc_delete_record_length,
2308  int mvcc_insert_record_length)
2309 {
2310  SPAGE_HEADER *page_header_p;
2311  SPAGE_SLOT *slot_p;
2312  int new_waste, old_waste;
2313  int space;
2314  int insert_record_space;
2315  int max_record_size;
2316 
2317  assert (page_p != NULL);
2318 
2319  if (mvcc_delete_record_length < 0 || mvcc_insert_record_length < 0)
2320  {
2321  assert (false);
2323  return SP_ERROR;
2324  }
2325 
2326  max_record_size = spage_max_record_size ();
2327  if (mvcc_delete_record_length > max_record_size || mvcc_insert_record_length > max_record_size)
2328  {
2329  return SP_DOESNT_FIT;
2330  }
2331 
2332  page_header_p = (SPAGE_HEADER *) page_p;
2333  SPAGE_VERIFY_HEADER (page_header_p);
2334 
2335  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
2336  if (slot_p == NULL)
2337  {
2339  pgbuf_get_volume_label (page_p));
2340  return SP_ERROR;
2341  }
2342 
2343  old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
2344  new_waste = DB_WASTED_ALIGN (mvcc_delete_record_length, page_header_p->alignment);
2345  space = mvcc_delete_record_length + new_waste - slot_p->record_length - old_waste;
2346 
2347  if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
2348  {
2349  return SP_DOESNT_FIT;
2350  }
2351 
2352  /* delete record fit into page_p check whether both delete and insert record fit into page_p */
2353  new_waste = DB_WASTED_ALIGN (mvcc_insert_record_length, page_header_p->alignment);
2354  insert_record_space = (mvcc_insert_record_length + new_waste);
2355  space += insert_record_space;
2356 
2357  if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
2358  {
2359  return SP_DOESNT_FIT;
2360  }
2361 
2362  /* Find a free slot. Try to reuse an unused slotid, instead of allocating a new one */
2363  slot_id = spage_find_free_slot (page_p, &slot_p, 0);
2364  if (slot_id == SP_ERROR)
2365  {
2366  return SP_ERROR; /* this will not happen */
2367  }
2368 
2369  /* Make sure that there is enough space for the record and the slot */
2370  if (slot_id > page_header_p->num_slots)
2371  { /* defense code */
2372  assert (false);
2374  return SP_ERROR;
2375  }
2376 
2377  if (slot_id == page_header_p->num_slots)
2378  {
2379  /* check whether is enough space for old record, new record and the slot */
2380  insert_record_space += sizeof (SPAGE_SLOT);
2381  space += sizeof (SPAGE_SLOT);
2382 
2383  if (spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
2384  {
2385  return SP_DOESNT_FIT;
2386  }
2387  else if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, insert_record_space) == false)
2388  {
2389  return SP_ERROR;
2390  }
2391  }
2392  else
2393  {
2394  /* We already know that there is total space available since the slot is reused and the space was checked above */
2395  if (spage_has_enough_contiguous_space (thread_p, page_p, page_header_p, insert_record_space) == false)
2396  {
2397  return SP_ERROR;
2398  }
2399  }
2400 
2401  return SP_SUCCESS;
2402 }
2403 
2404 /*
2405  * spage_update_record_in_place () -
2406  * return:
2407  *
2408  * page_p(in): Pointer to slotted page
2409  * page_header_p(in): Pointer to header of slotted page
2410  * slot_p(in/out): Pointer to slotted page pointer array
2411  * record_descriptor_p(in/out):
2412  * space(in):
2413  */
2414 static int
2416  const RECDES * record_descriptor_p, int space)
2417 {
2418  bool is_located_end;
2419 
2420  SPAGE_VERIFY_HEADER (page_header_p);
2421 
2422  if (record_descriptor_p->length < 0)
2423  {
2424  assert (false);
2426  return SP_ERROR;
2427  }
2428 
2429  /* Update the record in place. Same area */
2430  is_located_end = spage_is_record_located_at_end (page_header_p, slot_p);
2431 
2432  slot_p->record_length = record_descriptor_p->length;
2433  if (SPAGE_OVERFLOW (slot_p->offset_to_record + record_descriptor_p->length))
2434  {
2436  assert_release (false);
2437  return SP_ERROR;
2438  }
2439  memcpy (((char *) page_p + slot_p->offset_to_record), record_descriptor_p->data, record_descriptor_p->length);
2440  page_header_p->total_free -= space;
2441 
2442  /* If the record was located at the end, we can execute a simple compaction */
2443  if (is_located_end)
2444  {
2445  page_header_p->cont_free -= space;
2446  page_header_p->offset_to_free_area += space;
2447 
2448  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
2449 
2450  SPAGE_VERIFY_HEADER (page_header_p);
2451  }
2452 
2453  spage_verify_header (page_p);
2454 
2455  return SP_SUCCESS;
2456 }
2457 
2458 /*
2459  * spage_update_record_after_compact () -
2460  * return:
2461  *
2462  * page_p(in): Pointer to slotted page
2463  * page_header_p(in): Pointer to header of slotted page
2464  * slot_p(in/out): Pointer to slotted page pointer array
2465  * record_descriptor_p(in/out):
2466  * space(in):
2467  * old_waste(in):
2468  * new_waste(in):
2469  */
2470 static int
2472  SPAGE_SLOT * slot_p, const RECDES * record_descriptor_p, int space, int old_waste,
2473  int new_waste)
2474 {
2475  int old_offset;
2476 
2477  SPAGE_VERIFY_HEADER (page_header_p);
2478 
2479  if (record_descriptor_p->length < 0)
2480  {
2481  assert (false);
2483  return SP_ERROR;
2484  }
2485 
2486  /*
2487  * If record does not fit in the contiguous free area, compress the page
2488  * leaving the desired record at the end of the free area.
2489  *
2490  * If the record is at the end and there is free space. Do a simple
2491  * compaction
2492  */
2493  if (spage_is_record_located_at_end (page_header_p, slot_p) && space <= page_header_p->cont_free)
2494  {
2495  old_waste += slot_p->record_length;
2496  spage_add_contiguous_free_space (page_p, old_waste);
2497  space = record_descriptor_p->length + new_waste;
2498  old_waste = 0;
2499  }
2500  else if (record_descriptor_p->length + new_waste > page_header_p->cont_free)
2501  {
2502  /*
2503  * Full compaction: eliminate record from compaction (like a quick
2504  * delete). Compaction always finish with the correct amount of free
2505  * space.
2506  */
2507  old_offset = slot_p->offset_to_record;
2509  page_header_p->total_free += (old_waste + slot_p->record_length);
2510  page_header_p->num_records--;
2511 
2512  if (spage_compact (thread_p, page_p) != NO_ERROR)
2513  {
2514  slot_p->offset_to_record = old_offset;
2515  ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
2516  page_header_p->total_free -= (old_waste + slot_p->record_length);
2517  page_header_p->num_records++;
2518 
2519  spage_verify_header (page_p);
2520  return SP_ERROR;
2521  }
2522 
2523  page_header_p->num_records++;
2524  space = record_descriptor_p->length + new_waste;
2525  }
2526 
2527  /* Now update the record */
2528  spage_set_slot (slot_p, page_header_p->offset_to_free_area, record_descriptor_p->length, slot_p->record_type);
2529  if (page_header_p->offset_to_free_area + record_descriptor_p->length > SPAGE_DB_PAGESIZE)
2530  {
2532  assert_release (false);
2533  return SP_ERROR;
2534  }
2535  memcpy (((char *) page_p + page_header_p->offset_to_free_area), record_descriptor_p->data,
2536  record_descriptor_p->length);
2537 
2538  /* Adjust the header */
2539  page_header_p->total_free -= space;
2540  page_header_p->cont_free -= (record_descriptor_p->length + new_waste);
2541  page_header_p->offset_to_free_area += (record_descriptor_p->length + new_waste);
2542 
2543  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
2544 
2545  spage_verify_header (page_p);
2546 
2547  return SP_SUCCESS;
2548 }
2549 
2550 /*
2551  * spage_update () - Update the record located at the given slot with the data
2552  * described by the given record descriptor
2553  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
2554  *
2555  * page_p(in): Pointer to slotted page
2556  * slot_id(in): Slot identifier of record to update
2557  * record_descriptor_p(in): Pointer to a record descriptor
2558  *
2559  * Note: This function do not update the type of the record. If it is changed, it must be handled by the caller
2560  */
2561 int
2562 spage_update (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, const RECDES * record_descriptor_p)
2563 {
2564  SPAGE_HEADER *page_header_p;
2565  SPAGE_SLOT *slot_p;
2566  int new_waste, old_waste;
2567  int space;
2568  int total_free_save;
2569  int status;
2570 
2571  assert (page_p != NULL);
2572  assert (record_descriptor_p != NULL);
2573 
2575 
2576  if (record_descriptor_p->length < 0)
2577  {
2578  assert (false);
2580  return SP_ERROR;
2581  }
2582 
2583  page_header_p = (SPAGE_HEADER *) page_p;
2584  SPAGE_VERIFY_HEADER (page_header_p);
2585 
2586  total_free_save = page_header_p->total_free;
2587 
2588  status =
2589  spage_check_updatable (thread_p, page_p, slot_id, record_descriptor_p->length, &slot_p, &space, &old_waste,
2590  &new_waste);
2591  if (status != SP_SUCCESS)
2592  {
2593  return status;
2594  }
2595 
2596  /* If the new representation fits onto the area of the old representation, execute the update in place */
2597 
2598  if (record_descriptor_p->length <= (int) slot_p->record_length)
2599  {
2600  status = spage_update_record_in_place (page_p, page_header_p, slot_p, record_descriptor_p, space);
2601  }
2602  else
2603  {
2604  status =
2605  spage_update_record_after_compact (thread_p, page_p, page_header_p, slot_p, record_descriptor_p, space,
2606  old_waste, new_waste);
2607  }
2608 
2609  if (status != SP_SUCCESS)
2610  {
2611  return status;
2612  }
2613 
2614  if (page_header_p->is_saving
2615  && spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR)
2616  {
2617  assert (false);
2618 
2620  return SP_ERROR;
2621  }
2622  SPAGE_VERIFY_HEADER (page_header_p);
2623 
2624  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2625 
2626 #ifdef SPAGE_DEBUG
2627  spage_check (thread_p, page_p);
2628 #endif /* SPAGE_DEBUG */
2629 
2630  return SP_SUCCESS;
2631 }
2632 
2633 /*
2634  * spage_is_updatable () - Find if there is enough area to update the record with
2635  * the given data
2636  * return: true if there is enough area to update, or false
2637  *
2638  * page_p(in): Pointer to slotted page
2639  * slot_id(in): Slot identifier of record to update
2640  * record_descriptor_length(in): record descriptor length
2641  */
2642 bool
2643 spage_is_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_descriptor_length)
2644 {
2645  if (spage_check_updatable (thread_p, page_p, slot_id, record_descriptor_length, NULL, NULL, NULL, NULL) != SP_SUCCESS)
2646  {
2647  return false;
2648  }
2649 
2650  return true;
2651 }
2652 
2653 /*
2654 * spage_is_mvcc_updatable () - check whether is enough free area to update
2655  * a record in MVCC
2656  * return: true if there is enough area to update in MVCC, or false
2657  *
2658  * page_p(in): Pointer to slotted page
2659  * slot_id(in): Slot identifier of record to logical delete
2660  * mvcc_delete_record_length(in): the length of delete record
2661  * mvcc_insert_record_length(in): the length of insert record
2662  */
2663 bool
2664 spage_is_mvcc_updatable (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int delete_record_length,
2665  int insert_record_length)
2666 {
2667  if (spage_check_mvcc_updatable (thread_p, page_p, slot_id, delete_record_length, insert_record_length) != SP_SUCCESS)
2668  {
2669  return false;
2670  }
2671 
2672  return true;
2673 }
2674 
2675 /*
2676  * spage_update_record_type () - Update the type of the record located at the
2677  * given slot
2678  * return: void
2679  *
2680  * page_p(in): Pointer to slotted page
2681  * slot_id(in): Slot identifier of record to update
2682  * type(in): record type
2683  */
2684 void
2685 spage_update_record_type (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, INT16 record_type)
2686 {
2687  SPAGE_HEADER *page_header_p;
2688  SPAGE_SLOT *slot_p;
2689 
2690  assert (page_p != NULL);
2691 
2693 
2694  page_header_p = (SPAGE_HEADER *) page_p;
2695  SPAGE_VERIFY_HEADER (page_header_p);
2696 
2697  assert (REC_UNKNOWN <= record_type && record_type <= REC_4BIT_USED_TYPE_MAX);
2698 
2699  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
2700  if (slot_p == NULL)
2701  {
2702  assert (false);
2704  pgbuf_get_volume_label (page_p));
2705  return;
2706  }
2707 
2708  slot_p->record_type = record_type;
2709  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2710 }
2711 
2712 /*
2713  * spage_reclaim () - Reclaim all slots of marked deleted slots of anchored with
2714  * don't reuse slots pages
2715  * return: true if anything was reclaimed and false if nothing was reclaimed
2716  *
2717  * page_p(in): Pointer to slotted page
2718  *
2719  * Note: This function is intended to be run when there are no more references
2720  * of the marked deleted slots, and thus they can be reused.
2721  */
2722 bool
2724 {
2725  SPAGE_HEADER *page_header_p;
2726  SPAGE_SLOT *slot_p, *first_slot_p;
2727  PGSLOTID slot_id;
2728  bool is_reclaim = false;
2729 
2730  assert (page_p != NULL);
2731 
2732  page_header_p = (SPAGE_HEADER *) page_p;
2733  SPAGE_VERIFY_HEADER (page_header_p);
2734 
2735  if (page_header_p->num_slots > 0)
2736  {
2737  first_slot_p = spage_find_slot (page_p, page_header_p, 0, false);
2738 
2739  /* Start backwards so we can reuse space easily */
2740  for (slot_id = page_header_p->num_slots - 1; slot_id >= 0; slot_id--)
2741  {
2742  slot_p = first_slot_p - slot_id;
2743  if (slot_p->offset_to_record == SPAGE_EMPTY_OFFSET
2744  && (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE))
2745  {
2746  assert (page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS);
2748 
2749  if ((slot_id + 1) == page_header_p->num_slots)
2750  {
2751  spage_reduce_a_slot (page_p);
2752  }
2753  else
2754  {
2756  }
2757  is_reclaim = true;
2758  }
2759  }
2760  }
2761 
2762  if (is_reclaim == true)
2763  {
2764  assert (page_header_p->anchor_type == ANCHORED_DONT_REUSE_SLOTS);
2765 
2766  if (page_header_p->num_slots == 0)
2767  {
2768  /* Initialize the page to avoid future compactions */
2769  spage_initialize (thread_p, page_p, page_header_p->anchor_type, page_header_p->alignment,
2770  page_header_p->is_saving);
2771  }
2772  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2773  }
2774 
2775  SPAGE_VERIFY_HEADER (page_header_p);
2776 
2777 #ifdef SPAGE_DEBUG
2778  spage_check (thread_p, page_p);
2779 #endif /* SPAGE_DEBUG */
2780 
2781  return is_reclaim;
2782 }
2783 
2784 /*
2785  * spage_split () - Split the record stored at given slot_id at offset location
2786  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
2787  *
2788  * page_p(in): Pointer to slotted page
2789  * slot_id(in): Slot identifier of record to update
2790  * offset(in): Location of split must be > 0 and smaller than record length
2791  * new_slotid(out): new slot id
2792  */
2793 int
2794 spage_split (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset, PGSLOTID * out_new_slot_id_p)
2795 {
2796  SPAGE_HEADER *page_header_p;
2797  SPAGE_SLOT *slot_p;
2798  SPAGE_SLOT *new_slot_p;
2799  char *copyarea;
2800  int remain_length;
2801  int total_free_save;
2802  int old_waste, remain_waste, new_waste;
2803  int space;
2804  int status;
2805 
2806  assert (page_p != NULL);
2807  assert (out_new_slot_id_p != NULL);
2808 
2809  page_header_p = (SPAGE_HEADER *) page_p;
2810  SPAGE_VERIFY_HEADER (page_header_p);
2811 
2812  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
2813  if (slot_p == NULL)
2814  {
2816  pgbuf_get_volume_label (page_p));
2817  return SP_ERROR;
2818  }
2819 
2820  if (offset < 0 || offset > (int) slot_p->record_length)
2821  {
2823  /* Offset is wrong */
2824  *out_new_slot_id_p = NULL_SLOTID;
2825  return SP_ERROR;
2826  }
2827 
2828 
2829  status = spage_find_empty_slot (thread_p, page_p, 0, slot_p->record_type, &new_slot_p, &new_waste, out_new_slot_id_p);
2830  if (status != SP_SUCCESS)
2831  {
2832  return status;
2833  }
2834 
2835  /* Do we need to worry about wasted space */
2836  remain_length = slot_p->record_length - offset;
2837  remain_waste = DB_WASTED_ALIGN (offset, page_header_p->alignment);
2838  if (remain_waste == 0)
2839  {
2840  /* We are not wasting any space due to alignments. We do not need to move any data, just modify the length and
2841  * offset of the slots. */
2842  new_slot_p->offset_to_record = slot_p->offset_to_record + offset;
2843  ASSERT_ALIGN ((char *) page_p + new_slot_p->offset_to_record, page_header_p->alignment);
2844  new_slot_p->record_length = remain_length;
2845  slot_p->record_length = offset;
2846  }
2847  else
2848  {
2849  /*
2850  * We must move the second portion of the record to an offset that
2851  * is aligned according to the page alignment method. In fact we
2852  * can moved it to the location (offset) returned by sp_empty, if
2853  * there is enough contiguous space, otherwise, we need to compact
2854  * the page.
2855  */
2856  total_free_save = page_header_p->total_free;
2857  old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
2858  remain_waste = DB_WASTED_ALIGN (offset, page_header_p->alignment);
2859  new_waste = DB_WASTED_ALIGN (remain_length, page_header_p->alignment);
2860  /*
2861  * Difference in space:
2862  * newlength1 + new_waste1 : sptr->record_length - offset + new_waste1
2863  * + newlength2 + new_waste2 : + offset + new_waste2
2864  * - oldlength - oldwaste : - sptr->record_length - old_waste
2865  * --------------------------------------------------------------------
2866  * new_waste1 + newwaste2 - oldwaste
2867  */
2868  space = remain_waste + new_waste - old_waste;
2869  if (space > 0 && spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
2870  {
2871  (void) spage_delete_for_recovery (thread_p, page_p, *out_new_slot_id_p);
2872  *out_new_slot_id_p = NULL_SLOTID;
2873  return SP_DOESNT_FIT;
2874  }
2875 
2876  if (remain_length > page_header_p->cont_free)
2877  {
2878  /*
2879  * Need to compact the page, before the second part is moved
2880  * to an alignment position.
2881  *
2882  * Save the second portion
2883  */
2884  copyarea = (char *) malloc (remain_length);
2885  if (copyarea == NULL)
2886  {
2887  (void) spage_delete_for_recovery (thread_p, page_p, *out_new_slot_id_p);
2888  *out_new_slot_id_p = NULL_SLOTID;
2890  return SP_ERROR;
2891  }
2892 
2893  if (SPAGE_OVERFLOW (slot_p->offset_to_record + offset + remain_length))
2894  {
2896  assert_release (false);
2897  return SP_ERROR;
2898  }
2899  memcpy (copyarea, (char *) page_p + slot_p->offset_to_record + offset, remain_length);
2900 
2901  /* For now indicate that it has an empty slot */
2902  new_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
2903  new_slot_p->record_length = remain_length;
2904  /* New length for first part of split. */
2905  slot_p->record_length = offset;
2906 
2907  /* Adjust some of the space for the compaction, then return the space back. That is, second part is gone for
2908  * now. */
2909  page_header_p->total_free += (space + remain_length + new_waste);
2910  page_header_p->num_records--;
2911 
2912  if (spage_compact (thread_p, page_p) != NO_ERROR)
2913  {
2914  slot_p->record_length += remain_length;
2915  page_header_p->total_free -= (space + remain_length + new_waste);
2916  (void) spage_delete_for_recovery (thread_p, page_p, *out_new_slot_id_p);
2917  *out_new_slot_id_p = NULL_SLOTID;
2918 
2919  free_and_init (copyarea);
2920  spage_verify_header (page_p);
2921  return SP_ERROR;
2922  }
2923  page_header_p->num_records++;
2924 
2925  /* Now update the record */
2926  new_slot_p->offset_to_record = page_header_p->offset_to_free_area;
2927  ASSERT_ALIGN ((char *) page_p + new_slot_p->offset_to_record, page_header_p->alignment);
2928  new_slot_p->record_length = remain_length;
2929 
2930  if (SPAGE_OVERFLOW (new_slot_p->offset_to_record + remain_length))
2931  {
2933  assert_release (false);
2934  return SP_ERROR;
2935  }
2936  memcpy (((char *) page_p + new_slot_p->offset_to_record), copyarea, remain_length);
2937  /* Adjust the header */
2938  spage_reduce_contiguous_free_space (page_p, remain_length + new_waste);
2939  free_and_init (copyarea);
2940  }
2941  else
2942  {
2943  /* We can just move the second part to the end of the page */
2944  if (SPAGE_OVERFLOW (new_slot_p->offset_to_record + remain_length))
2945  {
2947  assert_release (false);
2948  return SP_ERROR;
2949  }
2950  memcpy (((char *) page_p + new_slot_p->offset_to_record), (char *) page_p + slot_p->offset_to_record + offset,
2951  remain_length);
2952  new_slot_p->record_length = remain_length;
2953  slot_p->record_length = offset;
2954  /* Adjust the header */
2955  spage_reduce_contiguous_free_space (page_p, space);
2956  }
2957 
2958  if (page_header_p->is_saving
2959  && spage_save_space (thread_p, page_header_p, page_p,
2960  page_header_p->total_free - total_free_save) != NO_ERROR)
2961  {
2962  assert (false);
2963 
2965  return SP_ERROR;
2966  }
2967  }
2968 
2969  spage_verify_header (page_p);
2970 
2971  /* set page dirty */
2972  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
2973 
2974 #ifdef SPAGE_DEBUG
2975  spage_check (thread_p, page_p);
2976 #endif /* SPAGE_DEBUG */
2977 
2978  return SP_SUCCESS;
2979 }
2980 
2981 /*
2982  * spage_take_out () - REMOVE A PORTION OF A RECORD
2983  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
2984  *
2985  * page_p(in): Pointer to slotted page
2986  * slot_id(in): Slot identifier of desired record
2987  * takeout_offset(in): Location where to remove a portion of the data
2988  * takeout_length(in): Length of data to remove starting at takeout_offset
2989  */
2990 int
2991 spage_take_out (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int takeout_offset, int takeout_length)
2992 {
2993  SPAGE_HEADER *page_header_p;
2994  SPAGE_SLOT *slot_p;
2995  int new_waste, old_waste;
2996  int total_free_save;
2997  int mayshift_left;
2998 
2999  assert (page_p != NULL);
3000 
3001  page_header_p = (SPAGE_HEADER *) page_p;
3002  SPAGE_VERIFY_HEADER (page_header_p);
3003 
3004  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
3005  if (slot_p == NULL)
3006  {
3008  pgbuf_get_volume_label (page_p));
3009  return SP_ERROR;
3010  }
3011 
3012  if ((takeout_offset + takeout_length) > (int) slot_p->record_length)
3013  {
3014  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_TAKEOUT_WRONG_OFFSET, 4, takeout_offset, takeout_length,
3015  slot_p->record_length, slot_id);
3016  return SP_ERROR;
3017  }
3018 
3019  total_free_save = page_header_p->total_free;
3020  old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
3021  new_waste = DB_WASTED_ALIGN (slot_p->record_length - takeout_offset, page_header_p->alignment);
3022  /*
3023  * How to shift: The left portion to the right or
3024  * the right portion to the left ?
3025  *
3026  * We shift the left portion to the right only when the left portion is
3027  * smaller than the right portion and we will end up with the record
3028  * aligned without moving the right portion (is left aligned when we
3029  * shifted "takeout_length" ?).
3030  */
3031  /* Check alignment of second part */
3032  mayshift_left = DB_WASTED_ALIGN (slot_p->offset_to_record + takeout_length, page_header_p->alignment);
3033  if (mayshift_left == 0 && (takeout_offset < ((int) slot_p->record_length - takeout_offset - takeout_length)))
3034  {
3035  /*
3036  * Move left part to right since we can archive alignment by moving left
3037  * part "takeout_length" spaces and the left part is smaller than right
3038  * part.
3039  */
3040  if (takeout_offset == 0)
3041  {
3042  /* Don't need to move anything we are chopping the record from the left */
3043  ;
3044  }
3045  else
3046  {
3047  if (SPAGE_OVERFLOW (slot_p->offset_to_record + takeout_length + takeout_offset))
3048  {
3050  assert_release (false);
3051  return SP_ERROR;
3052  }
3053  memmove ((char *) page_p + slot_p->offset_to_record + takeout_length,
3054  (char *) page_p + slot_p->offset_to_record, takeout_offset);
3055  }
3056  slot_p->offset_to_record += takeout_length;
3057  }
3058  else
3059  {
3060  /* Move right part "takeout_length" positions to the left */
3061  if (((int) slot_p->record_length - takeout_offset - takeout_length) > 0)
3062  {
3063  /* We are removing a portion of the record from the middle. That is, we remove a portion of the record and
3064  * glue the remaining two pieces */
3065  if (SPAGE_OVERFLOW (slot_p->offset_to_record + takeout_offset
3066  + (slot_p->record_length - takeout_offset - takeout_length)))
3067  {
3069  assert_release (false);
3070  return SP_ERROR;
3071  }
3072  memmove ((char *) page_p + slot_p->offset_to_record + takeout_offset,
3073  (char *) page_p + slot_p->offset_to_record + takeout_offset + takeout_length,
3074  slot_p->record_length - takeout_offset - takeout_length);
3075  }
3076  else
3077  {
3078  /* We are truncating the record */
3079  ;
3080  }
3081 
3082  if (spage_is_record_located_at_end (page_header_p, slot_p))
3083  {
3084  /*
3085  * The record is located just before the contiguous free area. That is,
3086  * at the end of the page.
3087  *
3088  * Do a simple compaction
3089  */
3090  page_header_p->cont_free += takeout_length + old_waste - new_waste;
3091  page_header_p->offset_to_free_area -= takeout_length + old_waste - new_waste;
3092  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
3093  }
3094  }
3095 
3096  slot_p->record_length -= takeout_length;
3097  page_header_p->total_free += (takeout_length + old_waste - new_waste);
3098 
3099  if (page_header_p->is_saving
3100  && spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR)
3101  {
3102  assert (false);
3103 
3105  return SP_ERROR;
3106  }
3107 
3108  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
3109 
3110  spage_verify_header (page_p);
3111 
3112 #ifdef SPAGE_DEBUG
3113  spage_check (thread_p, page_p);
3114 #endif /* SPAGE_DEBUG */
3115 
3116  return SP_SUCCESS;
3117 }
3118 
3119 /*
3120  * spage_append () - Append the data described by the given record descriptor
3121  * into the record located at the given slot
3122  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
3123  * page_p(in): Pointer to slotted page
3124  * slot_id(in): Slot identifier of desired record
3125  * record_descriptor_p(in): Pointer to a record descriptor
3126  */
3127 int
3128 spage_append (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, const RECDES * record_descriptor_p)
3129 {
3130  return spage_put_helper (thread_p, page_p, slot_id, 0, record_descriptor_p, true);
3131 }
3132 
3133 /*
3134  * spage_put () - Add the data described by the given record descriptor within
3135  * the given offset of the record located at the given slot
3136  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
3137  * page_p(in): Pointer to slotted page
3138  * slot_id(in): Slot identifier of desired record
3139  * offset(in): Location where to add the portion of the data
3140  * record_descriptor_p(in): Pointer to a record descriptor
3141  */
3142 int
3143 spage_put (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset, const RECDES * record_descriptor_p)
3144 {
3145  return spage_put_helper (thread_p, page_p, slot_id, offset, record_descriptor_p, false);
3146 }
3147 
3148 /*
3149  * spage_put_helper () -
3150  * return:
3151  *
3152  * page_p(in): Pointer to slotted page
3153  * slot_id(in): Slot identifier of desired record
3154  * offset(in):
3155  * record_descriptor_p(in):
3156  * is_append(in):
3157  */
3158 static int
3159 spage_put_helper (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset,
3160  const RECDES * record_descriptor_p, bool is_append)
3161 {
3162  SPAGE_HEADER *page_header_p;
3163  SPAGE_SLOT *slot_p;
3164  int old_offset;
3165  int new_waste, old_waste;
3166  int space;
3167  int total_free_save;
3168  char *copyarea;
3169 
3170  assert (page_p != NULL);
3171  assert (record_descriptor_p != NULL);
3172 
3173  if (record_descriptor_p->length < 0)
3174  {
3175  assert (false);
3176  return SP_ERROR;
3177  }
3178 
3179  page_header_p = (SPAGE_HEADER *) page_p;
3180  SPAGE_VERIFY_HEADER (page_header_p);
3181 
3182  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
3183  if (slot_p == NULL)
3184  {
3186  pgbuf_get_volume_label (page_p));
3187  return SP_ERROR;
3188  }
3189 
3190  if ((record_descriptor_p->length + (int) slot_p->record_length) > spage_max_record_size ())
3191  {
3192  return SP_DOESNT_FIT;
3193  }
3194 
3195  total_free_save = page_header_p->total_free;
3196  old_waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
3197  new_waste = DB_WASTED_ALIGN (record_descriptor_p->length + slot_p->record_length, page_header_p->alignment);
3198  space = record_descriptor_p->length + new_waste - old_waste;
3199  if (space > 0 && spage_has_enough_total_space (thread_p, page_p, page_header_p, space) == false)
3200  {
3201  return SP_DOESNT_FIT;
3202  }
3203 
3204  if (spage_is_record_located_at_end (page_header_p, slot_p) && space <= page_header_p->cont_free)
3205  {
3206  /*
3207  * The record is at the end of the page (just before contiguous free
3208  * space), and there is space on the contiguous free are to put in the
3209  * new data.
3210  */
3211 
3212  spage_add_contiguous_free_space (page_p, old_waste);
3213  if (!is_append)
3214  {
3215  /* Move anything after offset, so we can insert the desired data */
3216  if (SPAGE_OVERFLOW (slot_p->offset_to_record + offset + record_descriptor_p->length
3217  + (slot_p->record_length - offset)))
3218  {
3220  assert_release (false);
3221  return SP_ERROR;
3222  }
3223  memmove ((char *) page_p + slot_p->offset_to_record + offset + record_descriptor_p->length,
3224  (char *) page_p + slot_p->offset_to_record + offset, slot_p->record_length - offset);
3225  }
3226  }
3227  else if ((int) record_descriptor_p->length + (int) slot_p->record_length <= page_header_p->cont_free)
3228  {
3229  /* Move the old data to the end and remove wasted space from the old data, so we can append at the right place. */
3230  if (is_append)
3231  {
3232  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + slot_p->record_length))
3233  {
3235  assert_release (false);
3236  return SP_ERROR;
3237  }
3238  memcpy ((char *) page_p + page_header_p->offset_to_free_area, (char *) page_p + slot_p->offset_to_record,
3239  slot_p->record_length);
3240  }
3241  else
3242  {
3243  if (page_header_p->offset_to_free_area + offset > SPAGE_DB_PAGESIZE)
3244  {
3246  assert_release (false);
3247  return SP_ERROR;
3248  }
3249  memcpy ((char *) page_p + page_header_p->offset_to_free_area, (char *) page_p + slot_p->offset_to_record,
3250  offset);
3251  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + offset + record_descriptor_p->length
3252  + (slot_p->record_length - offset)))
3253  {
3255  assert_release (false);
3256  return (SP_ERROR);
3257  }
3258  memmove ((char *) page_p + page_header_p->offset_to_free_area + offset + record_descriptor_p->length,
3259  (char *) page_p + slot_p->offset_to_record + offset, slot_p->record_length - offset);
3260  }
3261  slot_p->offset_to_record = page_header_p->offset_to_free_area;
3262  ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
3263  page_header_p->offset_to_free_area += slot_p->record_length; /* Don't increase waste here */
3264 
3265  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
3266 
3267  page_header_p->cont_free = page_header_p->cont_free - slot_p->record_length + old_waste;
3268  if (is_append)
3269  {
3270  page_header_p->total_free = page_header_p->total_free - slot_p->record_length + old_waste;
3271  }
3272  else
3273  {
3274  page_header_p->total_free += old_waste;
3275  }
3276 
3277  SPAGE_VERIFY_HEADER (page_header_p);
3278  }
3279  else
3280  {
3281  /*
3282  * We need to compress the data leaving the desired record at the end.
3283  * Eliminate the old data from compaction (like a quick delete), by
3284  * saving the data in memory. Then, after the compaction we place the
3285  * data on the contiguous free space. We remove the old_waste space
3286  * since we are appending.
3287  */
3288 
3289  copyarea = (char *) malloc (slot_p->record_length);
3290  if (copyarea == NULL)
3291  {
3293  return SP_ERROR;
3294  }
3295 
3296  if (SPAGE_OVERFLOW (slot_p->offset_to_record + slot_p->record_length))
3297  {
3299  assert_release (false);
3300  return SP_ERROR;
3301  }
3302  memcpy (copyarea, (char *) page_p + slot_p->offset_to_record, slot_p->record_length);
3303  /* For now indicate that it has an empty slot */
3304  old_offset = slot_p->offset_to_record;
3306  page_header_p->total_free += (slot_p->record_length + old_waste);
3307  page_header_p->num_records--;
3308 
3309  if (spage_compact (thread_p, page_p) != NO_ERROR)
3310  {
3311  slot_p->offset_to_record = old_offset;
3312  ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
3313  page_header_p->total_free -= (old_waste + slot_p->record_length);
3314  page_header_p->num_records++;
3315  free_and_init (copyarea);
3316 
3317  spage_verify_header (page_p);
3318  return SP_ERROR;
3319  }
3320  page_header_p->num_records++;
3321  /* Move the old data to the end */
3322  if (is_append)
3323  {
3324  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + slot_p->record_length))
3325  {
3327  assert_release (false);
3328  return SP_ERROR;
3329  }
3330  memcpy ((char *) page_p + page_header_p->offset_to_free_area, copyarea, slot_p->record_length);
3331  }
3332  else
3333  {
3334  if (page_header_p->offset_to_free_area + offset > SPAGE_DB_PAGESIZE)
3335  {
3337  assert_release (false);
3338  return SP_ERROR;
3339  }
3340  memcpy ((char *) page_p + page_header_p->offset_to_free_area, copyarea, offset);
3341  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + offset + +record_descriptor_p->length
3342  + (slot_p->record_length - offset)))
3343  {
3345  assert_release (false);
3346  return SP_ERROR;
3347  }
3348  memcpy ((char *) page_p + page_header_p->offset_to_free_area + offset + record_descriptor_p->length,
3349  copyarea + offset, slot_p->record_length - offset);
3350  }
3351  free_and_init (copyarea);
3352  slot_p->offset_to_record = page_header_p->offset_to_free_area;
3353  ASSERT_ALIGN ((char *) page_p + slot_p->offset_to_record, page_header_p->alignment);
3355  }
3356 
3357  /* Now perform the put operation. */
3358  if (is_append)
3359  {
3360  if (page_header_p->offset_to_free_area + record_descriptor_p->length > SPAGE_DB_PAGESIZE)
3361  {
3363  assert_release (false);
3364  return SP_ERROR;
3365  }
3366  memcpy (((char *) page_p + page_header_p->offset_to_free_area), record_descriptor_p->data,
3367  record_descriptor_p->length);
3368  }
3369  else
3370  {
3371  if (SPAGE_OVERFLOW (slot_p->offset_to_record + offset + record_descriptor_p->length))
3372  {
3374  assert_release (false);
3375  return SP_ERROR;
3376  }
3377  memcpy (((char *) page_p + slot_p->offset_to_record + offset), record_descriptor_p->data,
3378  record_descriptor_p->length);
3379  }
3380  slot_p->record_length += record_descriptor_p->length;
3381  /* Note that we have already eliminated the old waste, so do not take it in consideration right now. */
3382  spage_reduce_contiguous_free_space (page_p, record_descriptor_p->length + new_waste);
3383  if (page_header_p->is_saving
3384  && (spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR))
3385  {
3386  assert (false);
3387 
3389  return SP_ERROR;
3390  }
3391 
3392  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
3393 
3394  spage_verify_header (page_p);
3395 
3396 #ifdef SPAGE_DEBUG
3397  spage_check (thread_p, page_p);
3398 #endif /* SPAGE_DEBUG */
3399 
3400  return SP_SUCCESS;
3401 }
3402 
3403 /*
3404  * spage_overwrite () - Overwrite a portion of the record stored at given slot_id
3405  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
3406  *
3407  * page_p(in): Pointer to slotted page
3408  * slot_id(in): Slot identifier of record to overwrite
3409  * overwrite_offset(in): Offset on the record to start the overwrite process
3410  * record_descriptor_p(in): New replacement data
3411  *
3412  * Note: overwrite_offset + record_descriptor_p->length must be <= length of record stored
3413  * on slot.
3414  * If this is not the case, you must use a combination of overwrite and
3415  * append.
3416  */
3417 int
3418 spage_overwrite (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int overwrite_offset,
3419  const RECDES * record_descriptor_p)
3420 {
3421  SPAGE_HEADER *page_header_p;
3422  SPAGE_SLOT *slot_p;
3423 
3424  assert (page_p != NULL);
3425 
3426  page_header_p = (SPAGE_HEADER *) page_p;
3427  SPAGE_VERIFY_HEADER (page_header_p);
3428 
3429  assert (record_descriptor_p != NULL);
3430 
3431  if (record_descriptor_p->length < 0)
3432  {
3433  assert (false);
3435  return SP_ERROR;
3436  }
3437 
3438  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
3439  if (slot_p == NULL)
3440  {
3442  pgbuf_get_volume_label (page_p));
3443  return SP_ERROR;
3444  }
3445 
3446  if ((overwrite_offset + record_descriptor_p->length) > (int) slot_p->record_length)
3447  {
3449  record_descriptor_p->length, slot_p->record_length, slot_id);
3450  return SP_ERROR;
3451  }
3452 
3453  if (SPAGE_OVERFLOW (slot_p->offset_to_record + overwrite_offset + record_descriptor_p->length))
3454  {
3456  assert_release (false);
3457  return SP_ERROR;
3458  }
3459  memcpy (((char *) page_p + slot_p->offset_to_record + overwrite_offset), record_descriptor_p->data,
3460  record_descriptor_p->length);
3461 
3462  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
3463 
3464 #ifdef SPAGE_DEBUG
3465  spage_check (thread_p, page_p);
3466 #endif /* SPAGE_DEBUG */
3467 
3468  return SP_SUCCESS;
3469 }
3470 
3471 /*
3472  * spage_merge () - Merge the record of the second slot onto the record of the
3473  * first slot
3474  * return: either of SP_ERROR, SP_DOESNT_FIT, SP_SUCCESS
3475  *
3476  * page_p(in): Pointer to slotted page
3477  * slotid1(in): Slot identifier of first slot
3478  * slotid2(in): Slot identifier of second slot
3479  *
3480  * Note: Then the second slot is removed.
3481  */
3482 int
3483 spage_merge (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID first_slot_id, PGSLOTID second_slot_id)
3484 {
3485  SPAGE_HEADER *page_header_p;
3486  SPAGE_SLOT *first_slot_p;
3487  SPAGE_SLOT *second_slot_p;
3488  int first_old_offset, second_old_offset;
3489  int new_waste, first_old_waste, second_old_waste;
3490  int total_free_save;
3491  char *copyarea;
3492 
3493  assert (page_p != NULL);
3494 
3495  page_header_p = (SPAGE_HEADER *) page_p;
3496  SPAGE_VERIFY_HEADER (page_header_p);
3497 
3498  /* Find the slots */
3499  first_slot_p = spage_find_slot (page_p, page_header_p, first_slot_id, true);
3500  if (first_slot_p == NULL)
3501  {
3503  pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
3504  return SP_ERROR;
3505  }
3506 
3507  second_slot_p = spage_find_slot (page_p, page_header_p, second_slot_id, true);
3508  if (second_slot_p == NULL)
3509  {
3511  pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
3512  return SP_ERROR;
3513  }
3514 
3515  total_free_save = page_header_p->total_free;
3516  first_old_waste = DB_WASTED_ALIGN (first_slot_p->record_length, page_header_p->alignment);
3517  second_old_waste = DB_WASTED_ALIGN (second_slot_p->record_length, page_header_p->alignment);
3518  new_waste = DB_WASTED_ALIGN (first_slot_p->record_length + second_slot_p->record_length, page_header_p->alignment);
3519  if (spage_is_record_located_at_end (page_header_p, first_slot_p)
3520  && (int) second_slot_p->record_length <= page_header_p->cont_free)
3521  {
3522  /*
3523  * The first record is at the end of the page (just before contiguous free
3524  * space), and there is space on the contiguous free area to append the
3525  * second record.
3526  *
3527  * Remove the wasted space from the free spaces, so we can append at
3528  * the right place.
3529  */
3530 
3531  spage_add_contiguous_free_space (page_p, first_old_waste);
3532  first_old_waste = 0;
3533  }
3534  else if ((int) first_slot_p->record_length + (int) second_slot_p->record_length <= page_header_p->cont_free)
3535  {
3536  /* Move the first data to the end and remove wasted space from the first record, so we can append at the right
3537  * place. */
3538  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + first_slot_p->record_length))
3539  {
3541  assert_release (false);
3542  return SP_ERROR;
3543  }
3544  memcpy ((char *) page_p + page_header_p->offset_to_free_area, (char *) page_p + first_slot_p->offset_to_record,
3545  first_slot_p->record_length);
3546  first_slot_p->offset_to_record = page_header_p->offset_to_free_area;
3547  ASSERT_ALIGN ((char *) page_p + first_slot_p->offset_to_record, page_header_p->alignment);
3548  page_header_p->offset_to_free_area += first_slot_p->record_length;
3549 
3550  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
3551 
3552  /* Don't increase waste here */
3553  page_header_p->total_free -= (first_slot_p->record_length - first_old_waste);
3554  page_header_p->cont_free -= (first_slot_p->record_length - first_old_waste);
3555  first_old_waste = 0;
3556 
3557  SPAGE_VERIFY_HEADER (page_header_p);
3558  }
3559  else
3560  {
3561  /*
3562  * We need to compress the page leaving the desired record at end.
3563  * We eliminate the data of both records (like quick deletes), by
3564  * saving their data in memory. Then, after the compaction we restore
3565  * the data on the contiguous space.
3566  */
3567 
3568  copyarea = (char *) malloc (first_slot_p->record_length + second_slot_p->record_length);
3569  if (copyarea == NULL)
3570  {
3571  return SP_ERROR;
3572  }
3573 
3574  if (SPAGE_OVERFLOW (first_slot_p->offset_to_record + first_slot_p->record_length))
3575  {
3577  assert_release (false);
3578  return SP_ERROR;
3579  }
3580  memcpy (copyarea, (char *) page_p + first_slot_p->offset_to_record, first_slot_p->record_length);
3581  if (SPAGE_OVERFLOW (second_slot_p->offset_to_record + second_slot_p->record_length))
3582  {
3584  assert_release (false);
3585  return SP_ERROR;
3586  }
3587  memcpy (copyarea + first_slot_p->record_length, (char *) page_p + second_slot_p->offset_to_record,
3588  second_slot_p->record_length);
3589 
3590  /* Now indicate empty slots. */
3591  first_old_offset = first_slot_p->offset_to_record;
3592  second_old_offset = second_slot_p->offset_to_record;
3593  first_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
3594  second_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
3595  page_header_p->total_free += (first_slot_p->record_length + second_slot_p->record_length + first_old_waste
3596  + second_old_waste);
3597  page_header_p->num_records -= 2;
3598 
3599  if (spage_compact (thread_p, page_p) != NO_ERROR)
3600  {
3601  first_slot_p->offset_to_record = first_old_offset;
3602  ASSERT_ALIGN ((char *) page_p + first_slot_p->offset_to_record, page_header_p->alignment);
3603  second_slot_p->offset_to_record = second_old_offset;
3604  ASSERT_ALIGN ((char *) page_p + second_slot_p->offset_to_record, page_header_p->alignment);
3605  page_header_p->total_free -= (first_slot_p->record_length + second_slot_p->record_length + first_old_waste
3606  + second_old_waste);
3607  page_header_p->num_records += 2;
3608  free_and_init (copyarea);
3609 
3610  spage_verify_header (page_p);
3611  return SP_ERROR;
3612  }
3613  page_header_p->num_records += 2;
3614 
3615  /* Move the old data to the end */
3616  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + first_slot_p->record_length
3617  + second_slot_p->record_length))
3618  {
3620  assert_release (false);
3621  return SP_ERROR;
3622  }
3623  memcpy ((char *) page_p + page_header_p->offset_to_free_area, copyarea,
3624  first_slot_p->record_length + second_slot_p->record_length);
3625  free_and_init (copyarea);
3626 
3627  first_slot_p->offset_to_record = page_header_p->offset_to_free_area;
3628  ASSERT_ALIGN ((char *) page_p + first_slot_p->offset_to_record, page_header_p->alignment);
3629  first_slot_p->record_length += second_slot_p->record_length;
3630  second_slot_p->record_length = 0;
3631  second_slot_p->offset_to_record = SPAGE_EMPTY_OFFSET;
3632 
3633  spage_reduce_contiguous_free_space (page_p, first_slot_p->record_length);
3634  first_old_waste = 0;
3635  second_old_waste = 0;
3636  }
3637 
3638  /* Now perform the append operation if needed */
3639  if (second_slot_p->record_length != 0)
3640  {
3641  if (SPAGE_OVERFLOW (page_header_p->offset_to_free_area + second_slot_p->record_length))
3642  {
3644  assert_release (false);
3645  return SP_ERROR;
3646  }
3647  memcpy (((char *) page_p + page_header_p->offset_to_free_area), (char *) page_p + second_slot_p->offset_to_record,
3648  second_slot_p->record_length);
3649  first_slot_p->record_length += second_slot_p->record_length;
3650  second_slot_p->record_length = 0;
3651  }
3652 
3653  /* Note that we have already eliminated the old waste, so do not take it in consideration right now. */
3654 
3655  spage_reduce_contiguous_free_space (page_p, new_waste - first_old_waste - second_old_waste);
3656  (void) spage_delete (thread_p, page_p, second_slot_id);
3657 
3658  if (page_header_p->is_saving
3659  && spage_save_space (thread_p, page_header_p, page_p, page_header_p->total_free - total_free_save) != NO_ERROR)
3660  {
3661  assert (false);
3662 
3664  return SP_ERROR;
3665  }
3666 
3667  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
3668 
3669  spage_verify_header (page_p);
3670 
3671 #ifdef SPAGE_DEBUG
3672  spage_check (thread_p, page_p);
3673 #endif /* SPAGE_DEBUG */
3674 
3675  return SP_SUCCESS;
3676 }
3677 
3678 /*
3679  * spage_search_record () -
3680  * return:
3681  *
3682  * page_p(in): Pointer to slotted page
3683  * out_slot_id_p(in/out): Slot identifier of desired record
3684  * record_descriptor_p(in/out):
3685  * is_peeking(in):
3686  * direction(in):
3687  */
3688 static SCAN_CODE
3689 spage_search_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p, int is_peeking,
3690  int direction, bool skip_empty)
3691 {
3692  SPAGE_HEADER *page_header_p;
3693  SPAGE_SLOT *slot_p;
3694  PGSLOTID slot_id;
3695 
3696  assert (page_p != NULL);
3697  assert (out_slot_id_p != NULL);
3698  assert (record_descriptor_p != NULL);
3699 
3700  slot_id = *out_slot_id_p;
3701  page_header_p = (SPAGE_HEADER *) page_p;
3702  SPAGE_VERIFY_HEADER (page_header_p);
3703 
3704  if (slot_id < 0 || slot_id > page_header_p->num_slots)
3705  {
3706  if (direction == SPAGE_SEARCH_NEXT)
3707  {
3708  slot_id = 0;
3709  }
3710  else
3711  {
3712  slot_id = page_header_p->num_slots - 1;
3713  }
3714  }
3715  else
3716  {
3717  slot_id += direction;
3718  }
3719 
3720  slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
3721  while (slot_id >= 0 && slot_id < page_header_p->num_slots && slot_p->offset_to_record == SPAGE_EMPTY_OFFSET)
3722  {
3723  if (skip_empty)
3724  {
3725  slot_id += direction;
3726  slot_p -= direction;
3727  }
3728  else
3729  {
3730  record_descriptor_p->data = NULL;
3731  *out_slot_id_p = slot_id;
3732  return S_SUCCESS;
3733  }
3734  }
3735 
3736  SPAGE_VERIFY_HEADER (page_header_p);
3737 
3738  if (slot_id >= 0 && slot_id < page_header_p->num_slots)
3739  {
3740  *out_slot_id_p = slot_id;
3741  return spage_get_record_data (page_p, slot_p, record_descriptor_p, is_peeking);
3742  }
3743  else
3744  {
3745  /* There is not anymore records */
3746  *out_slot_id_p = -1;
3747  record_descriptor_p->length = 0;
3748  return S_END;
3749  }
3750 }
3751 
3752 /*
3753  * spage_next_record () - Get next record
3754  * return: Either of S_SUCCESS, S_DOESNT_FIT, S_END
3755  * page_p(in): Pointer to slotted page
3756  * out_slot_id_p(in/out): Slot identifier of current record
3757  * record_descriptor_p(out): Pointer to a record descriptor
3758  * is_peeking(in): Indicates whether the record is going to be copied
3759  * (like a copy) or peeked (read at the buffer)
3760  *
3761  * Note: When is_peeking is PEEK, the next available record is peeked onto the
3762  * page. The address of the record descriptor is set to the portion of
3763  * the buffer where the record is stored. Peeking a record should be
3764  * executed with caution since the slotted module may decide to move
3765  * the record around. In general, no other operation should be executed
3766  * on the page until the peeking of the record is done. The page should
3767  * be fixed and locked to avoid any funny behavior. RECORD should NEVER
3768  * be MODIFIED DIRECTLY. Only reads should be performed, otherwise
3769  * header information and other records may be corrupted.
3770  *
3771  * When is_peeking is COPY, the next available record is read
3772  * onto the area pointed by the record descriptor. If the record does not
3773  * fit in such an area, the length of the record is returned as a
3774  * negative value in record_descriptor_p->length and an error is
3775  * indicated in the return value.
3776  *
3777  * If the current value of out_slot_id_p is negative, the first record on the
3778  * page is retrieved.
3779  */
3780 SCAN_CODE
3781 spage_next_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p, int is_peeking)
3782 {
3783  return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_NEXT, true);
3784 }
3785 
3786 /*
3787  * spage_previous_record () - Get previous record
3788  * return: Either of S_SUCCESS, S_DOESNT_FIT, S_END
3789  * page_p(in): Pointer to slotted page
3790  * slot_id(out): Slot identifier of current record
3791  * record_descriptor_p(out): Pointer to a record descriptor
3792  * is_peeking(in): Indicates whether the record is going to be copied
3793  * (like a copy) or peeked (read at the buffer)
3794  *
3795  * Note: When is_peeking is PEEK, the previous available record is peeked onto
3796  * the page. The address of the record descriptor is set to the portion
3797  * of the buffer where the record is stored. Peeking a record should be
3798  * executed with caution since the slotted module may decide to move
3799  * the record around. In general, no other operation should be executed
3800  * on the page until the peeking of the record is done. The page should
3801  * be fixed and locked to avoid any funny behavior. RECORD should NEVER
3802  * be MODIFIED DIRECTLY. Only reads should be performed, otherwise
3803  * header information and other records may be corrupted.
3804  *
3805  * When is_peeking is COPY, the previous available record is
3806  * read onto the area pointed by the record descriptor. If the record
3807  * does not fit in such an area, the length of the record is returned
3808  * as a negative value in record_descriptor_p->length and an error is
3809  * indicated in the return value.
3810  *
3811  * If the current value of slot_id is negative, the first record on the
3812  * page is retrieved.
3813  */
3814 SCAN_CODE
3815 spage_previous_record (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p, int is_peeking)
3816 {
3817  return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_PREV, true);
3818 }
3819 
3820 /*
3821  * spage_get_record () - Get specified record
3822  * return: Either of S_SUCCESS, S_DOESNT_FIT, S_END
3823  *
3824  * page_p(in): Pointer to slotted page
3825  * slot_id(in): Slot identifier of current record
3826  * record_descriptor_p(out): Pointer to a record descriptor
3827  * is_peeking(in): Indicates whether the record is going to be copied
3828  * (like a copy) or peeked (read at the buffer)
3829  *
3830  * Note: When is_peeking is PEEK, the desired available record is peeked onto
3831  * the page. The address of the record descriptor is set to the portion
3832  * of the buffer where the record is stored. Peeking a record should be
3833  * executed with caution since the slotted module may decide to move
3834  * the record around. In general, no other operation should be executed
3835  * on the page until the peeking of the record is done. The page should
3836  * be fixed and locked to avoid any funny behavior. RECORD should NEVER
3837  * be MODIFIED DIRECTLY. Only reads should be performed, otherwise
3838  * header information and other records may be corrupted.
3839  *
3840  * When is_peeking is COPY, the desired available record is
3841  * read onto the area pointed by the record descriptor. If the record
3842  * does not fit in such an area, the length of the record is returned
3843  * as a negative value in record_descriptor_p->length and an error is
3844  * indicated in the return value.
3845  */
3846 SCAN_CODE
3847 spage_get_record (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES * record_descriptor_p,
3848  int is_peeking)
3849 {
3850  SPAGE_HEADER *page_header_p;
3851  SPAGE_SLOT *sptr;
3852 
3853  assert (page_p != NULL);
3854 
3855  page_header_p = (SPAGE_HEADER *) page_p;
3856  SPAGE_VERIFY_HEADER (page_header_p);
3857 
3858  assert (record_descriptor_p != NULL);
3859 
3860  sptr = spage_find_slot (page_p, page_header_p, slot_id, true);
3861  if (sptr == NULL)
3862  {
3863  record_descriptor_p->length = 0;
3865  pgbuf_get_volume_label (page_p));
3866  return S_DOESNT_EXIST;
3867  }
3868 
3869  return spage_get_record_data (page_p, sptr, record_descriptor_p, is_peeking);
3870 }
3871 
3872 /*
3873  * spage_get_record_data () -
3874  * return:
3875  *
3876  * page_p(in): Pointer to slotted page
3877  * slot_p(in/out): Pointer to slotted page pointer array
3878  * record_descriptor_p(in/out):
3879  * is_peeking(in):
3880  */
3881 static SCAN_CODE
3882 spage_get_record_data (PAGE_PTR page_p, SPAGE_SLOT * slot_p, RECDES * record_descriptor_p, bool is_peeking)
3883 {
3884  assert (page_p != NULL);
3885  assert (slot_p != NULL);
3886  assert (record_descriptor_p != NULL);
3887 
3888  /*
3889  * If peeking, the address of the data in the descriptor is set to the
3890  * address of the record in the buffer. Otherwise, the record is copied
3891  * onto the area specified by the descriptor
3892  */
3893  if (is_peeking == PEEK)
3894  {
3895  record_descriptor_p->area_size = -1;
3896  record_descriptor_p->data = (char *) page_p + slot_p->offset_to_record;
3897  }
3898  else
3899  {
3900  /* copy the record */
3901  if (record_descriptor_p->area_size < 0 || record_descriptor_p->area_size < (int) slot_p->record_length)
3902  {
3903  /*
3904  * DOES NOT FIT
3905  * Give a hint to the user of the needed length. Hint is given as a
3906  * negative value
3907  */
3908  /* do not use unary minus because slot_p->record_length is unsigned */
3909  record_descriptor_p->length = (slot_p->record_length * -1);
3910  return S_DOESNT_FIT;
3911  }
3912 
3913  if (SPAGE_OVERFLOW (slot_p->offset_to_record + slot_p->record_length))
3914  {
3916  assert_release (false);
3917  return S_ERROR;
3918  }
3919  memcpy (record_descriptor_p->data, (char *) page_p + slot_p->offset_to_record, slot_p->record_length);
3920  }
3921 
3922  record_descriptor_p->length = slot_p->record_length;
3923  record_descriptor_p->type = slot_p->record_type;
3924 
3925  return S_SUCCESS;
3926 }
3927 
3928 /*
3929  * spage_get_record_length () - Find the length of the record associated with
3930  * the given slot on the given page
3931  * return: Length of the record or -1 in case of error
3932  *
3933  * page_p(in): Pointer to slotted page
3934  * slot_id(in): Slot identifier of record
3935  */
3936 int
3938 {
3939  SPAGE_HEADER *page_header_p;
3940  SPAGE_SLOT *slot_p;
3941 
3942  assert (page_p != NULL);
3943 
3944  page_header_p = (SPAGE_HEADER *) page_p;
3945  SPAGE_VERIFY_HEADER (page_header_p);
3946 
3947  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
3948  if (slot_p == NULL)
3949  {
3951  pgbuf_get_volume_label (page_p));
3952  return -1;
3953  }
3954 
3955  return slot_p->record_length;
3956 }
3957 
3958 /*
3959  * spage_get_space_for_record () -
3960  * return:
3961  *
3962  * page_p(in): Pointer to slotted page
3963  * slot_id(in): Slot identifier of record
3964  */
3965 int
3967 {
3968  SPAGE_HEADER *page_header_p;
3969  SPAGE_SLOT *slot_p;
3970 
3971  assert (page_p != NULL);
3972 
3973  page_header_p = (SPAGE_HEADER *) page_p;
3974  SPAGE_VERIFY_HEADER (page_header_p);
3975 
3976  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
3977  if (slot_p == NULL)
3978  {
3980  pgbuf_get_volume_label (page_p));
3981  return -1;
3982  }
3983 
3984  return (slot_p->record_length + DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment) +
3985  spage_slot_size ());
3986 }
3987 
3988 /*
3989  * spage_get_record_type () - Find the type of the record associated with the given slot
3990  * on the given page
3991  * return: record type, or -1 if the given slot is invalid
3992  *
3993  * page_p(in): Pointer to slotted page
3994  * slot_id(in): Slot identifier of record
3995  */
3996 INT16
3998 {
3999  SPAGE_HEADER *page_header_p;
4000  SPAGE_SLOT *slot_p;
4001 
4002  assert (page_p != NULL);
4003 
4004  page_header_p = (SPAGE_HEADER *) page_p;
4005  SPAGE_VERIFY_HEADER (page_header_p);
4006 
4007  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
4008  if (slot_p == NULL || slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE)
4009  {
4010  return REC_UNKNOWN;
4011  }
4012 
4013  return slot_p->record_type;
4014 }
4015 
4016 /*
4017  * spage_mark_deleted_slot_as_reusable () - Marks the slot of a previously
4018  * deleted record as reusable
4019  * return: SP_ERROR or SP_SUCCESS
4020  *
4021  * page_p(in): Pointer to slotted page
4022  * slot_id(in): Slot identifier
4023  */
4024 int
4026 {
4027  SPAGE_HEADER *page_header_p = NULL;
4028  SPAGE_SLOT *slot_p = NULL;
4029 
4030  assert (page_p != NULL);
4031 
4032  page_header_p = (SPAGE_HEADER *) page_p;
4033  SPAGE_VERIFY_HEADER (page_header_p);
4034 
4035  if (0 > slot_id || slot_id >= page_header_p->num_slots)
4036  {
4037  assert (false);
4039  pgbuf_get_volume_label (page_p));
4040  return SP_ERROR;
4041  }
4042 
4043  slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
4044 
4045  if (slot_p && slot_p->offset_to_record == SPAGE_EMPTY_OFFSET
4046  && (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE))
4047  {
4048  ; /* OK */
4049  }
4050  else
4051  {
4052  /*
4053  * If the function is called in the scenarios it was designed for, this
4054  * should not happen. The slot to be set as reusable should always
4055  * exist and should not point to a valid record.
4056  */
4057  assert (false);
4059  pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
4060  return SP_ERROR;
4061  }
4062 
4064 
4065  SPAGE_VERIFY_HEADER (page_header_p);
4066 
4067  return SP_SUCCESS;
4068 }
4069 
4070 /*
4071  * spage_is_slot_exist () - Find if there is a valid record in given slot
4072  * return: true/false
4073  *
4074  * page_p(in): Pointer to slotted page
4075  * slot_id(in): Slot identifier of record
4076  */
4077 bool
4079 {
4080  SPAGE_HEADER *page_header_p;
4081  SPAGE_SLOT *slot_p;
4082 
4083  assert (page_p != NULL);
4084 
4085  page_header_p = (SPAGE_HEADER *) page_p;
4086  SPAGE_VERIFY_HEADER (page_header_p);
4087 
4088  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
4089  if (slot_p == NULL || slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE)
4090  {
4091  return false;
4092  }
4093  else
4094  {
4095  return true;
4096  }
4097 }
4098 
4099 /*
4100  * spage_record_type_string () -
4101  * return:
4102  *
4103  * record_type(in):
4104  */
4105 static const char *
4106 spage_record_type_string (INT16 record_type)
4107 {
4108  assert (REC_UNKNOWN <= record_type && record_type <= REC_4BIT_USED_TYPE_MAX);
4109 
4110  switch (record_type)
4111  {
4112  case REC_HOME:
4113  return "HOME";
4114  case REC_NEWHOME:
4115  return "NEWHOME";
4116  case REC_RELOCATION:
4117  return "RELOCATION";
4118  case REC_BIGONE:
4119  return "BIGONE";
4120  case REC_MARKDELETED:
4121  return "MARKDELETED";
4123  return "DELETED_WILL_REUSE";
4124  case REC_ASSIGN_ADDRESS:
4125  return "ASSIGN_ADDRESS";
4126  default:
4127  assert (false);
4128  return "UNKNOWN";
4129  }
4130 }
4131 
4132 /*
4133  * spage_anchor_flag_string () -
4134  * return:
4135  *
4136  * anchor_type(in):
4137  */
4138 const char *
4139 spage_anchor_flag_string (const INT16 anchor_type)
4140 {
4141  assert (ANCHORED <= anchor_type && anchor_type <= UNANCHORED_KEEP_SEQUENCE);
4142 
4143  switch (anchor_type)
4144  {
4145  case ANCHORED:
4146  return "ANCHORED";
4148  return "ANCHORED_DONT_REUSE_SLOTS";
4150  return "UNANCHORED_ANY_SEQUENCE";
4152  return "UNANCHORED_KEEP_SEQUENCE";
4153  default:
4154  assert (false);
4155  return "UNKNOWN";
4156  }
4157 }
4158 
4159 /*
4160  * spage_alignment_string () -
4161  * return:
4162  *
4163  * alignment(in):
4164  */
4165 const char *
4166 spage_alignment_string (unsigned short alignment)
4167 {
4168  switch (alignment)
4169  {
4170  case CHAR_ALIGNMENT:
4171  return "CHAR";
4172  case SHORT_ALIGNMENT:
4173  return "SHORT";
4174  case INT_ALIGNMENT:
4175  return "INT";
4176  case DOUBLE_ALIGNMENT:
4177  return "DOUBLE";
4178  default:
4179  return "UNKNOWN";
4180  }
4181 }
4182 
4183 /*
4184  * spage_dump_header () - Dump an slotted page header
4185  * return: void
4186  * page_header_p(in): Pointer to header of slotted page
4187  *
4188  * Note: This function is used for debugging purposes.
4189  */
4190 static void
4191 spage_dump_header (FILE * fp, const SPAGE_HEADER * page_header_p)
4192 {
4193  char buffer[1024];
4194 
4195  spage_dump_header_to_string (buffer, sizeof (buffer), page_header_p);
4196 
4197  (void) fprintf (fp, "%s", buffer);
4198 }
4199 
4200 /*
4201  * spage_dump_header_to_string () -
4202  * return: void
4203  *
4204  * buffer(out): char buffer pointer
4205  * size(in): buffer size
4206  * page_header_p(in): Pointer to header of slotted page
4207  *
4208  * Note:
4209  */
4210 static void
4211 spage_dump_header_to_string (char *buffer, int size, const SPAGE_HEADER * page_header_p)
4212 {
4213  int n = 0;
4214  /* Dump header information */
4215  n +=
4216  snprintf (buffer + n, size - n, "NUM SLOTS = %d, NUM RECS = %d, TYPE OF SLOTS = %s,\n", page_header_p->num_slots,
4217  page_header_p->num_records, spage_anchor_flag_string (page_header_p->anchor_type));
4218  n += snprintf (buffer + n, size - n, "ALIGNMENT-TO = %s\n", spage_alignment_string (page_header_p->alignment));
4219  n +=
4220  snprintf (buffer + n, size - n,
4221  "TOTAL FREE AREA = %d, CONTIGUOUS FREE AREA = %d,"
4222  " FREE SPACE OFFSET = %d, NEED UPDATE BEST HINT = %d\n", page_header_p->total_free,
4223  page_header_p->cont_free, page_header_p->offset_to_free_area, page_header_p->need_update_best_hint);
4224  n += snprintf (buffer + n, size - n, "IS_SAVING = %d\n", page_header_p->is_saving);
4225 }
4226 
4227 /*
4228  * spage_dump_slots () - Dump the slotted page array
4229  * return: void
4230  *
4231  * slot_p(in): Pointer to slotted page pointer array
4232  * num_slots(in): Number of slots
4233  * alignment(in): Alignment for records
4234  *
4235  * Note: The content of the record is not dumped by this function.
4236  * This function is used for debugging purposes.
4237  */
4238 static void
4239 spage_dump_slots (FILE * fp, const SPAGE_SLOT * slot_p, PGNSLOTS num_slots, unsigned short alignment)
4240 {
4241  int i;
4242  unsigned int waste;
4243 
4244  assert (slot_p != NULL);
4245 
4246  for (i = 0; i < num_slots; slot_p--, i++)
4247  {
4248  (void) fprintf (fp, "\nSlot-id = %2d, offset = %4d, type = %s", i, slot_p->offset_to_record,
4250  if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
4251  {
4252  waste = DB_WASTED_ALIGN (slot_p->record_length, alignment);
4253  (void) fprintf (fp, ", length = %4d, waste = %u", slot_p->record_length, waste);
4254  }
4255  (void) fprintf (fp, "\n");
4256  }
4257 }
4258 
4259 /*
4260  * spage_dump_record () -
4261  * return:
4262  *
4263  * fp(in/out):
4264  * page_p(in): Pointer to slotted page
4265  * slot_id(in): Slot identifier of desired record
4266  * slot_p(in): Pointer to slotted page pointer array
4267  */
4268 static void
4269 spage_dump_record (FILE * fp, PAGE_PTR page_p, PGSLOTID slot_id, SPAGE_SLOT * slot_p)
4270 {
4271  VFID *vfid;
4272  OID *oid;
4273  char *record_p;
4274  int i;
4275 
4276  if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
4277  {
4278  (void) fprintf (fp, "\nSlot-id = %2d\n", slot_id);
4279  switch (slot_p->record_type)
4280  {
4281  case REC_BIGONE:
4282  vfid = (VFID *) (page_p + slot_p->offset_to_record);
4283  fprintf (fp, "VFID = %d|%d\n", vfid->volid, vfid->fileid);
4284  break;
4285 
4286  case REC_RELOCATION:
4287  oid = (OID *) (page_p + slot_p->offset_to_record);
4288  fprintf (fp, "OID = %d|%d|%d\n", oid->volid, oid->pageid, oid->slotid);
4289  break;
4290 
4291  default:
4292  record_p = (char *) page_p + slot_p->offset_to_record;
4293  for (i = 0; i < (int) slot_p->record_length; i++)
4294  {
4295  (void) fprintf (fp, "%02X ", (unsigned char) (*record_p++));
4296  if (i % 20 == 19)
4297  {
4298  fputc ('\n', fp);
4299  }
4300  else if (i % 10 == 9)
4301  {
4302  fputc (' ', fp);
4303  }
4304  }
4305  (void) fprintf (fp, "\n");
4306  }
4307  }
4308  else
4309  {
4311 
4312  (void) fprintf (fp, "\nSlot-id = %2d has been deleted\n", slot_id);
4313  }
4314 }
4315 
4316 /*
4317  * spage_dump () - Dump an slotted page
4318  * return: void
4319  * pgptr(in): Pointer to slotted page
4320  * isrecord_printed(in): If true, records are printed in ascii format,
4321  * otherwise, the records are not printed.
4322  *
4323  * Note: The records are printed only when the value of isrecord_printed is
4324  * true. This function is used for debugging purposes.
4325  */
4326 void
4327 spage_dump (THREAD_ENTRY * thread_p, FILE * fp, PAGE_PTR page_p, int is_record_printed)
4328 {
4329  SPAGE_HEADER *page_header_p;
4330  SPAGE_SLOT *slot_p;
4331  int i;
4332 
4333  assert (page_p != NULL);
4334 
4335  (void) fprintf (fp, "\n*** Dumping pageid = %d of volume = %s ***\n", pgbuf_get_page_id (page_p),
4336  pgbuf_get_volume_label (page_p));
4337 
4338  page_header_p = (SPAGE_HEADER *) page_p;
4339  spage_dump_header (fp, page_header_p);
4340 
4341  /* Dump each slot and its corresponding record */
4342  slot_p = spage_find_slot (page_p, page_header_p, 0, false);
4343  spage_dump_slots (fp, slot_p, page_header_p->num_slots, page_header_p->alignment);
4344 
4345  if (is_record_printed)
4346  {
4347  (void) fprintf (fp, "\nRecords in ascii follow ...\n");
4348  for (i = 0; i < page_header_p->num_slots; slot_p--, i++)
4349  {
4350  spage_dump_record (fp, page_p, i, slot_p);
4351  }
4352  }
4353 
4355 #ifdef SPAGE_DEBUG
4356  spage_check (thread_p, page_p);
4357 #endif
4358 }
4359 
4360 #if !defined(NDEBUG)
4361 /*
4362  * spage_check_num_slots () - Check consistency of page. This function is used for
4363  * debugging purposes
4364  * return: true/false
4365  * ppage_p(in): Pointer to slotted page
4366  */
4367 bool
4369 {
4370  SPAGE_HEADER *page_header_p;
4371  SPAGE_SLOT *slot_p;
4372  int i, nrecs;
4373 
4374  assert (page_p != NULL);
4375 
4376  page_header_p = (SPAGE_HEADER *) page_p;
4377  SPAGE_VERIFY_HEADER (page_header_p);
4378 
4379  slot_p = spage_find_slot (page_p, page_header_p, 0, false);
4380 
4381  nrecs = 0;
4382  for (i = 0; i < page_header_p->num_slots; slot_p--, i++)
4383  {
4384  if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
4385  {
4386  nrecs++;
4387  }
4388  }
4389  assert (page_header_p->num_records == nrecs);
4390 
4391  return (page_header_p->num_records == nrecs) ? true : false;
4392 }
4393 #endif
4394 
4395 /*
4396  * spage_check () - Check consistency of page. This function is used for
4397  * debugging purposes
4398  * return: void
4399  * pgptr(in): Pointer to slotted page
4400  */
4401 int
4402 spage_check (THREAD_ENTRY * thread_p, PAGE_PTR page_p)
4403 {
4404  SPAGE_HEADER *page_header_p;
4405  SPAGE_SLOT *slot_p;
4406  char err_msg[1024];
4407  int used_length = 0;
4408  int i, nrecs;
4409 
4410  assert (page_p != NULL);
4411 
4412  page_header_p = (SPAGE_HEADER *) page_p;
4413  SPAGE_VERIFY_HEADER (page_header_p);
4414 
4415  slot_p = spage_find_slot (page_p, page_header_p, 0, false);
4416  used_length = (sizeof (SPAGE_HEADER) + sizeof (SPAGE_SLOT) * page_header_p->num_slots);
4417 
4418  nrecs = 0;
4419  for (i = 0; i < page_header_p->num_slots; slot_p--, i++)
4420  {
4421  if (slot_p->offset_to_record != SPAGE_EMPTY_OFFSET)
4422  {
4423  used_length += DB_ALIGN (slot_p->record_length, page_header_p->alignment);
4424  nrecs++;
4425  }
4426  }
4427  assert (page_header_p->num_records == nrecs);
4428 
4429  if (used_length + page_header_p->total_free > SPAGE_DB_PAGESIZE)
4430  {
4431  snprintf (err_msg, sizeof (err_msg),
4432  "spage_check: Inconsistent page = %d of volume = %s.\n"
4433  "(Used_space + tfree > SPAGE_DB_PAGESIZE\n (%d + %d) > %d \n %d > %d\n",
4434  pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p), used_length,
4435  page_header_p->total_free, SPAGE_DB_PAGESIZE, used_length + page_header_p->total_free,
4437 
4439  pgbuf_get_volume_label (page_p), err_msg);
4440 
4441  assert (false);
4442  return ER_SP_INVALID_HEADER;
4443  }
4444 
4445  if ((page_header_p->cont_free + page_header_p->offset_to_free_area +
4446  SSIZEOF (SPAGE_SLOT) * page_header_p->num_slots) > SPAGE_DB_PAGESIZE)
4447  {
4448  snprintf (err_msg, sizeof (err_msg),
4449  "spage_check: Inconsistent page = %d of volume = %s.\n"
4450  " (cfree + foffset + SIZEOF(SPAGE_SLOT) * nslots) > "
4451  " SPAGE_DB_PAGESIZE\n (%d + %d + (%zu * %d)) > %d\n %zu > %d\n", pgbuf_get_page_id (page_p),
4452  pgbuf_get_volume_label (page_p), page_header_p->cont_free, page_header_p->offset_to_free_area,
4453  sizeof (SPAGE_SLOT), page_header_p->num_slots, SPAGE_DB_PAGESIZE,
4454  (page_header_p->cont_free + page_header_p->offset_to_free_area
4455  + sizeof (SPAGE_SLOT) * page_header_p->num_slots), SPAGE_DB_PAGESIZE);
4456 
4458  pgbuf_get_volume_label (page_p), err_msg);
4459 
4460  assert (false);
4461  return ER_SP_INVALID_HEADER;
4462  }
4463 
4464  if (page_header_p->cont_free <= (int) -(page_header_p->alignment - 1))
4465  {
4466  snprintf (err_msg, sizeof (err_msg),
4467  "spage_check: Cfree %d is inconsistent in page = %d of volume = %s. Cannot be < -%d\n",
4468  page_header_p->cont_free, pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p),
4469  page_header_p->alignment);
4470 
4472  pgbuf_get_volume_label (page_p), err_msg);
4473 
4474  assert (false);
4475  return ER_SP_INVALID_HEADER;
4476  }
4477 
4478  /* Update any savings, before we check for any incosistencies */
4479  if (page_header_p->is_saving)
4480  {
4481  int other_saved_spaces = 0;
4482  int total_saved = spage_get_saved_spaces (thread_p, page_header_p, page_p,
4483  &other_saved_spaces);
4484 #if 1
4485  if (other_saved_spaces < 0)
4486 #else
4487  if (other_saved_spaces < 0 || total_saved > page_header_p->total_free)
4488 #endif
4489 
4490  {
4491  snprintf (err_msg, sizeof (err_msg),
4492  "spage_check: Other savings of %d is inconsistent in page = %d of volume = %s.\n",
4493  other_saved_spaces, pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
4494 
4496  pgbuf_get_volume_label (page_p), err_msg);
4497 
4498  assert (false);
4499  return ER_SP_INVALID_HEADER;
4500  }
4501 
4502  if (total_saved < 0)
4503  {
4504  snprintf (err_msg, sizeof (err_msg),
4505  "spage_check: Total savings of %d is inconsistent in page = %d of volume = %s. Cannot be < 0\n",
4506  total_saved, pgbuf_get_page_id (page_p), pgbuf_get_volume_label (page_p));
4507 
4509  pgbuf_get_volume_label (page_p), err_msg);
4510 
4511  assert (false);
4512  return ER_SP_INVALID_HEADER;
4513  }
4514  }
4515 
4516  return NO_ERROR;
4517 }
4518 
4519 /*
4520  * spage_check_slot_owner () -
4521  * return:
4522  *
4523  * page_p(in):
4524  * slot_id(in):
4525  */
4526 int
4528 {
4529  SPAGE_HEADER *page_header_p;
4530  SPAGE_SLOT *slot_p;
4531  TRANID tranid;
4532 
4533  assert (page_p != NULL);
4534 
4535  page_header_p = (SPAGE_HEADER *) page_p;
4536  SPAGE_VERIFY_HEADER (page_header_p);
4537 
4538  tranid = logtb_find_current_tranid (thread_p);
4539  slot_p = spage_find_slot (page_p, page_header_p, slot_id, false);
4540  if (slot_p == NULL)
4541  {
4542  assert (false);
4544  pgbuf_get_volume_label (page_p));
4545  return 0;
4546  }
4547 
4548  return (*(TRANID *) (page_p + slot_p->offset_to_record) == tranid);
4549 }
4550 
4551 /*
4552  * spage_is_unknown_slot () -
4553  * return:
4554  *
4555  * slot_id(in): Slot identifier of desired record
4556  * page_header_p(in): Pointer to header of slotted page
4557  * slot_p(in): Pointer to slotted page pointer array
4558  */
4559 STATIC_INLINE bool
4560 spage_is_unknown_slot (PGSLOTID slot_id, SPAGE_HEADER * page_header_p, SPAGE_SLOT * slot_p)
4561 {
4562  unsigned int max_offset;
4563 
4564  assert (slot_p != NULL);
4565  SPAGE_VERIFY_HEADER (page_header_p);
4566 
4567  max_offset = SPAGE_DB_PAGESIZE - page_header_p->num_slots * sizeof (SPAGE_SLOT);
4568 
4570  assert_release (slot_p->offset_to_record <= max_offset);
4571 
4572  return (slot_id < 0 || slot_id >= page_header_p->num_slots || slot_p->offset_to_record == SPAGE_EMPTY_OFFSET
4573  || slot_p->offset_to_record < sizeof (SPAGE_HEADER) || slot_p->offset_to_record > max_offset);
4574 }
4575 
4576 /*
4577  * spage_find_slot () -
4578  * return:
4579  *
4580  * page_p(in): Pointer to slotted page
4581  * page_header_p(in): Pointer to header of slotted page
4582  * slot_id(in): Slot identifier of desired record
4583  * is_unknown_slot_check(in):
4584  */
4586 spage_find_slot (PAGE_PTR page_p, SPAGE_HEADER * page_header_p, PGSLOTID slot_id, bool is_unknown_slot_check)
4587 {
4588  SPAGE_SLOT *slot_p;
4589 
4590  assert (page_p != NULL);
4591  SPAGE_VERIFY_HEADER (page_header_p);
4592 
4593  slot_p = (SPAGE_SLOT *) (page_p + SPAGE_DB_PAGESIZE - sizeof (SPAGE_SLOT));
4594  slot_p -= slot_id;
4595 
4596  if (is_unknown_slot_check)
4597  {
4598  if (spage_is_unknown_slot (slot_id, page_header_p, slot_p))
4599  {
4600  return NULL;
4601  }
4602  }
4603 
4604  return slot_p;
4605 }
4606 
4607 /*
4608  * spage_has_enough_total_space () -
4609  * return:
4610  *
4611  * page_p(in): Pointer to slotted page
4612  * page_header_p(in): Pointer to header of slotted page
4613  * space(in):
4614  */
4615 static bool
4616 spage_has_enough_total_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space)
4617 {
4618  TRANID tranid;
4619  int total_saved = 0;
4620 
4621  assert (page_p != NULL);
4622  SPAGE_VERIFY_HEADER (page_header_p);
4623 
4624  if (space <= 0)
4625  {
4626  return true;
4627  }
4628 
4629  if (page_header_p->is_saving)
4630  {
4631  tranid = logtb_find_current_tranid (thread_p);
4632 
4633  if (logtb_is_active (thread_p, tranid))
4634  {
4635  total_saved = spage_get_total_saved_spaces (thread_p, page_header_p, page_p);
4636  }
4637  assert (total_saved >= 0);
4638 
4639  return (space <= (page_header_p->total_free - total_saved));
4640  }
4641  else
4642  {
4643  return (space <= page_header_p->total_free);
4644  }
4645 }
4646 
4647 /*
4648  * spage_has_enough_contiguous_space () -
4649  * return:
4650  *
4651  * page_p(in): Pointer to slotted page
4652  * page_header_p(in): Pointer to header of slotted page
4653  * space(in)
4654  */
4655 static bool
4656 spage_has_enough_contiguous_space (THREAD_ENTRY * thread_p, PAGE_PTR page_p, SPAGE_HEADER * page_header_p, int space)
4657 {
4658  assert (page_p != NULL);
4659  SPAGE_VERIFY_HEADER (page_header_p);
4660 
4661  return (space <= page_header_p->cont_free || spage_compact (thread_p, page_p) == NO_ERROR);
4662 }
4663 
4664 /*
4665  * spage_add_contiguous_free_space () -
4666  * return:
4667  *
4668  * page_p(in): Pointer to slotted page
4669  * space(in):
4670  */
4671 static void
4673 {
4674  SPAGE_HEADER *page_header_p;
4675 
4676  page_header_p = (SPAGE_HEADER *) page_p;
4677  SPAGE_VERIFY_HEADER (page_header_p);
4678 
4679  page_header_p->total_free += space;
4680  page_header_p->cont_free += space;
4681  page_header_p->offset_to_free_area -= space;
4682 
4683  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
4684 
4685  spage_verify_header (page_p);
4686 }
4687 
4688 /*
4689  * spage_next_record_dont_skip_empty () - Get next slot without skipping
4690  * empty records.
4691  *
4692  * return :
4693  * page_p (in) :
4694  * out_slot_id_p (in) :
4695  * record_descriptor_p (in) :
4696  * is_peeking (in) :
4697  */
4698 SCAN_CODE
4699 spage_next_record_dont_skip_empty (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p,
4700  int is_peeking)
4701 {
4702  return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_NEXT, false);
4703 }
4704 
4705 /*
4706  * spage_previous_record_dont_skip_empty () - Get previous slot without
4707  * skipping empty records.
4708  *
4709  * return :
4710  * page_p (in) :
4711  * out_slot_id_p (in) :
4712  * record_descriptor_p (in) :
4713  * is_peeking (in) :
4714  */
4715 SCAN_CODE
4716 spage_previous_record_dont_skip_empty (PAGE_PTR page_p, PGSLOTID * out_slot_id_p, RECDES * record_descriptor_p,
4717  int is_peeking)
4718 {
4719  return spage_search_record (page_p, out_slot_id_p, record_descriptor_p, is_peeking, SPAGE_SEARCH_PREV, false);
4720 }
4721 
4722 /*
4723  * spage_get_page_header_info () - Obtain page information for spage_header.
4724  *
4725  * return :
4726  * page_p (in) :
4727  * page_header_info (in) :
4728  */
4729 SCAN_CODE
4730 spage_get_page_header_info (PAGE_PTR page_p, DB_VALUE ** page_header_info)
4731 {
4732  SPAGE_HEADER *page_header_p;
4733 
4734  if (page_p == NULL || page_header_info == NULL)
4735  {
4736  return S_SUCCESS;
4737  }
4738 
4739  page_header_p = (SPAGE_HEADER *) page_p;
4740  SPAGE_VERIFY_HEADER (page_header_p);
4741 
4742  db_make_int (page_header_info[HEAP_PAGE_INFO_NUM_SLOTS], page_header_p->num_slots);
4743  db_make_int (page_header_info[HEAP_PAGE_INFO_NUM_RECORDS], page_header_p->num_records);
4744  db_make_int (page_header_info[HEAP_PAGE_INFO_ANCHOR_TYPE], page_header_p->anchor_type);
4745  db_make_int (page_header_info[HEAP_PAGE_INFO_ALIGNMENT], page_header_p->alignment);
4746  db_make_int (page_header_info[HEAP_PAGE_INFO_TOTAL_FREE], page_header_p->total_free);
4747  db_make_int (page_header_info[HEAP_PAGE_INFO_CONT_FREE], page_header_p->cont_free);
4748  db_make_int (page_header_info[HEAP_PAGE_INFO_OFFSET_TO_FREE_AREA], page_header_p->offset_to_free_area);
4749  db_make_int (page_header_info[HEAP_PAGE_INFO_IS_SAVING], page_header_p->is_saving);
4750  db_make_int (page_header_info[HEAP_PAGE_INFO_UPDATE_BEST], page_header_p->need_update_best_hint);
4751 
4752  return S_SUCCESS;
4753 }
4754 
4755 /*
4756  * spage_get_record_offset () - Find the offset of the record associated with
4757  * the given slot on the given page
4758  *
4759  * return : Record offset.
4760  * page_p (in): Pointer to slotted page.
4761  * slotid (in): Slot identifier of record.
4762  */
4763 int
4765 {
4766  SPAGE_HEADER *page_header_p;
4767  SPAGE_SLOT *slot_p;
4768 
4769  assert (page_p != NULL);
4770 
4771  page_header_p = (SPAGE_HEADER *) page_p;
4772  SPAGE_VERIFY_HEADER (page_header_p);
4773 
4774  slot_p = spage_find_slot (page_p, page_header_p, slot_id, true);
4775  if (slot_p == NULL)
4776  {
4778  pgbuf_get_volume_label (page_p));
4779  return -1;
4780  }
4781 
4782  return slot_p->offset_to_record;
4783 }
4784 
4785 /*
4786  * spage_get_slot () - Looks for the slot with slot_id identifier in page_p
4787  * and returns a SPAGE_SLOT. Does not check for unknown
4788  * slots.
4789  *
4790  * return : SPAGE_SLOT.
4791  * page_p (in) : Pointer to slotted page.
4792  * slot_id (in) : Slot identifier.
4793  */
4794 SPAGE_SLOT *
4796 {
4797  SPAGE_HEADER *page_header_p = NULL;
4798 
4799  assert (page_p != NULL);
4800 
4801  page_header_p = (SPAGE_HEADER *) page_p;
4802  SPAGE_VERIFY_HEADER (page_header_p);
4803 
4804  if (slot_id < 0 || slot_id >= page_header_p->num_slots)
4805  {
4806  return NULL;
4807  }
4808 
4809  return spage_find_slot (page_p, page_header_p, slot_id, false);
4810 }
4811 
4812 /*
4813  * spage_vacuum_slot () - Vacuum the slot of an invisible object version.
4814  *
4815  * return : void.
4816  * thread_p (in) : Thread entry.
4817  * page_p (in) : Page pointer.
4818  * slotid (in) : Record slot id.
4819  * reusable (in) : True if slots are reusable, false if they are
4820  * referable.
4821  *
4822  * NOTE: Vacuuming the slot can be done in two ways depending on reusable:
4823  * 1. Reusable = false: Replace slot with
4824  * REC_MARKDELETED (deleted slot, but cannot be reused since there
4825  * may still be references pointing to this slot).
4826  * 2. Reusable = true: Slot is always replaced with
4827  * REC_DELETED_WILL_REUSE. Since there are no references to this
4828  * slot and this version is invisible, there is no point in keeping
4829  * links to newer versions.
4830  */
4831 void
4832 spage_vacuum_slot (THREAD_ENTRY * thread_p, PAGE_PTR page_p, PGSLOTID slotid, bool reusable)
4833 {
4834  SPAGE_HEADER *page_header_p = (SPAGE_HEADER *) page_p;
4835  SPAGE_SLOT *slot_p = NULL;
4836  int waste;
4837  int free_space;
4838 
4839  SPAGE_VERIFY_HEADER (page_header_p);
4840 
4841  assert (spage_is_valid_anchor_type (page_header_p->anchor_type));
4842 
4843  slot_p = spage_find_slot (page_p, page_header_p, slotid, false);
4844  if (slot_p->record_type == REC_MARKDELETED || slot_p->record_type == REC_DELETED_WILL_REUSE)
4845  {
4846  /* Vacuum error log */
4847  vacuum_er_log_error (VACUUM_ER_LOG_ERROR, "Object (%d, %d, %d) was already vacuumed",
4848  pgbuf_get_vpid_ptr (page_p)->volid, pgbuf_get_vpid_ptr (page_p)->pageid, slotid);
4849  }
4850 
4851  /* Slot is deleted */
4852  page_header_p->num_records--;
4853  waste = DB_WASTED_ALIGN (slot_p->record_length, page_header_p->alignment);
4854  free_space = slot_p->record_length + waste;
4855  page_header_p->total_free += free_space;
4856 
4858  if (reusable)
4859  {
4861  }
4862  else
4863  {
4864  slot_p->record_type = REC_MARKDELETED;
4865  }
4866  SPAGE_VERIFY_HEADER (page_header_p);
4867 
4868 #ifdef SPAGE_DEBUG
4869  spage_check (thread_p, page_p);
4870 #endif
4871 }
4872 
4873 /*
4874  * spage_reduce_contiguous_free_space () -
4875  * return:
4876  *
4877  * page_p(in): Pointer to slotted page
4878  * space(in):
4879  */
4880 static void
4882 {
4883  SPAGE_HEADER *page_header_p;
4884 
4885  page_header_p = (SPAGE_HEADER *) page_p;
4886  SPAGE_VERIFY_HEADER (page_header_p);
4887 
4888  page_header_p->total_free -= space;
4889  page_header_p->cont_free -= space;
4890  page_header_p->offset_to_free_area += space;
4891 
4892  ASSERT_ALIGN ((char *) page_p + page_header_p->offset_to_free_area, page_header_p->alignment);
4893 
4894  spage_verify_header (page_p);
4895 }
4896 
4897 
4898 /*
4899  * spage_header_start_scan () -
4900  * return: NO_ERROR, or ER_code
4901  *
4902  * thread_p(in):
4903  * show_type(in):
4904  * arg_values(in):
4905  * arg_cnt(in):
4906  * ptr(out): allocate new context. should free by end_scan() function
4907  */
4908 int
4909 spage_header_start_scan (THREAD_ENTRY * thread_p, int show_type, DB_VALUE ** arg_values, int arg_cnt, void **ptr)
4910 {
4911  int error = NO_ERROR;
4912  DB_VALUE *arg_val0, *arg_val1;
4913  SPAGE_HEADER_CONTEXT *ctx;
4914  PAGE_PTR pgptr = NULL;
4915  PAGE_TYPE ptype;
4916 
4917  *ptr = NULL;
4918 
4919  arg_val0 = arg_values[0];
4920  arg_val1 = arg_values[1];
4921 
4922  assert (arg_val0 != NULL && DB_VALUE_TYPE (arg_val0) == DB_TYPE_INTEGER);
4923  assert (arg_val1 != NULL && DB_VALUE_TYPE (arg_val1) == DB_TYPE_INTEGER);
4924 
4925  ctx = (SPAGE_HEADER_CONTEXT *) db_private_alloc (thread_p, sizeof (SPAGE_HEADER_CONTEXT));
4926 
4927  if (ctx == NULL)
4928  {
4929  assert (er_errid () != NO_ERROR);
4930  error = er_errid ();
4931  goto exit_on_error;
4932  }
4933 
4934  ctx->vpid.volid = db_get_int (arg_val0);
4935  ctx->vpid.pageid = db_get_int (arg_val1);
4936 
4938  if (error != NO_ERROR)
4939  {
4940  ASSERT_ERROR ();
4941  goto exit_on_error;
4942  }
4943  if (pgptr == NULL)
4944  {
4945  /* page deallocated. */
4947  error = ER_DIAG_PAGE_NOT_FOUND;
4948  goto exit_on_error;
4949  }
4950 
4951  ptype = pgbuf_get_page_ptype (thread_p, pgptr);
4952  if (!spage_is_slotted_page_type (ptype))
4953  {
4954  pgbuf_unfix_and_init (thread_p, pgptr);
4955 
4957  error = ER_DIAG_NOT_SPAGE;
4958  goto exit_on_error;
4959  }
4960 
4961  memcpy (&ctx->header, pgptr, sizeof (ctx->header));
4962  pgbuf_unfix_and_init (thread_p, pgptr);
4963 
4964  *ptr = ctx;
4965 
4966  return NO_ERROR;
4967 
4968 exit_on_error:
4969  if (ctx != NULL)
4970  {
4971  db_private_free_and_init (thread_p, ctx);
4972  }
4973 
4974  return error;
4975 }
4976 
4977 /*
4978  * spage_header_next_scan () -
4979  * return: NO_ERROR, or ER_code
4980  *
4981  * thread_p(in):
4982  * cursor(in):
4983  * out_values(in):
4984  * out_cnt(in):
4985  * ptr(in): context pointer
4986  */
4987 SCAN_CODE
4988 spage_header_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt, void *ptr)
4989 {
4990  int idx = 0;
4992  SPAGE_HEADER *header = (SPAGE_HEADER *) (&ctx->header);
4993 
4994  if (cursor >= 1)
4995  {
4996  return S_END;
4997  }
4998 
4999  db_make_int (out_values[idx], ctx->vpid.volid);
5000  idx++;
5001 
5002  db_make_int (out_values[idx], ctx->vpid.pageid);
5003  idx++;
5004 
5005  db_make_int (out_values[idx], header->num_slots);
5006  idx++;
5007 
5008  db_make_int (out_values[idx], header->num_records);
5009  idx++;
5010 
5011  db_make_string (out_values[idx], spage_anchor_flag_string (header->anchor_type));
5012  idx++;
5013 
5014  db_make_string (out_values[idx], spage_alignment_string (header->alignment));
5015  idx++;
5016 
5017  db_make_int (out_values[idx], header->total_free);
5018  idx++;
5019 
5020  db_make_int (out_values[idx], header->cont_free);
5021  idx++;
5022 
5023  db_make_int (out_values[idx], header->offset_to_free_area);
5024  idx++;
5025 
5026  db_make_int (out_values[idx], header->need_update_best_hint);
5027  idx++;
5028 
5029  db_make_int (out_values[idx], header->is_saving);
5030  idx++;
5031 
5032  db_make_int (out_values[idx], header->flags);
5033  idx++;
5034 
5035  assert (idx == out_cnt);
5036 
5037  return S_SUCCESS;
5038 }
5039 
5040 /*
5041  * spage_header_end_scan () - free the context
5042  * return: NO_ERROR, or ER_code
5043  *
5044  * thread_p(in):
5045  * ptr(in): context pointer
5046  */
5047 int
5048 spage_header_end_scan (THREAD_ENTRY * thread_p, void **ptr)
5049 {
5050  if (*ptr != NULL)
5051  {
5052  db_private_free_and_init (thread_p, *ptr);
5053  }
5054 
5055  return NO_ERROR;
5056 }
5057 
5058 /*
5059  * spage_slots_start_scan () -
5060  * return: NO_ERROR, or ER_code
5061  *
5062  * thread_p(in):
5063  * show_type(in):
5064  * arg_values(in):
5065  * arg_cnt(in):
5066  * ptr(out): allocate new context. should free by end_scan() function
5067  */
5068 int
5069 spage_slots_start_scan (THREAD_ENTRY * thread_p, int show_type, DB_VALUE ** arg_values, int arg_cnt, void **ptr)
5070 {
5071  int error = NO_ERROR;
5072  SPAGE_HEADER *header = NULL;
5073  DB_VALUE *arg_val0, *arg_val1;
5074  SPAGE_SLOTS_CONTEXT *ctx;
5075  PAGE_PTR pgptr = NULL;
5076  PAGE_TYPE ptype;
5077 
5078  *ptr = NULL;
5079 
5080  arg_val0 = arg_values[0];
5081  arg_val1 = arg_values[1];
5082 
5083  assert (arg_val0 != NULL && DB_VALUE_TYPE (arg_val0) == DB_TYPE_INTEGER);
5084  assert (arg_val1 != NULL && DB_VALUE_TYPE (arg_val1) == DB_TYPE_INTEGER);
5085 
5086  ctx = (SPAGE_SLOTS_CONTEXT *) db_private_alloc (thread_p, sizeof (SPAGE_SLOTS_CONTEXT));
5087 
5088  if (ctx == NULL)
5089  {
5090  assert (er_errid () != NO_ERROR);
5091  error = er_errid ();
5092  goto exit_on_error;
5093  }
5094 
5095  memset (ctx, 0, sizeof (SPAGE_SLOTS_CONTEXT));
5096 
5097  ctx->vpid.volid = db_get_int (arg_val0);
5098  ctx->vpid.pageid = db_get_int (arg_val1);
5099 
5101  if (error != NO_ERROR)
5102  {
5103  ASSERT_ERROR ();
5104  goto exit_on_error;
5105  }
5106  if (pgptr == NULL)
5107  {
5108  /* page deallocated. */
5110  error = ER_DIAG_PAGE_NOT_FOUND;
5111  goto exit_on_error;
5112  }
5113 
5114  ctx->pgptr = (PAGE_PTR) db_private_alloc (thread_p, SPAGE_DB_PAGESIZE);
5115  if (ctx->pgptr == NULL)
5116  {
5117  assert (er_errid () != NO_ERROR);
5118  error = er_errid ();
5119  goto exit_on_error;
5120  }
5121 
5122  ptype = pgbuf_get_page_ptype (thread_p, pgptr);
5123  if (!spage_is_slotted_page_type (ptype))
5124  {
5125  pgbuf_unfix_and_init (thread_p, pgptr);
5127  error = ER_DIAG_NOT_SPAGE;
5128  goto exit_on_error;
5129  }
5130 
5131  memcpy (ctx->pgptr, pgptr, SPAGE_DB_PAGESIZE);
5132  pgbuf_unfix_and_init (thread_p, pgptr);
5133 
5134  header = (SPAGE_HEADER *) ctx->pgptr;
5135  ctx->slot = spage_find_slot ((PAGE_PTR) header, header, 0, false);
5136 
5137  *ptr = ctx;
5138 
5139  return NO_ERROR;
5140 
5141 exit_on_error:
5142  if (ctx != NULL)
5143  {
5144  if (ctx->pgptr != NULL)
5145  {
5146  db_private_free (thread_p, ctx->pgptr);
5147  }
5148 
5149  db_private_free_and_init (thread_p, ctx);
5150  }
5151 
5152  return error;
5153 }
5154 
5155 /*
5156  * spage_slots_next_scan () -
5157  * return: NO_ERROR, or ER_code
5158  *
5159  * thread_p(in):
5160  * cursor(in):
5161  * out_values(in):
5162  * out_cnt(in):
5163  * ptr(in): context pointer
5164  */
5165 SCAN_CODE
5166 spage_slots_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt, void *ptr)
5167 {
5168  int idx = 0;
5170  SPAGE_HEADER *header = (SPAGE_HEADER *) ctx->pgptr;
5171 
5172  if (cursor >= header->num_slots)
5173  {
5174  return S_END;
5175  }
5176 
5177  /*
5178  * In the case of read a arbitrary specified page as slotted page,
5179  * num_slots of SPAGE_HEADER is meaningless data.
5180  * num_slots maybe too big to cause slot header address out of the page range.
5181  */
5182  if ((char *) ctx->slot < ((char *) header) + sizeof (SPAGE_HEADER))
5183  {
5184  return S_END;
5185  }
5186 
5187  db_make_int (out_values[idx], ctx->vpid.volid);
5188  idx++;
5189 
5190  db_make_int (out_values[idx], ctx->vpid.pageid);
5191  idx++;
5192 
5193  db_make_int (out_values[idx], cursor);
5194  idx++;
5195 
5196  db_make_int (out_values[idx], ctx->slot->offset_to_record);
5197  idx++;
5198 
5199  db_make_string (out_values[idx], spage_record_type_string (ctx->slot->record_type));
5200  idx++;
5201 
5202  db_make_int (out_values[idx], ctx->slot->record_length);
5203  idx++;
5204 
5205  db_make_int (out_values[idx], DB_WASTED_ALIGN (ctx->slot->record_length, header->alignment));
5206  idx++;
5207 
5208  assert (idx == out_cnt);
5209 
5210  ctx->slot--;
5211 
5212  return S_SUCCESS;
5213 }
5214 
5215 /*
5216  * spage_slots_end_scan () - free the context
5217  * return: NO_ERROR, or ER_code
5218  *
5219  * thread_p(in):
5220  * ptr(in): context pointer
5221  */
5222 int
5223 spage_slots_end_scan (THREAD_ENTRY * thread_p, void **ptr)
5224 {
5225  SPAGE_SLOTS_CONTEXT *ctx = (SPAGE_SLOTS_CONTEXT *) * ptr;
5226 
5227  if (ctx != NULL)
5228  {
5229  if (ctx->pgptr != NULL)
5230  {
5231  db_private_free (thread_p, ctx->pgptr);
5232  }
5233 
5234  db_private_free (thread_p, ctx);
5235  *ptr = NULL;
5236  }
5237 
5238  return NO_ERROR;
5239 }
5240 
5241 /*
5242  * spage_need_compact () - Checks if the page needs compact
5243  * return: true if page needs compact, false otherwise
5244  *
5245  * thread_p(in):
5246  * page_p(in):
5247  */
5248 bool
5250 {
5251  SPAGE_HEADER *page_header_p;
5252 
5253  assert (page_p != NULL);
5254 
5255  page_header_p = (SPAGE_HEADER *) page_p;
5256  SPAGE_VERIFY_HEADER (page_header_p);
5257 
5258  /* estimated gain after compact is >= 5% of page */
5259  if (page_header_p->total_free - page_header_p->cont_free >= SPAGE_DB_PAGESIZE / 20)
5260  {
5261  return true;
5262  }
5263 
5264  return false;
5265 }
char * PAGE_PTR
TRANID logtb_find_current_tranid(THREAD_ENTRY *thread_p)
static int spage_save_head_uninit(void *entry_p)
Definition: slotted_page.c:308
static void spage_dump_header_to_string(char *buffer, int size, const SPAGE_HEADER *page_header_p)
static bool spage_is_slotted_page_type(PAGE_TYPE ptype)
#define vacuum_er_log_error(er_log_level, msg,...)
Definition: vacuum.h:69
#define ER_DIAG_PAGE_NOT_FOUND
Definition: error_code.h:1470
static void spage_add_contiguous_free_space(PAGE_PTR pgptr, int space)
#define NO_ERROR
Definition: error_code.h:46
static int spage_check_space(THREAD_ENTRY *thread_p, PAGE_PTR page_p, SPAGE_HEADER *page_header_p, int space)
int area_size
#define __attribute__(X)
Definition: porting.h:36
SPAGE_SLOT * slot
Definition: slotted_page.c:170
static bool spage_has_enough_contiguous_space(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, SPAGE_HEADER *sphdr, int space)
static const char * spage_record_type_string(INT16 record_type)
#define DB_ALIGN_BELOW(offset, align)
Definition: memory_alloc.h:87
#define ER_SP_OVERWRITE_WRONG_OFFSET
Definition: error_code.h:791
static void spage_dump_slots(FILE *fp, const SPAGE_SLOT *sptr, PGNSLOTS nslots, unsigned short alignment)
#define ASSERT_ERROR()
SCAN_CODE
int TRANID
int offset_to_free_area
Definition: slotted_page.h:70
bool spage_is_mvcc_updatable(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int delete_record_length, int insert_record_length)
static void spage_reduce_a_slot(PAGE_PTR page_p)
#define FLOAT_ALIGNMENT
Definition: memory_alloc.h:63
SPAGE_SAVE_ENTRY * next
Definition: slotted_page.c:95
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 spage_saving_hashmap_type spage_Saving_hashmap
Definition: slotted_page.c:154
static bool spage_has_enough_total_space(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, SPAGE_HEADER *sphdr, int space)
int spage_insert(THREAD_ENTRY *thread_p, PAGE_PTR page_p, RECDES *record_descriptor_p, PGSLOTID *out_slot_id_p)
#define SPAGE_VERIFY_HEADER(sphdr)
Definition: slotted_page.c:71
static bool spage_is_record_located_at_end(SPAGE_HEADER *page_header_p, SPAGE_SLOT *slot_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)
PAGEID pgbuf_get_page_id(PAGE_PTR pgptr)
Definition: page_buffer.c:4657
int db_get_int(const DB_VALUE *value)
static API_MUTEX mutex
Definition: api_util.c:72
#define ER_FAILED
Definition: error_code.h:47
void spage_initialize(THREAD_ENTRY *thread_p, PAGE_PTR page_p, INT16 slot_type, unsigned short alignment, bool is_saving)
const char * spage_alignment_string(unsigned short alignment)
int spage_overwrite(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int overwrite_offset, const RECDES *record_descriptor_p)
#define pthread_mutex_init(a, b)
Definition: slotted_page.c:55
#define ALWAYS_INLINE
SCAN_CODE spage_previous_record_dont_skip_empty(PAGE_PTR page_p, PGSLOTID *out_slot_id_p, RECDES *record_descriptor_p, int is_peeking)
SCAN_CODE spage_slots_next_scan(THREAD_ENTRY *thread_p, int cursor, DB_VALUE **out_values, int out_cnt, void *ptr)
SPAGE_SLOT * spage_get_slot(PAGE_PTR page_p, PGSLOTID slot_id)
Definition: lock_free.h:63
#define assert_release(e)
Definition: error_manager.h:96
void pgbuf_set_dirty(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, bool free_page)
Definition: page_buffer.c:4280
SPAGE_SAVE_HEAD * rstack
Definition: slotted_page.c:103
static int spage_compare_slot_offset(const void *arg1, const void *arg2)
#define SHORT_ALIGNMENT
Definition: memory_alloc.h:60
void spage_update_record_type(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, INT16 record_type)
SPAGE_SAVE_ENTRY * first
Definition: slotted_page.c:110
#define NULL_SLOTID
#define VACUUM_IS_THREAD_VACUUM_MASTER
Definition: vacuum.h:217
unsigned int reserved_bits
Definition: slotted_page.h:80
char * data
LOG_TDES * LOG_FIND_TDES(int tran_index)
Definition: log_impl.h:1095
struct spage_slot SPAGE_SLOT
Definition: slotted_page.h:84
PGNSLOTS num_records
Definition: slotted_page.h:64
int32_t pageid
Definition: dbtype_def.h:879
SPAGE_SAVE_HEAD * head
Definition: slotted_page.c:98
int spage_take_out(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int takeout_offset, int takeout_length)
LF_TRAN_SYSTEM spage_saving_Ts
Definition: lock_free.c:46
int er_errid(void)
#define SP_SUCCESS
Definition: slotted_page.h:50
bool spage_reclaim(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
PAGE_TYPE
#define PTR_ALIGN(addr, boundary)
Definition: memory_alloc.h:77
bool spage_check_num_slots(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
void spage_set_need_update_best_hint(THREAD_ENTRY *thread_p, PAGE_PTR page_p, bool need_update)
Definition: slotted_page.c:962
PGSLOTID spage_delete_for_recovery(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
const char * spage_anchor_flag_string(const INT16 anchor_type)
static int spage_put_helper(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, PGSLOTID slotid, int offset, const RECDES *recdes, bool is_append)
bool spage_is_slot_exist(PAGE_PTR page_p, PGSLOTID slot_id)
static int spage_find_empty_slot_at(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, PGSLOTID slotid, int length, INT16 type, SPAGE_SLOT **sptr)
int spage_header_start_scan(THREAD_ENTRY *thread_p, int show_type, DB_VALUE **arg_values, int arg_cnt, void **ptr)
static int rv
Definition: slotted_page.c:60
SCAN_CODE spage_get_record(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES *record_descriptor_p, int is_peeking)
bool spage_is_updatable(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int record_descriptor_length)
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)
bool find_or_insert(cubthread::entry *thread_p, Key &key, T *&t)
#define ER_SP_WRONG_NUM_SLOTS
Definition: error_code.h:96
void THREAD_ENTRY
SPAGE_SAVE_ENTRY * tran_next_save
Definition: slotted_page.c:97
#define NULL_PAGEID
int spage_slots_start_scan(THREAD_ENTRY *thread_p, int show_type, DB_VALUE **arg_values, int arg_cnt, void **ptr)
#define pgbuf_unfix_and_init(thread_p, pgptr)
Definition: page_buffer.h:63
static int spage_check_record_for_insert(RECDES *record_descriptor_p)
#define CHAR_ALIGNMENT
Definition: memory_alloc.h:59
int spage_get_record_length(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
int spage_update(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, const RECDES *record_descriptor_p)
void spage_finalize(THREAD_ENTRY *thread_p)
Definition: slotted_page.c:816
unsigned short alignment
Definition: slotted_page.h:67
int db_make_string(DB_VALUE *value, DB_CONST_C_CHAR str)
static void spage_reduce_contiguous_free_space(PAGE_PTR pgptr, int space)
PAGE_TYPE pgbuf_get_page_ptype(THREAD_ENTRY *thread_p, PAGE_PTR pgptr)
Definition: page_buffer.c:4675
int spage_max_record_size(void)
Definition: slotted_page.c:848
int spage_check(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
void spage_vacuum_slot(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slotid, bool reusable)
void spage_dump(THREAD_ENTRY *thread_p, FILE *fp, PAGE_PTR page_p, int is_record_printed)
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
#define ER_DIAG_NOT_SPAGE
Definition: error_code.h:1474
int spage_max_space_for_new_record(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
Definition: slotted_page.c:984
void spage_free_saved_spaces(THREAD_ENTRY *thread_p, void *first_save_entry)
Definition: slotted_page.c:382
static int spage_save_head_init(void *entry_p)
Definition: slotted_page.c:287
char * fileio_get_volume_label(VOLID vol_id, bool is_peek)
Definition: file_io.c:6182
int lf_callback_vpid_compare(void *vpid_1, void *vpid_2)
Definition: lock_free.c:154
#define assert(x)
void end_tran(cubthread::entry *thread_p)
void logpb_fatal_error_exit_immediately_wo_flush(THREAD_ENTRY *thread_p, const char *file_name, const int lineno, const char *fmt,...)
int spage_check_slot_owner(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
int32_t fileid
Definition: dbtype_def.h:886
SPAGE_SAVE_HEAD * next
Definition: slotted_page.c:104
#define ASSERT_ALIGN(ptr, alignment)
static int spage_get_saved_spaces(THREAD_ENTRY *thread_p, SPAGE_HEADER *page_header_p, PAGE_PTR page_p, int *other_saved_spaces)
Definition: slotted_page.c:690
T * find(cubthread::entry *thread_p, Key &key)
#define ER_GENERIC_ERROR
Definition: error_code.h:49
#define STATIC_INLINE
unsigned int record_length
Definition: slotted_page.h:88
#define VACUUM_IS_THREAD_VACUUM_WORKER
Definition: vacuum.h:216
int saved
Definition: slotted_page.c:94
bool erase_locked(cubthread::entry *thread_p, Key &key, T *&t)
const char * pgbuf_get_volume_label(PAGE_PTR pgptr)
Definition: page_buffer.c:4731
PGSLOTID PGNSLOTS
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
PGBUF_LATCH_MODE pgbuf_get_latch_mode(PAGE_PTR pgptr)
Definition: page_buffer.c:4633
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))
Definition: slotted_page.c:91
void spage_collect_statistics(PAGE_PTR page_p, int *npages, int *nrecords, int *rec_length)
unsigned int record_type
Definition: slotted_page.h:89
unsigned int lf_callback_vpid_hash(void *vpid, int htsize)
Definition: lock_free.c:140
int spage_compact(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
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)
SPAGE_SAVE_ENTRY * prev
Definition: slotted_page.c:96
PGLENGTH db_page_size(void)
bool logtb_is_current_active(THREAD_ENTRY *thread_p)
int spage_slots_end_scan(THREAD_ENTRY *thread_p, void **ptr)
int spage_mark_deleted_slot_as_reusable(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
short volid
Definition: dbtype_def.h:880
SCAN_CODE spage_get_page_header_info(PAGE_PTR page_p, DB_VALUE **page_header_info)
static int spage_save_space(THREAD_ENTRY *thread_p, SPAGE_HEADER *sphdr, PAGE_PTR pgptr, int save)
Definition: slotted_page.c:477
SCAN_CODE spage_header_next_scan(THREAD_ENTRY *thread_p, int cursor, DB_VALUE **out_values, int out_cnt, void *ptr)
#define NULL
Definition: freelistheap.h:34
static int spage_insert_data(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, RECDES *recdes, void *slotptr)
PGNSLOTS spage_number_of_records(PAGE_PTR page_p)
Definition: slotted_page.c:860
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)
#define ER_SP_SPLIT_WRONG_OFFSET
Definition: error_code.h:789
if(extra_options)
Definition: dynamic_load.c:958
#define pthread_mutex_destroy(a)
Definition: slotted_page.c:56
bool log_is_in_crash_recovery(void)
Definition: log_manager.c:476
static int success()
void init(lf_tran_system &transys, int entry_idx, int hash_size, int freelist_block_size, int freelist_block_count, lf_entry_descriptor &edesc)
static void spage_dump_saved_spaces_by_other_trans(THREAD_ENTRY *thread_p, FILE *fp, VPID *vpid)
Definition: slotted_page.c:767
#define db_private_free_and_init(thrd, ptr)
Definition: memory_alloc.h:141
static void spage_shift_slot_down(PAGE_PTR page_p, SPAGE_HEADER *page_header_p, SPAGE_SLOT *slot_p)
#define db_private_free(thrd, ptr)
Definition: memory_alloc.h:229
SPAGE_HEADER header
Definition: slotted_page.c:161
int spage_get_record_offset(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
#define db_private_alloc(thrd, size)
Definition: memory_alloc.h:227
SCAN_CODE spage_next_record_dont_skip_empty(PAGE_PTR page_p, PGSLOTID *out_slot_id_p, RECDES *record_descriptor_p, int is_peeking)
PGSLOTID spage_find_free_slot(PAGE_PTR page_p, SPAGE_SLOT **out_slot_p, PGSLOTID start_slot)
void pgbuf_get_vpid(PAGE_PTR pgptr, VPID *vpid)
Definition: page_buffer.c:4579
int spage_split(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset, PGSLOTID *out_new_slot_id_p)
static INLINE void spage_verify_header(PAGE_PTR page_p) __attribute__((ALWAYS_INLINE))
Definition: slotted_page.c:335
PGNSLOTS spage_number_of_slots(PAGE_PTR page_p)
Definition: slotted_page.c:879
static void error(const char *msg)
Definition: gencat.c:331
static int spage_save_head_free(void *entry_p)
Definition: slotted_page.c:275
static int spage_get_saved_spaces_by_other_trans(THREAD_ENTRY *thread_p, SPAGE_HEADER *sphdr, PAGE_PTR pgptr)
Definition: slotted_page.c:648
TRANID tranid
Definition: slotted_page.c:93
#define ER_SP_INVALID_HEADER
Definition: error_code.h:1443
LOG_TDES * LOG_FIND_CURRENT_TDES(THREAD_ENTRY *thread_p=NULL)
Definition: log_impl.h:1115
PGSLOTID spage_delete(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
static INLINE bool spage_is_unknown_slot(PGSLOTID slotid, SPAGE_HEADER *sphdr, SPAGE_SLOT *sptr) __attribute__((ALWAYS_INLINE))
#define pgbuf_fix_if_not_deallocated(thread_p, vpid, latch_mode, latch_condition, page)
Definition: page_buffer.h:441
#define LOG_FIND_THREAD_TRAN_INDEX(thrd)
Definition: perf_monitor.h:158
int spage_get_space_for_record(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
#define ER_SP_UNKNOWN_SLOTID
Definition: error_code.h:99
#define ARG_FILE_LINE
Definition: error_manager.h:44
static PGLENGTH spage_User_page_size
Definition: slotted_page.c:66
void spage_boot(THREAD_ENTRY *thread_p)
Definition: slotted_page.c:798
INT16 PGLENGTH
static LF_ENTRY_DESCRIPTOR spage_Saving_entry_descriptor
Definition: slotted_page.c:128
unsigned int offset_to_record
Definition: slotted_page.h:87
void * first_save_entry
Definition: log_impl.h:512
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)
INT16 PGSLOTID
#define SPAGE_DB_PAGESIZE
Definition: slotted_page.c:68
#define DOUBLE_ALIGNMENT
Definition: memory_alloc.h:64
unsigned int is_saving
Definition: slotted_page.h:73
#define pthread_mutex_unlock(a)
Definition: slotted_page.c:59
static void spage_shift_slot_up(PAGE_PTR page_p, SPAGE_HEADER *page_header_p, SPAGE_SLOT *slot_p)
#define free_and_init(ptr)
Definition: memory_alloc.h:147
#define DB_ALIGN(offset, align)
Definition: memory_alloc.h:84
SCAN_CODE spage_previous_record(PAGE_PTR page_p, PGSLOTID *out_slot_id_p, RECDES *record_descriptor_p, int is_peeking)
#define LONG_ALIGNMENT
Definition: memory_alloc.h:62
static void spage_dump_header(FILE *fp, const SPAGE_HEADER *sphdr)
#define SPAGE_SEARCH_PREV
Definition: slotted_page.c:64
#define DB_WASTED_ALIGN(offset, align)
Definition: memory_alloc.h:90
int spage_put(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, int offset, const RECDES *record_descriptor_p)
static INLINE SPAGE_SLOT * spage_find_slot(PAGE_PTR pgptr, SPAGE_HEADER *sphdr, PGSLOTID slotid, bool is_unknown_slot_check) __attribute__((ALWAYS_INLINE))
#define INT_ALIGNMENT
Definition: memory_alloc.h:61
static void spage_dump_record(FILE *Fp, PAGE_PTR page_p, PGSLOTID slot_id, SPAGE_SLOT *slot_p)
int spage_get_free_space_without_saving(THREAD_ENTRY *thread_p, PAGE_PTR page_p, bool *need_update)
Definition: slotted_page.c:925
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)
static SCAN_CODE spage_get_record_data(PAGE_PTR pgptr, SPAGE_SLOT *sptr, RECDES *recdes, bool ispeeking)
INT16 anchor_type
Definition: slotted_page.h:65
SCAN_CODE spage_next_record(PAGE_PTR page_p, PGSLOTID *out_slot_id_p, RECDES *record_descriptor_p, int is_peeking)
#define DB_VALUE_TYPE(value)
Definition: dbtype.h:72
#define SPAGE_OVERFLOW(offset)
Definition: slotted_page.c:118
int i
Definition: dynamic_load.c:954
pthread_mutex_t mutex
Definition: slotted_page.c:105
int spage_get_free_space(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
Definition: slotted_page.c:898
int spage_header_size(void)
Definition: slotted_page.c:837
#define INLINE
for(p=libs;*p;p++)
Definition: dynamic_load.c:968
int spage_slot_size(void)
Definition: slotted_page.c:827
INT16 type
bool spage_is_valid_anchor_type(const INT16 anchor_type)
Definition: slotted_page.c:364
static void spage_set_slot(SPAGE_SLOT *slot_p, int offset, int length, INT16 type)
#define SP_ERROR
Definition: slotted_page.h:49
static void * spage_save_head_alloc(void)
Definition: slotted_page.c:256
int db_make_int(DB_VALUE *value, const int num)
short volid
Definition: dbtype_def.h:887
struct spage_header SPAGE_HEADER
Definition: slotted_page.h:60
int spage_header_end_scan(THREAD_ENTRY *thread_p, void **ptr)
bool logtb_is_active(THREAD_ENTRY *thread_p, TRANID trid)
#define DONT_FREE
Definition: page_buffer.h:41
#define ER_SP_TAKEOUT_WRONG_OFFSET
Definition: error_code.h:790
#define ER_SP_BAD_INSERTION_SLOT
Definition: error_code.h:98
INT16 spage_get_record_type(PAGE_PTR page_p, PGSLOTID slot_id)
#define SP_DOESNT_FIT
Definition: slotted_page.h:51
int spage_merge(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID first_slot_id, PGSLOTID second_slot_id)
#define PEEK
Definition: file_io.h:74
#define VACUUM_ER_LOG_ERROR
Definition: vacuum.h:46
static int spage_add_new_slot(THREAD_ENTRY *thread_p, PAGE_PTR page_p, SPAGE_HEADER *page_header_p, int *out_space_p)
#define pthread_mutex_lock(a)
Definition: slotted_page.c:57
bool spage_need_compact(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
#define LF_EM_USING_MUTEX
Definition: lock_free.h:60
VPID * pgbuf_get_vpid_ptr(PAGE_PTR pgptr)
Definition: page_buffer.c:4609
#define SPAGE_SEARCH_NEXT
Definition: slotted_page.c:63
void start_tran(cubthread::entry *thread_p)
int spage_append(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, const RECDES *record_descriptor_p)
int spage_insert_at(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES *record_descriptor_p)
int lf_callback_vpid_copy(void *src, void *dest)
Definition: lock_free.c:169
int spage_insert_for_recovery(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES *record_descriptor_p)
PGNSLOTS num_slots
Definition: slotted_page.h:63
unsigned int need_update_best_hint
Definition: slotted_page.h:74
#define SPAGE_HEADER_FLAG_NONE
Definition: slotted_page.h:57
static int spage_get_total_saved_spaces(THREAD_ENTRY *thread_p, SPAGE_HEADER *page_header_p, PAGE_PTR page_p)
Definition: slotted_page.c:666