CUBRID Engine  latest
extendible_hash.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  * extendible_hash.c - Extendible hash manager
21  */
22 
23 #ident "$Id$"
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stddef.h>
29 #include <math.h>
30 #if defined(sun) || defined(HPUX)
31 #include <sys/types.h>
32 #include <netinet/in.h>
33 #endif
34 #if defined(_AIX)
35 #include <net/nh.h>
36 #endif
37 
38 #include "chartype.h"
39 #include "storage_common.h"
40 #include "memory_alloc.h"
41 #include "object_representation.h"
42 #include "error_manager.h"
43 #include "xserver_interface.h"
44 #include "log_manager.h"
45 #include "extendible_hash.h"
46 #include "page_buffer.h"
47 #include "lock_manager.h"
48 #include "slotted_page.h"
49 #include "file_manager.h"
50 #include "overflow_file.h"
51 #include "memory_hash.h" /* For hash functions */
52 #include "tz_support.h"
53 #include "db_date.h"
54 #include "thread_compat.hpp"
55 
56 #ifdef EHASH_DEBUG
57 #define EHASH_BALANCE_FACTOR 4 /* Threshold rate of no. of directory pointers over no. of bucket pages. If
58  * this threshold is exceeded, a warning is issued in the debugging mode */
59 #endif /* EHASH_DEBUG */
60 
61  /*
62  * NOTICE: The constants EHASH_OVERFLOW_RATE and UNDERFLOW_RATE must be
63  * less than 1. The following values are very appropriate. Avoid changing
64  * them unless absolutely necessary.
65  */
66 
67 #define EHASH_OVERFLOW_RATE 0.9 /* UPPER THRESHOLD for a merge operation. */
68 
69  /*
70  * After a bucket merge operation, up to what percent of the sibling bucket
71  * space (i.e. DB_PAGESIZE) can be full. If it is found that during a merge
72  * the sibling bucket will become too full the merge is delayed to avoid an
73  * immediate split on the sibling bucket. (Exception: if the bucket becomes
74  * completely empty, the merge operation is performed no matter how full the
75  * sibling bucket is). That is we try to avoid a yoyo behaviour (split,
76  * merge).
77  */
78 
79 #define EHASH_UNDERFLOW_RATE 0.4 /* LOWER THRESHOLD for a merge operation. */
80 
81  /*
82  * After a deletion operation, if the remaining records occupy less than
83  * this rate of bucket space (i.e. DB_PAGESIZE) then the bucket is tried to
84  * be merged with its sibling bucket.
85  */
86 
87 /* Conversion of these rates into absolute values */
88 #define EHASH_OVERFLOW_THRESHOLD (EHASH_OVERFLOW_RATE * DB_PAGESIZE)
89 #define EHASH_UNDERFLOW_THRESHOLD (EHASH_UNDERFLOW_RATE * DB_PAGESIZE)
90 
91 /* Number of bits pseudo key consists of */
92 #define EHASH_HASH_KEY_BITS (sizeof(EHASH_HASH_KEY) * 8)
93 
94 /* Number of bits the short data type consists of */
95 #define EHASH_SHORT_BITS (sizeof(short) * 8)
96 
97 typedef unsigned int EHASH_HASH_KEY; /* Pseudo_key type */
98 
99 /* Extendible hashing directory header */
102 {
103  /* Fields should be ordered according to their sizes */
104  VFID bucket_file; /* bucket file identifier */
105  VFID overflow_file; /* overflow (buckets) file identifier */
106 
107  /* Each one keeps the number of buckets having a local depth equal to the index value of counter. Used for noticing
108  * directory shrink condition */
110 
111  DB_TYPE key_type; /* type of the keys */
112  short depth; /* global depth of the directory */
113  char alignment; /* alignment value used on slots of bucket pages */
114 };
115 
116 /* Directory elements : Pointer to the bucket */
119 {
120  VPID bucket_vpid; /* bucket pointer */
121 };
122 
123 /* Each bucket has a header area to store its local depth */
126 {
127  char local_depth; /* The local depth of the bucket */
128 };
129 
130 typedef enum
131 {
133  EHASH_BUCKET_FULL, /* Bucket full condition; used in insertion */
134  EHASH_BUCKET_UNDERFLOW, /* Bucket underflow condition; used in deletion */
135  EHASH_BUCKET_EMPTY, /* Bucket empty condition; used in deletion */
139 } EHASH_RESULT;
140 
141 /* Recovery structures */
144 {
145  /* The "vpid" is repeated "count" times */
147  int count;
148 };
149 
150 /* Definitions about long strings */
151 #define EHASH_LONG_STRING_PREFIX_SIZE 10
152 
153 /* Directory header size is aligned to size of integer */
154 #define EHASH_DIR_HEADER_SIZE \
155  ((ssize_t) (((sizeof(EHASH_DIR_HEADER) + sizeof(int) - 1 ) / sizeof(int) ) * sizeof(int)))
156 
157 /* Maximum size of a string key; 16 is for two slot indices */
158 #define EHASH_MAX_STRING_SIZE \
159  (DB_PAGESIZE - (SSIZEOF(EHASH_BUCKET_HEADER) + 16))
160 
161 /* Number of pointers the first page of the directory contains */
162 #define EHASH_NUM_FIRST_PAGES \
163  ((DB_PAGESIZE - EHASH_DIR_HEADER_SIZE) / SSIZEOF (EHASH_DIR_RECORD))
164 
165 /* Offset of the last pointer in the first directory page */
166 #define EHASH_LAST_OFFSET_IN_FIRST_PAGE \
167  (EHASH_DIR_HEADER_SIZE + (EHASH_NUM_FIRST_PAGES - 1) * sizeof(EHASH_DIR_RECORD))
168 
169 /* Number of pointers for each directory page (other than the first one) */
170 #define EHASH_NUM_NON_FIRST_PAGES \
171  (DB_PAGESIZE / sizeof(EHASH_DIR_RECORD))
172 
173 /* Offset of the last pointer in the other directory pages */
174 #define EHASH_LAST_OFFSET_IN_NON_FIRST_PAGE \
175  ((EHASH_NUM_NON_FIRST_PAGES - 1) * sizeof(EHASH_DIR_RECORD))
176 
177 /*
178  * GETBITS
179  *
180  * value: value from which bits are extracted; its type should be
181  * "unsigned int"
182  * pos: first bit position (left adjusted) of the field
183  * n: length of the field to be extracted
184  *
185  * Note: Returns the n-bit field of key that begins at left adjusted
186  * position pos. The type of key is supposed to be unsigned
187  * so that when it is right-shifted vacated bits will be filled
188  * with zeros, not sign bits.
189  */
190 /* TODO: ~0: M2 64-bit */
191 #define GETBITS(value, pos, n) \
192  ( ((value) >> ( EHASH_HASH_KEY_BITS - (pos) - (n) + 1)) & (~(~0UL << (n))) )
193  /* Plus 1 since bits are numbered from 1 to 32 */
194 
195 /*
196  * FIND_OFFSET
197  *
198  * hash_key: pseudo key to map to a directory pointer
199  * depth: depth of directory: tells how many bits to use
200  *
201  * Note: This macro maps a hash_key to an entry in a directory of one
202  * area. It extracts first "depth" bits from the left side of
203  * "hash_key" and returns this value.
204  *
205  */
206 
207 #define FIND_OFFSET(hash_key, depth) (GETBITS((hash_key), 1, (depth)))
208 
209 /*
210  * GETBIT
211  * return: int
212  * word: computer word from which the bit is extracted.
213  * Its type should be "unsigned int".
214  * pos: bit position (left adjusted) of word to return
215  *
216  *
217  * Note: Returns the value of the bit (in "pos" position) of a "word".
218  *
219  */
220 
221 #define GETBIT(word, pos) (GETBITS((word), (pos), 1))
222 
223 
224 /*
225  * SETBIT
226  *
227  * word: computer word whose bit is set
228  * pos: bit position (left adjusted) of word to be set to
229  *
230  * Note: Returns a value identical to "word" except the "pos" bit
231  * is set to one.
232  */
233 
234 #define SETBIT(word, pos) ( (word) | (1 << (EHASH_HASH_KEY_BITS - (pos))) )
235 
236 /*
237  * CLEARBIT
238  *
239  * word: computer word whose bit is to be cleared
240  * Nth_most_significant_bit: bit position of word to be set to
241  * key_side: which side of the word should be used
242  *
243  * Note: Returns a value identical to "word" except the "pos" bit
244  * is cleared (zeroed).
245  */
246 
247 #define CLEARBIT(word, pos) ( (word) & ~(1 << (EHASH_HASH_KEY_BITS - (pos))) )
248 
249 #if defined (ENABLE_UNUSED_FUNCTION)
250 static int ehash_ansi_sql_strncmp (const char *s, const char *t, int max);
251 #endif
252 static char *ehash_allocate_recdes (RECDES * recdes, int size, short type);
253 static void ehash_free_recdes (RECDES * recdes);
254 #if defined (ENABLE_UNUSED_FUNCTION)
255 static int ehash_compare_overflow (THREAD_ENTRY * thread_p, const VPID * ovf_vpid, char *key, int *comp_result);
256 static char *ehash_compose_overflow (THREAD_ENTRY * thread_p, RECDES * recdes);
257 #endif
258 static int ehash_initialize_bucket_new_page (THREAD_ENTRY * thread_p, PAGE_PTR page_p, void *alignment_depth);
259 static int ehash_initialize_dir_new_page (THREAD_ENTRY * thread_p, PAGE_PTR page_p, void *args);
260 static short ehash_get_key_size (DB_TYPE key_type);
261 static EHID *ehash_create_helper (THREAD_ENTRY * thread_p, EHID * ehid, DB_TYPE key_type, int exp_num_entries,
262  OID * class_oid, int attr_id, bool istmp);
263 static PAGE_PTR ehash_fix_old_page (THREAD_ENTRY * thread_p, const VFID * vfid, const VPID * vpid,
265 static PAGE_PTR ehash_fix_ehid_page (THREAD_ENTRY * thread_p, EHID * ehid, PGBUF_LATCH_MODE latch_mode);
266 static PAGE_PTR ehash_fix_nth_page (THREAD_ENTRY * thread_p, const VFID * vfid, int offset, PGBUF_LATCH_MODE mode);
267 static void *ehash_insert_helper (THREAD_ENTRY * thread_p, EHID * ehid, void *key, OID * value_ptr, int lock_type,
268  VPID * existing_ovf_vpid);
269 static EHASH_RESULT ehash_insert_to_bucket (THREAD_ENTRY * thread_p, EHID * ehid, VFID * ovf_file, bool is_temp,
270  PAGE_PTR buc_pgptr, DB_TYPE key_type, void *key_ptr, OID * value_ptr,
271  VPID * existing_ovf_vpid);
272 static int ehash_compose_record (DB_TYPE key_type, void *key_ptr, OID * value_ptr, RECDES * recdes);
273 static bool ehash_locate_slot (THREAD_ENTRY * thread_p, PAGE_PTR page, DB_TYPE key_type, void *key,
274  PGSLOTID * position);
275 static PAGE_PTR ehash_split_bucket (THREAD_ENTRY * thread_p, EHASH_DIR_HEADER * dir_header, PAGE_PTR buc_pgptr,
276  void *key, int *old_local_depth, int *new_local_depth, VPID * sib_vpid,
277  bool is_temp);
278 static int ehash_expand_directory (THREAD_ENTRY * thread_p, EHID * ehid, int new_depth, bool is_temp);
279 static int ehash_connect_bucket (THREAD_ENTRY * thread_p, EHID * ehid, int local_depth, EHASH_HASH_KEY hash_key,
280  VPID * buc_vpid, bool is_temp);
281 static char ehash_find_depth (THREAD_ENTRY * thread_p, EHID * ehid, int location, VPID * vpid, VPID * sib_vpid);
282 static void ehash_merge (THREAD_ENTRY * thread_p, EHID * ehid, void *key, bool is_temp);
283 static void ehash_shrink_directory (THREAD_ENTRY * thread_p, EHID * ehid, int new_depth, bool is_temp);
284 static EHASH_HASH_KEY ehash_hash (void *orig_key, DB_TYPE key_type);
285 static int ehash_find_bucket_vpid (THREAD_ENTRY * thread_p, EHID * ehid, EHASH_DIR_HEADER * dir_header, int location,
286  PGBUF_LATCH_MODE latch, VPID * out_vpid);
287 static PAGE_PTR ehash_find_bucket_vpid_with_hash (THREAD_ENTRY * thread_p, EHID * ehid, void *key,
288  PGBUF_LATCH_MODE root_latch, PGBUF_LATCH_MODE bucket_latch,
289  VPID * out_vpid, EHASH_HASH_KEY * out_hash_key, int *out_location);
290 #if defined (ENABLE_UNUSED_FUNCTION)
291 static int ehash_create_overflow_file (THREAD_ENTRY * thread_p, EHID * ehid, PAGE_PTR dir_Rpgptr,
292  EHASH_DIR_HEADER * dir_header, FILE_TYPE file_type);
293 #endif
294 static char *ehash_read_oid_from_record (char *rec_p, OID * oid_p);
295 static char *ehash_write_oid_to_record (char *rec_p, OID * oid_p);
296 static char *ehash_read_ehid_from_record (char *rec_p, EHID * ehid_p);
297 static char *ehash_write_ehid_to_record (char *rec_p, EHID * ehid_p);
298 static int ehash_rv_delete (THREAD_ENTRY * thread_p, EHID * ehid, void *key);
299 static void ehash_adjust_local_depth (THREAD_ENTRY * thread_p, EHID * ehid, PAGE_PTR dir_Rpgptr,
300  EHASH_DIR_HEADER * dir_header, int depth, int delta, bool is_temp);
301 static int ehash_apply_each (THREAD_ENTRY * thread_p, EHID * ehid, RECDES * recdes, DB_TYPE key_type, char *bucrec_ptr,
302  OID * assoc_value, int *out_apply_error, int (*apply_function) (THREAD_ENTRY * thread_p,
303  void *key, void *data,
304  void *args), void *args);
305 static int ehash_merge_permanent (THREAD_ENTRY * thread_p, EHID * ehid, PAGE_PTR dir_Rpgptr,
306  EHASH_DIR_HEADER * dir_header, PAGE_PTR buc_pgptr, PAGE_PTR sib_pgptr,
307  VPID * buc_vpid, VPID * sib_vpid, int num_recs, int loc, PGSLOTID first_slotid,
308  int *out_new_local_depth, bool is_temp);
309 static void ehash_shrink_directory_if_need (THREAD_ENTRY * thread_p, EHID * ehid, EHASH_DIR_HEADER * dir_header,
310  bool is_temp);
311 static EHASH_RESULT ehash_check_merge_possible (THREAD_ENTRY * thread_p, EHID * ehid, EHASH_DIR_HEADER * dir_header,
312  VPID * buc_vpid, PAGE_PTR buc_pgptr, int location, int lock_type,
313  int *old_local_depth, VPID * sib_vpid, PAGE_PTR * out_sib_pgptr,
314  PGSLOTID * out_first_slotid, int *out_num_recs, int *out_loc);
315 static int ehash_distribute_records_into_two_bucket (THREAD_ENTRY * thread_p, EHASH_DIR_HEADER * dir_header,
316  PAGE_PTR buc_pgptr, EHASH_BUCKET_HEADER * buc_header, int num_recs,
317  PGSLOTID first_slotid, PAGE_PTR sib_pgptr);
318 static int ehash_find_first_bit_position (THREAD_ENTRY * thread_p, EHASH_DIR_HEADER * dir_header, PAGE_PTR buc_pgptr,
319  EHASH_BUCKET_HEADER * buc_header, void *key, int num_recs,
320  PGSLOTID first_slotid, int *old_local_depth, int *new_local_depth);
321 static int ehash_get_pseudo_key (THREAD_ENTRY * thread_p, RECDES * recdes, DB_TYPE key_type,
322  EHASH_HASH_KEY * out_hash_key);
323 static bool ehash_binary_search_bucket (THREAD_ENTRY * thread_p, PAGE_PTR buc_pgptr, PGSLOTID num_record,
324  DB_TYPE key_type, void *key, PGSLOTID * position);
325 static int ehash_compare_key (THREAD_ENTRY * thread_p, char *bucrec_ptr, DB_TYPE key_type, void *key, INT16 rec_type,
326  int *out_compare_result);
327 static int ehash_write_key_to_record (RECDES * recdes, DB_TYPE key_type, void *key_ptr, short key_size, OID * value_ptr,
328  bool long_str);
330  EHASH_DIR_HEADER * dir_header, VPID * buc_vpid, void *key,
331  EHASH_HASH_KEY hash_key, int lock_type,
332  bool is_temp, OID * value_ptr, VPID * existing_ovf_vpid);
333 static PAGE_PTR ehash_extend_bucket (THREAD_ENTRY * thread_p, EHID * ehid, PAGE_PTR dir_Rpgptr,
334  EHASH_DIR_HEADER * dir_header, PAGE_PTR buc_pgptr, void *key,
335  EHASH_HASH_KEY hash_key, int *new_bit, VPID * buc_vpid, bool is_temp);
336 static int ehash_insert_to_bucket_after_create (THREAD_ENTRY * thread_p, EHID * ehid, PAGE_PTR dir_Rpgptr,
337  EHASH_DIR_HEADER * dir_header, VPID * buc_vpid, int location,
338  EHASH_HASH_KEY hash_key, bool is_temp, void *key,
339  OID * value_ptr, VPID * existing_ovf_vpid);
340 
341 static EHASH_HASH_KEY ehash_hash_string_type (char *key, char *orig_key);
343 #if defined (ENABLE_UNUSED_FUNCTION)
344 static EHASH_HASH_KEY ehash_hash_four_bytes_type (char *key);
345 static EHASH_HASH_KEY ehash_hash_two_bytes_type (char *key);
346 #endif
347 
348 /* For debugging purposes only */
349 #if defined(EHINSERTION_ORDER)
350 static void eh_dump_key (DB_TYPE key_type, void *key, OID * value_ptr);
351 #endif /* EHINSERTION_ORDER */
352 static void ehash_dump_bucket (THREAD_ENTRY * thread_p, PAGE_PTR buc_pgptr, DB_TYPE key_type);
353 
354 /*
355  * ehash_dir_locate()
356  *
357  * page_no_var(in): The nth directory page containing the pointer
358  * offset_var(in): The offset into the directory just like if the directory
359  * where contained in one contiguous area.
360  * SET by this macro according to the page_no_var where the
361  * offset is located in this page.
362  *
363  * Note: This macro finds the location of a directory pointer
364  * (i.e. the number of directory page containing this pointer and
365  * the offset value to reach the pointer within that page).
366  * The pointer number is given to macro with the
367  * "offset_var" parameter.
368  * Since this operation is performed very frequently, it is
369  * coded as a macro. So, it must be used very carefully. Two
370  * variables should be passed as parameters. The variable passed
371  * to "offset_var" should have the number of pointer prior to
372  * this macro call.
373  * In the implementation of this macro, division operation
374  * is performed twice; once for the remainder and once for the
375  * quotient. This is preferred to the method of successive
376  * subtractions since built-in "/" and "%" operators take
377  * constant time, whereas the latter method has linear
378  * characteristic (i.e., it would take longer for bigger
379  * directory sizes).
380  */
381 static void
382 ehash_dir_locate (int *out_page_no_p, int *out_offset_p)
383 {
384  int page_no, offset;
385 
386  offset = *out_offset_p;
387 
388  if (offset < EHASH_NUM_FIRST_PAGES)
389  {
390  /* in the first page */
391  offset = offset * sizeof (EHASH_DIR_RECORD) + EHASH_DIR_HEADER_SIZE;
392  page_no = 0; /* at least one page */
393  }
394  else
395  {
396  /* not in the first page */
397  offset -= EHASH_NUM_FIRST_PAGES;
398  page_no = offset / EHASH_NUM_NON_FIRST_PAGES + 1;
399  offset = (offset % EHASH_NUM_NON_FIRST_PAGES) * sizeof (EHASH_DIR_RECORD);
400  }
401 
402  *out_page_no_p = page_no;
403  *out_offset_p = offset;
404 }
405 
406 #if defined (ENABLE_UNUSED_FUNCTION)
407 /*
408  * Overflow page handling functions
409  */
410 
411 /*
412  * ehash_ansi_sql_strncmp () -
413  * return:
414  * s(in):
415  * t(in):
416  * max(in):
417  */
418 static int
419 ehash_ansi_sql_strncmp (const char *s, const char *t, int max)
420 {
421  int i;
422 
423  for (i = 0; (*s == *t) && (i < max); s++, t++, i++)
424  {
425  if (*s == '\0')
426  {
427  return 0;
428  }
429  }
430 
431  if (i == max)
432  {
433  return 0;
434  }
435 
436  if (*s == '\0')
437  {
438  while (*t != '\0')
439  { /* consume space-char */
440  if (*t++ != ' ')
441  {
442  return -1;
443  }
444  }
445  return 0;
446  }
447  else if (*t == '\0')
448  {
449  while (*s != '\0')
450  { /* consume space-char */
451  if (*s++ != ' ')
452  {
453  return 1;
454  }
455  }
456  return 0;
457  }
458 
459 
460  return (*(unsigned const char *) s < *(unsigned const char *) t) ? -1 : +1;
461 
462 }
463 #endif
464 
465 /*
466  * ehash_allocate_recdes () -
467  * return: char *, or NULL
468  * recdes(out) : Record descriptor
469  * size(in) : Request size
470  * type(in) : Request type
471  */
472 static char *
473 ehash_allocate_recdes (RECDES * recdes_p, int size, short type)
474 {
475  recdes_p->area_size = recdes_p->length = size;
476  recdes_p->type = type;
477  recdes_p->data = NULL;
478 
479  if (recdes_p->area_size > 0)
480  {
481  recdes_p->data = (char *) malloc (recdes_p->area_size);
482  if (recdes_p->data == NULL)
483  {
485  }
486  }
487 
488  return recdes_p->data;
489 }
490 
491 /*
492  * ehash_free_recdes () -
493  * return:
494  * recdes(in): Record descriptor
495  */
496 static void
498 {
499  if (recdes_p->data)
500  {
501  free_and_init (recdes_p->data);
502  }
503  recdes_p->area_size = recdes_p->length = 0;
504 
505 }
506 
507 #if defined (ENABLE_UNUSED_FUNCTION)
508 /*
509  * ehash_compare_overflow () - get/retrieve the content of a multipage object from overflow
510  * return: NO_ERROR, or ER_FAILED
511  * ovf_vpid(in): Overflow address
512  * key(in):
513  * comp_result(out):
514  *
515  * Note: The content of a multipage object associated with the given
516  * overflow address(oid) is placed into the area pointed to by
517  * the record descriptor. If the content of the object does not
518  * fit in such an area (i.e., recdes->area_size), an error is
519  * returned and a hint of its length is returned as a negative
520  * value in recdes->length. The length of the retrieved object is
521  * set in the the record descriptor (i.e., recdes->length).
522  */
523 static int
524 ehash_compare_overflow (THREAD_ENTRY * thread_p, const VPID * ovf_vpid_p, char *key_p, int *out_comp_result)
525 {
526  RECDES ovf_recdes;
527  int size;
528  int er_code;
529 
530  size = overflow_get_length (thread_p, ovf_vpid_p);
531  if (size == -1)
532  {
533  return ER_FAILED;
534  }
535 
536  if (ehash_allocate_recdes (&ovf_recdes, size, REC_HOME) == NULL)
537  {
538  return ER_FAILED;
539  }
540 
541  er_code = NO_ERROR;
542 
543  if (overflow_get (thread_p, ovf_vpid_p, &ovf_recdes, NULL) != S_SUCCESS)
544  {
545  goto exit_on_error;
546  }
547 
548  *out_comp_result = ansisql_strcmp (key_p, ovf_recdes.data);
549 
550 end:
551  ehash_free_recdes (&ovf_recdes);
552  return er_code;
553 
554 exit_on_error:
555  er_code = ER_FAILED;
556  goto end;
557 }
558 
559 /*
560  * ehash_compose_overflow () - get/retrieve the content of a multipage object from
561  * overflow
562  * return:
563  * recdes(in): Record descriptor
564  *
565  * Note: The content of a multipage object associated with the given
566  * overflow address(oid) is placed into the area pointed to by
567  * the record descriptor. If the content of the object does not
568  * fit in such an area (i.e., recdes->area_size), an error is
569  * returned and a hint of its length is returned as a negative
570  * value in recdes->length. The length of the retrieved object is
571  * set in the the record descriptor (i.e., recdes->length).
572  */
573 static char *
574 ehash_compose_overflow (THREAD_ENTRY * thread_p, RECDES * recdes_p)
575 {
576  VPID *ovf_vpid_p;
577  RECDES ovf_recdes;
578  int size;
579  char *bucket_p;
580 
581  /* Skip the OID field and point to ovf_vpid field */
582  ovf_vpid_p = (VPID *) (recdes_p->data + sizeof (OID));
583 
584  /* Allocate the space for the whole key */
585  size = overflow_get_length (thread_p, ovf_vpid_p);
586  if (size == -1)
587  {
588  return NULL;
589  }
591 
592  if (ehash_allocate_recdes (&ovf_recdes, size, REC_HOME) == NULL)
593  {
594  return NULL;
595  }
596 
597  /* Copy the prefix key portion first */
598 
599  /* Skip the OID & ovf_vpid fields and point to the prefix key */
600  bucket_p = recdes_p->data + sizeof (OID) + sizeof (VPID);
601 
602  memcpy (ovf_recdes.data, bucket_p, EHASH_LONG_STRING_PREFIX_SIZE);
603  ovf_recdes.data += EHASH_LONG_STRING_PREFIX_SIZE;
605 
606  /* Copy to the overflow portion of the key */
607  if (overflow_get (thread_p, ovf_vpid_p, &ovf_recdes, NULL) != S_SUCCESS)
608  {
609  ehash_free_recdes (&ovf_recdes);
610  return NULL;
611  }
612 
613  return ovf_recdes.data; /* key */
614 }
615 #endif
616 
617 /*
618  * ehash_initialize_bucket_new_page () - Initialize a newly allocated page
619  *
620  * return : Error code.
621  * thread_p (in) : Thread entry
622  * page_p (in) : New ehash bucket page
623  * args (in) : Alignment, depth and is_temp. Used to initialize and log the page.
624  */
625 static int
627 {
628  char alignment;
629  char depth;
630  bool is_temp;
631  int offset = 0;
632  EHASH_BUCKET_HEADER bucket_header;
633  RECDES bucket_recdes;
634  PGSLOTID slot_id;
635  int success = SP_SUCCESS;
636 
637  int error_code = NO_ERROR;
638 
639  alignment = *(char *) args;
640  offset += sizeof (alignment);
641 
642  depth = *((char *) args + offset);
643  offset += sizeof (depth);
644 
645  is_temp = *(bool *) ((char *) args + offset);
646 
647  /*
648  * fetch and initialize the new page. The parameter UNANCHORED_KEEP_
649  * SEQUENCE indicates that the order of records will be preserved
650  * during insertions and deletions.
651  */
652 
653  pgbuf_set_page_ptype (thread_p, page_p, PAGE_EHASH);
654 
655  /*
656  * Initialize the bucket to contain variable-length records
657  * on ordered slots.
658  */
659  spage_initialize (thread_p, page_p, UNANCHORED_KEEP_SEQUENCE, alignment, DONT_SAFEGUARD_RVSPACE);
660 
661  /* Initialize the bucket header */
662  bucket_header.local_depth = depth;
663 
664  /* Set the record descriptor to the Bucket header */
665  bucket_recdes.data = (char *) &bucket_header;
666  bucket_recdes.area_size = bucket_recdes.length = sizeof (EHASH_BUCKET_HEADER);
667  bucket_recdes.type = REC_HOME;
668 
669  /*
670  * Insert the bucket header into the first slot (slot # 0)
671  * on the bucket page
672  */
673  success = spage_insert (thread_p, page_p, &bucket_recdes, &slot_id);
674  if (success != SP_SUCCESS)
675  {
676  /*
677  * Slotted page module refuses to insert a short size record to an
678  * empty page. This should never happen.
679  */
680  if (success != SP_ERROR)
681  {
683  error_code = ER_FAILED;
684  }
685  else
686  {
687  ASSERT_ERROR_AND_SET (error_code);
688  }
689  return error_code;
690  }
691 
692  if (!is_temp)
693  {
694  log_append_undoredo_data2 (thread_p, RVEH_INIT_BUCKET, NULL, page_p, -1, 0, 2, NULL, args);
695  }
696  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
697 
698  return NO_ERROR;
699 }
700 
701 /*
702  * ehash_initialize_dir_new_pages () - Initialize new page used to expand extensible hash directory.
703  *
704  * return : Error code
705  * thread_p (in) : Thread entry
706  * page_p (in) : New directory page
707  * ignore_args (in) : (not used)
708  */
709 static int
710 ehash_initialize_dir_new_page (THREAD_ENTRY * thread_p, PAGE_PTR page_p, void *args)
711 {
712  bool is_temp = *(bool *) args;
713 
714  pgbuf_set_page_ptype (thread_p, page_p, PAGE_EHASH);
715  if (!is_temp)
716  {
717  log_append_undoredo_data2 (thread_p, RVEH_INIT_NEW_DIR_PAGE, NULL, page_p, -1, 0, 0, NULL, NULL);
718  }
719  pgbuf_set_dirty (thread_p, page_p, DONT_FREE);
720 
721  return NO_ERROR;
722 }
723 
724 /*
725  * ehash_rv_init_dir_new_page_redo () - Redo initialize new page used to
726  * expand extensible hash table.
727  *
728  * return : NO_ERROR.
729  * thread_p (in) : Thread entry.
730  * rcv (in) : No data.
731  */
732 int
734 {
735  pgbuf_set_page_ptype (thread_p, rcv->pgptr, PAGE_EHASH);
736  pgbuf_set_dirty (thread_p, rcv->pgptr, DONT_FREE);
737  return NO_ERROR;
738 }
739 
740 #if defined(EHINSERTION_ORDER)
741 /*
742  * eh_dump_key () -
743  * return:
744  * key_type(in):
745  * key(in):
746  * value_ptr(in):
747  */
748 static void
749 eh_dump_key (DB_TYPE key_type, void *key, OID * value_ptr)
750 {
751  int hour, minute, second, month, day, year;
752  double d;
753 
754  switch (key_type)
755  {
756  case DB_TYPE_STRING:
757  fprintf (stdout, "key:%s", (char *) key);
758  break;
759 
760  case DB_TYPE_OBJECT:
761  fprintf (stdout, "key:%d|%d|%d", ((OID *) key)->volid, ((OID *) key)->pageid, ((OID *) key)->slotid);
762  break;
763 
764  case DB_TYPE_DOUBLE:
765  fprintf (stdout, "key:%f", *(double *) key);
766  break;
767 
768  case DB_TYPE_FLOAT:
769  fprintf (stdout, "key:%f", *(float *) key);
770  break;
771 
772  case DB_TYPE_INTEGER:
773  fprintf (stdout, "key:%d", *(int *) key);
774  break;
775 
776  case DB_TYPE_BIGINT:
777  fprintf (stdout, "key:%lld", (long long) (*(DB_BIGINT *) key));
778  break;
779 
780  case DB_TYPE_SHORT:
781  fprintf (stdout, "key:%d", *(short *) key);
782  break;
783 
784  case DB_TYPE_DATE:
785  db_date_decode ((DB_DATE *) key, &month, &day, &year);
786  fprintf (stdout, "key:%2d/%2d/%4d", month, day, year);
787  break;
788 
789  case DB_TYPE_TIME:
790  db_time_decode ((DB_TIME *) key, &hour, &minute, &second);
791  fprintf (stdout, "key:%3d:%3d:%3d", hour, minute, second);
792  break;
793 
794  case DB_TYPE_TIMESTAMP:
796  fprintf (stdout, "key:%d", *(DB_UTIME *) key);
797  break;
798 
799  case DB_TYPE_TIMESTAMPTZ:
800  fprintf (stdout, "key:%d", ((DB_TIMESTAMPTZ *) key)->timestamp, ((DB_TIMESTAMPTZ *) key)->tz_id);
801  break;
802 
803  case DB_TYPE_DATETIME:
804  case DB_TYPE_DATETIMELTZ:
805  fprintf (stdout, "key:%d,%d", ((DB_DATETIME *) key)->date, ((DB_DATETIME *) key)->time);
806  break;
807 
808  case DB_TYPE_DATETIMETZ:
809  fprintf (stdout, "key:%d,%d", ((DB_DATETIMETZ *) key)->datetime.date, ((DB_DATETIMETZ *) key)->datetime.time,
810  ((DB_DATETIMETZ *) key)->tz_id);
811  break;
812 
813  case DB_TYPE_MONETARY:
814  OR_MOVE_DOUBLE (key, &d);
815  fprintf (stdout, "key:%f type %d", d, ((DB_MONETARY *) key)->type);
816  break;
817 
818  default:
819  /* Unspecified key type: Directory header has been corrupted */
821  return;
822  }
823 
824  fprintf (stdout, " OID associated value:%5d,%5d,%5d\n", value_ptr->volid, value_ptr->pageid, value_ptr->slotid);
825 
826 }
827 #endif /* EHINSERTION_ORDER */
828 
829 /*
830  * xehash_create () - Create an extendible hashing structure
831  * return: EHID * (NULL in case of error)
832  * ehid(in): identifier for the extendible hashing structure to
833  * create. The volid field should already be set; others
834  * are set by this function.
835  * key_type(in): key type for the extendible hashing structure
836  * exp_num_entries(in): expected number of entries (i.e., <key, oid> pairs).
837  * This figure is used as a guide to estimate the number of
838  * pages the extendible hashing structure will occupy.
839  * The purpose of this estimate is to increase the locality
840  * of reference on the disk.
841  * If the number of entries is not known, a negative value
842  * should be passed to this parameter.
843  * class_oid(in): OID of the class for which the index is created
844  * attr_id(in): Identifier of the attribute of the class for which the
845  * index is created.
846  * is_tmp(in): true, if the EHT will be based on temporary files.
847  *
848  * Note: Creates an extendible hashing structure for the particular
849  * key type on the disk volume whose identifier is passed in
850  * ehid->vfid.volid field. It creates two files on this volume: one
851  * for the directory and one for the buckets. It also allocates
852  * the first page of each file; the first bucket and the root
853  * page of the directory.
854  * The directory header area of the root page is
855  * initialized by this function. Both the global depth of the
856  * directory and the local depth of first bucket are set to 0.
857  * The identifier of bucket file is kept in the directory header.
858  * The directory file identifier and directory root page
859  * identifier are loaded into the remaining fields of "Dir"
860  * to be used as the complete identifier of this extendible
861  * hashing structure for future reference.
862  */
863 EHID *
864 xehash_create (THREAD_ENTRY * thread_p, EHID * ehid_p, DB_TYPE key_type, int exp_num_entries, OID * class_oid_p,
865  int attr_id, bool is_tmp)
866 {
867  return ehash_create_helper (thread_p, ehid_p, key_type, exp_num_entries, class_oid_p, attr_id, is_tmp);
868 }
869 
870 /*
871  * ehash_get_key_size () - Return the size of keys in bytes;
872  * Undeclared if key_type is "DB_TYPE_STRING".
873  * return: byte size of key_type, or -1 for error;
874  * key_type(in):
875  */
876 static short
878 {
879  short key_size;
880 
881  switch (key_type)
882  {
883  case DB_TYPE_STRING:
884  key_size = 1; /* Size of each key will vary */
885  break;
886 
887  case DB_TYPE_OBJECT:
888  key_size = sizeof (OID);
889  break;
890 
891 #if defined (ENABLE_UNUSED_FUNCTION)
892  case DB_TYPE_DOUBLE:
893  key_size = sizeof (double);
894  break;
895 
896  case DB_TYPE_FLOAT:
897  key_size = sizeof (float);
898  break;
899 
900  case DB_TYPE_INTEGER:
901  key_size = sizeof (int);
902  break;
903 
904  case DB_TYPE_BIGINT:
905  key_size = sizeof (DB_BIGINT);
906  break;
907 
908  case DB_TYPE_SHORT:
909  key_size = sizeof (short);
910  break;
911 
912  case DB_TYPE_DATE:
913  key_size = sizeof (DB_DATE);
914  break;
915 
916  case DB_TYPE_TIME:
917  key_size = sizeof (DB_TIME);
918  break;
919 
920  case DB_TYPE_TIMESTAMP:
921  key_size = sizeof (DB_UTIME);
922  break;
923 
924  case DB_TYPE_DATETIME:
925  key_size = sizeof (DB_DATETIME);
926  break;
927 
928  case DB_TYPE_MONETARY:
929  key_size = OR_MONETARY_SIZE;
930  break;
931 #endif
932  default:
934  key_size = -1;
935  break;
936  }
937 
938  return key_size;
939 }
940 
941 /*
942  * ehash_create_helper () -
943  * return:
944  * ehid(in):
945  * key_type(in):
946  * exp_num_entries(in):
947  * class_oid(in):
948  * attr_id(in):
949  * is_tmp(in):
950  */
951 static EHID *
952 ehash_create_helper (THREAD_ENTRY * thread_p, EHID * ehid_p, DB_TYPE key_type, int exp_num_entries, OID * class_oid_p,
953  int attr_id, bool is_tmp)
954 {
955  EHASH_DIR_HEADER *dir_header_p = NULL;
956  EHASH_DIR_RECORD *dir_record_p = NULL;
957  VFID dir_vfid = VFID_INITIALIZER;
958  VPID dir_vpid = VPID_INITIALIZER;
959  PAGE_PTR dir_page_p = NULL;
960  VFID bucket_vfid = VFID_INITIALIZER;
961  VPID bucket_vpid = VPID_INITIALIZER;
962  char init_bucket_data[3];
963  DKNPAGES exp_bucket_pages;
964  DKNPAGES exp_dir_pages;
965  short key_size;
966  char alignment;
967  OID value;
968  unsigned int i;
969  FILE_EHASH_DES ehdes;
970  PAGE_TYPE ptype = PAGE_EHASH;
971 
972  assert (key_type == DB_TYPE_STRING || key_type == DB_TYPE_OBJECT);
973 
974  if (ehid_p == NULL)
975  {
976  return NULL;
977  }
978 
979  /* create a file descriptor */
980  if (class_oid_p != NULL)
981  {
982  COPY_OID (&ehdes.class_oid, class_oid_p);
983  }
984  else
985  {
986  OID_SET_NULL (&ehdes.class_oid);
987  }
988  ehdes.attr_id = attr_id;
989 
990  /* Set the key size */
991  key_size = ehash_get_key_size (key_type);
992  if (key_size < 0)
993  {
994  return NULL;
995  }
996 
997  /* Estimate number of bucket pages */
998  if (exp_num_entries < 0)
999  {
1000  /* Assume minimum size */
1001  exp_bucket_pages = 1;
1002  }
1003  else
1004  {
1005  if (key_type == DB_TYPE_STRING)
1006  {
1007  exp_bucket_pages = exp_num_entries * (20 + sizeof (OID) + 4);
1008  }
1009  else
1010  {
1011  exp_bucket_pages = exp_num_entries * (key_size + sizeof (OID) + 4);
1012  }
1013 
1014  exp_bucket_pages = CEIL_PTVDIV (exp_bucket_pages, DB_PAGESIZE);
1015  }
1016 
1017  /* Calculate alignment to use on slots of bucket pages */
1018  if (SSIZEOF (value.pageid) >= key_size)
1019  {
1020  alignment = sizeof (value.pageid);
1021  }
1022  else
1023  {
1024  alignment = (char) key_size;
1025  }
1026 
1027  /* TODO: M2 64-bit */
1028  /* May want to remove later on; not portable to 64-bit machines */
1029  if (alignment > SSIZEOF (int))
1030  {
1031  alignment = sizeof (int);
1032  }
1033 
1034  /* Create the first bucket and initialize its header */
1035 
1036  bucket_vfid.volid = ehid_p->vfid.volid;
1037 
1038  if (file_create_ehash (thread_p, exp_bucket_pages, is_tmp, &ehdes, &bucket_vfid) != NO_ERROR)
1039  {
1040  ASSERT_ERROR ();
1041  return NULL;
1042  }
1043 
1044  /* Log the initialization of the first bucket page */
1045 
1046  init_bucket_data[0] = alignment;
1047  init_bucket_data[1] = 0;
1048  init_bucket_data[2] = is_tmp;
1049 
1050  if (file_alloc (thread_p, &bucket_vfid, ehash_initialize_bucket_new_page, init_bucket_data, &bucket_vpid, NULL)
1051  != NO_ERROR)
1052  {
1053  ASSERT_ERROR ();
1054  goto exit_on_error;
1055  }
1056 
1057  /* Estimate number of directory pages */
1058  if (exp_num_entries < 0)
1059  {
1060  exp_dir_pages = 1;
1061  }
1062  else
1063  {
1064  /* Calculate how many directory pages will be used */
1065  ehash_dir_locate (&exp_dir_pages, &exp_bucket_pages);
1066  /* exp_dir_pages is actually an index. the number of pages should be +1 */
1067  exp_dir_pages++;
1068  }
1069 
1070  /* Create the directory (allocate the first page) and initialize its header */
1071 
1072  dir_vfid.volid = bucket_vfid.volid;
1073 
1074  /*
1075  * Create the file and allocate the first page
1076  *
1077  * We do not initialize the page during the allocation since the file is
1078  * new, and the file is going to be removed in the event of a crash.
1079  */
1080 
1081  if (file_create_ehash_dir (thread_p, exp_dir_pages, is_tmp, &ehdes, &dir_vfid) != NO_ERROR)
1082  {
1083  ASSERT_ERROR ();
1084  goto exit_on_error;
1085  }
1086  if (file_alloc (thread_p, &dir_vfid, is_tmp ? file_init_temp_page_type : file_init_page_type, &ptype, &dir_vpid,
1087  &dir_page_p) != NO_ERROR)
1088  {
1089  ASSERT_ERROR ();
1090  goto exit_on_error;
1091  }
1092  if (dir_page_p == NULL)
1093  {
1094  assert_release (false);
1095  goto exit_on_error;
1096  }
1097  pgbuf_check_page_ptype (thread_p, dir_page_p, PAGE_EHASH);
1098 
1099  dir_header_p = (EHASH_DIR_HEADER *) dir_page_p;
1100 
1101  /* Initialize the directory header */
1102  dir_header_p->depth = 0;
1103  dir_header_p->key_type = key_type;
1104  dir_header_p->alignment = alignment;
1105  VFID_COPY (&dir_header_p->bucket_file, &bucket_vfid);
1106  VFID_SET_NULL (&dir_header_p->overflow_file);
1107 
1108  /* Initialize local depth information */
1109  dir_header_p->local_depth_count[0] = 1;
1110  for (i = 1; i <= EHASH_HASH_KEY_BITS; i++)
1111  {
1112  dir_header_p->local_depth_count[i] = 0;
1113  }
1114 
1115  /*
1116  * Check if the key is of fixed size, and if so, store this information
1117  * in the directory header
1118  */
1119 
1120  dir_record_p = (EHASH_DIR_RECORD *) ((char *) dir_page_p + EHASH_DIR_HEADER_SIZE);
1121  dir_record_p->bucket_vpid = bucket_vpid;
1122 
1123  /*
1124  * Don't need UNDO since we are just creating the file. If we abort, the
1125  * file is removed.
1126  */
1127 
1128  /* Log the directory root page */
1129  if (!is_tmp)
1130  {
1131  log_append_redo_data2 (thread_p, RVEH_INIT_DIR, &dir_vfid, dir_page_p, 0,
1132  EHASH_DIR_HEADER_SIZE + sizeof (EHASH_DIR_RECORD), dir_page_p);
1133  }
1134 
1135  /* Finishing up; release the pages and return directory file id */
1136  pgbuf_set_dirty (thread_p, dir_page_p, FREE);
1137 
1138  VFID_COPY (&ehid_p->vfid, &dir_vfid);
1139  ehid_p->pageid = dir_vpid.pageid;
1140 
1141  return ehid_p;
1142 
1143 exit_on_error:
1144 
1145  if (!VFID_ISNULL (&bucket_vfid))
1146  {
1147  if (is_tmp)
1148  {
1149  if (file_destroy (thread_p, &bucket_vfid, is_tmp) != NO_ERROR)
1150  {
1151  assert_release (false);
1152  }
1153  }
1154  else
1155  {
1156  (void) file_postpone_destroy (thread_p, &bucket_vfid);
1157  }
1158  }
1159  if (!VFID_ISNULL (&dir_vfid))
1160  {
1161  if (is_tmp)
1162  {
1163  if (file_destroy (thread_p, &dir_vfid, is_tmp) != NO_ERROR)
1164  {
1165  assert_release (false);
1166  }
1167  }
1168  else
1169  {
1170  (void) file_postpone_destroy (thread_p, &dir_vfid);
1171  }
1172  }
1173  return NULL;
1174 }
1175 
1176 /*
1177  * ehash_fix_old_page () -
1178  * return: specified page pointer, or NULL
1179  * vfid_p(in): only for error reporting
1180  * vpid_p(in):
1181  * latch_mode(in): lock mode
1182  */
1183 static PAGE_PTR
1184 ehash_fix_old_page (THREAD_ENTRY * thread_p, const VFID * vfid_p, const VPID * vpid_p, PGBUF_LATCH_MODE latch_mode)
1185 {
1186  PAGE_PTR page_p;
1187 
1188  page_p = pgbuf_fix (thread_p, vpid_p, OLD_PAGE, latch_mode, PGBUF_UNCONDITIONAL_LATCH);
1189  if (page_p == NULL)
1190  {
1191  if (er_errid () == ER_PB_BAD_PAGEID)
1192  {
1194  vpid_p->volid, vpid_p->pageid);
1195  }
1196 
1197  return NULL;
1198  }
1199 
1200  (void) pgbuf_check_page_ptype (thread_p, page_p, PAGE_EHASH);
1201 
1202  return page_p;
1203 }
1204 
1205 /*
1206  * ehash_fix_ehid_page () -
1207  * return: specified page pointer, or NULL
1208  * ehid(in): extendible hashing structure
1209  * latch_mode(in): lock mode
1210  */
1211 static PAGE_PTR
1212 ehash_fix_ehid_page (THREAD_ENTRY * thread_p, EHID * ehid, PGBUF_LATCH_MODE latch_mode)
1213 {
1214  VPID vpid;
1215 
1216  vpid.volid = ehid->vfid.volid;
1217  vpid.pageid = ehid->pageid;
1218 
1219  return ehash_fix_old_page (thread_p, &(ehid->vfid), &vpid, latch_mode);
1220 }
1221 
1222 /*
1223  * ehash_fix_nth_page () -
1224  * return: specified page pointer, or NULL
1225  * vfid(in):
1226  * offset(in):
1227  * lock(in): lock mode
1228  */
1229 static PAGE_PTR
1230 ehash_fix_nth_page (THREAD_ENTRY * thread_p, const VFID * vfid_p, int offset, PGBUF_LATCH_MODE latch_mode)
1231 {
1232  VPID vpid;
1233 
1234  if (file_numerable_find_nth (thread_p, vfid_p, offset, false, NULL, NULL, &vpid) != NO_ERROR)
1235  {
1236  ASSERT_ERROR ();
1237  return NULL;
1238  }
1239 
1240  return ehash_fix_old_page (thread_p, vfid_p, &vpid, latch_mode);
1241 }
1242 
1243 /*
1244  * xehash_destroy () - destroy the extensible hash table
1245  *
1246  * return : error code
1247  * thread_p (in) : thread entry
1248  * ehid_p (in) : extensible hash identifier
1249  *
1250  * note: only temporary extensible hash tables can be destroyed.
1251  */
1252 int
1253 xehash_destroy (THREAD_ENTRY * thread_p, EHID * ehid_p)
1254 {
1255  EHASH_DIR_HEADER *dir_header_p;
1256  PAGE_PTR dir_page_p;
1257 
1258  if (ehid_p == NULL)
1259  {
1260  return ER_FAILED;
1261  }
1262 
1263  dir_page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_WRITE);
1264  if (dir_page_p == NULL)
1265  {
1266  return ER_FAILED;
1267  }
1268 
1269  log_sysop_start (thread_p);
1270 
1271  dir_header_p = (EHASH_DIR_HEADER *) dir_page_p;
1272 
1273  if (file_destroy (thread_p, &(dir_header_p->bucket_file), true) != NO_ERROR)
1274  {
1275  assert_release (false);
1276  }
1277  pgbuf_unfix (thread_p, dir_page_p);
1278  if (file_destroy (thread_p, &ehid_p->vfid, true) != NO_ERROR)
1279  {
1280  assert_release (false);
1281  }
1282 
1283  log_sysop_commit (thread_p);
1284 
1285  return NO_ERROR;
1286 }
1287 
1288 static int
1289 ehash_find_bucket_vpid (THREAD_ENTRY * thread_p, EHID * ehid_p, EHASH_DIR_HEADER * dir_header_p, int location,
1290  PGBUF_LATCH_MODE latch, VPID * out_vpid_p)
1291 {
1292  EHASH_DIR_RECORD *dir_record_p;
1293  PAGE_PTR dir_page_p;
1294  int dir_offset;
1295 
1296  ehash_dir_locate (&dir_offset, &location);
1297 
1298  if (dir_offset != 0)
1299  {
1300  /* The bucket pointer is not in the root (first) page of the directory */
1301  dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, dir_offset, latch);
1302  if (dir_page_p == NULL)
1303  {
1304  return ER_FAILED;
1305  }
1306 
1307  /* Find the bucket page containing the key */
1308  dir_record_p = (EHASH_DIR_RECORD *) ((char *) dir_page_p + location);
1309  pgbuf_unfix_and_init (thread_p, dir_page_p);
1310  }
1311  else
1312  {
1313  dir_record_p = (EHASH_DIR_RECORD *) ((char *) dir_header_p + location);
1314  }
1315 
1316  *out_vpid_p = dir_record_p->bucket_vpid;
1317  return NO_ERROR;
1318 }
1319 
1320 static PAGE_PTR
1321 ehash_find_bucket_vpid_with_hash (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p, PGBUF_LATCH_MODE root_latch,
1322  PGBUF_LATCH_MODE bucket_latch, VPID * out_vpid_p, EHASH_HASH_KEY * out_hash_key_p,
1323  int *out_location_p)
1324 {
1325  EHASH_DIR_HEADER *dir_header_p;
1326  PAGE_PTR dir_root_page_p;
1327  EHASH_HASH_KEY hash_key;
1328  int location;
1329 
1330  dir_root_page_p = ehash_fix_ehid_page (thread_p, ehid_p, root_latch);
1331  if (dir_root_page_p == NULL)
1332  {
1333  return NULL;
1334  }
1335 
1336  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
1337 
1338  /* Get the pseudo key */
1339  hash_key = ehash_hash (key_p, dir_header_p->key_type);
1340  if (out_hash_key_p)
1341  {
1342  *out_hash_key_p = hash_key;
1343  }
1344 
1345  /* Find the location of bucket pointer in the directory */
1346  location = FIND_OFFSET (hash_key, dir_header_p->depth);
1347  if (out_location_p)
1348  {
1349  *out_location_p = location;
1350  }
1351 
1352  if (ehash_find_bucket_vpid (thread_p, ehid_p, dir_header_p, location, bucket_latch, out_vpid_p) != NO_ERROR)
1353  {
1354  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1355  return NULL;
1356  }
1357 
1358  return dir_root_page_p;
1359 }
1360 
1361 /*
1362  * ehash_search () - Search for the key; return associated value
1363  * return: EH_SEARCH
1364  * ehid(in): identifier for the extendible hashing structure
1365  * key(in): key to search
1366  * value_ptr(out): pointer to return the value associated with the key
1367  *
1368  * Note: Returns the value associated with the given key, if it is
1369  * found in the specified extendible hashing structure. If the
1370  * key is not found an error condition is returned.
1371  */
1372 EH_SEARCH
1373 ehash_search (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p, OID * value_p)
1374 {
1375  EHASH_DIR_HEADER *dir_header_p;
1376  PAGE_PTR dir_root_page_p = NULL;
1377  PAGE_PTR bucket_page_p = NULL;
1378  VPID bucket_vpid;
1379  RECDES recdes;
1380  PGSLOTID slot_id;
1381  EH_SEARCH result = EH_KEY_NOTFOUND;
1382 
1383  if (ehid_p == NULL || key_p == NULL)
1384  {
1385  return EH_KEY_NOTFOUND;
1386  }
1387 
1388  dir_root_page_p =
1389  ehash_find_bucket_vpid_with_hash (thread_p, ehid_p, key_p, PGBUF_LATCH_READ, PGBUF_LATCH_READ, &bucket_vpid, NULL,
1390  NULL);
1391  if (dir_root_page_p == NULL)
1392  {
1393  return EH_ERROR_OCCURRED;
1394  }
1395 
1396  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
1397 
1398  if (bucket_vpid.pageid == NULL_PAGEID)
1399  {
1400  result = EH_KEY_NOTFOUND;
1401  goto end;
1402  }
1403 
1404  bucket_page_p = ehash_fix_old_page (thread_p, &ehid_p->vfid, &bucket_vpid, PGBUF_LATCH_READ);
1405  if (bucket_page_p == NULL)
1406  {
1407  result = EH_ERROR_OCCURRED;
1408  goto end;
1409  }
1410 
1411  if (ehash_locate_slot (thread_p, bucket_page_p, dir_header_p->key_type, key_p, &slot_id) == false)
1412  {
1413  result = EH_KEY_NOTFOUND;
1414  goto end;
1415  }
1416 
1417  (void) spage_get_record (thread_p, bucket_page_p, slot_id, &recdes, PEEK);
1418  (void) ehash_read_oid_from_record (recdes.data, value_p);
1419  result = EH_KEY_FOUND;
1420 
1421 end:
1422  if (bucket_page_p)
1423  {
1424  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1425  }
1426 
1427  if (dir_root_page_p)
1428  {
1429  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1430  }
1431 
1432  return result;
1433 }
1434 
1435 /*
1436  * ehash_insert () - Insert (key, assoc_value) pair to ext. hashing
1437  * return: void * (NULL is returned in case of error)
1438  * ehid(in): identifier of the extendible hashing structure
1439  * key(in): key value to insert
1440  * value_ptr(in): pointer to the associated value to insert
1441  *
1442  * Note: Inserts the given (key & assoc_value) pair into the specified
1443  * extendible hashing structure. If the key already exists then
1444  * the previous associated value is replaced with the new one.
1445  * Otherwise, a new record is inserted to the correct bucket.
1446  * To perform the insertion operation safely in the concurrent
1447  * environment, an auxiliary (and private) function
1448  * "eh_insert_helper", which takes an additional parameter to
1449  * specify the lock type to acquire on the directory, is used
1450  * by this function. Hoping that this insertion will not effect
1451  * the directory pages, it passes a shared lock
1452  * (i.e, an "S_LOCK") for this parameter.
1453  */
1454 void *
1455 ehash_insert (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p, OID * value_p)
1456 {
1457  if (ehid_p == NULL || key_p == NULL)
1458  {
1459  return NULL;
1460  }
1461 
1462  return ehash_insert_helper (thread_p, ehid_p, key_p, value_p, S_LOCK, NULL);
1463 }
1464 
1465 static int
1466 ehash_insert_to_bucket_after_create (THREAD_ENTRY * thread_p, EHID * ehid_p, PAGE_PTR dir_root_page_p,
1467  EHASH_DIR_HEADER * dir_header_p, VPID * bucket_vpid_p, int location,
1468  EHASH_HASH_KEY hash_key, bool is_temp, void *key_p, OID * value_p,
1469  VPID * existing_ovf_vpid_p)
1470 {
1471  PAGE_PTR bucket_page_p;
1472  EHASH_BUCKET_HEADER bucket_header;
1473  char found_depth;
1474  char init_bucket_data[3];
1475  VPID null_vpid = { NULL_VOLID, NULL_PAGEID };
1476  EHASH_RESULT ins_result;
1477 
1478  int error_code = NO_ERROR;
1479 
1480  found_depth = ehash_find_depth (thread_p, ehid_p, location, &null_vpid, &null_vpid);
1481  if (found_depth == 0)
1482  {
1483  return ER_FAILED;
1484  }
1485 
1486  init_bucket_data[0] = dir_header_p->alignment;
1487  init_bucket_data[1] = dir_header_p->depth - found_depth;
1488  init_bucket_data[2] = is_temp;
1489  bucket_header.local_depth = init_bucket_data[1];
1490 
1491  log_sysop_start (thread_p);
1492 
1493  error_code = file_alloc (thread_p, &dir_header_p->bucket_file, ehash_initialize_bucket_new_page, init_bucket_data,
1494  bucket_vpid_p, &bucket_page_p);
1495  if (error_code != NO_ERROR)
1496  {
1497  ASSERT_ERROR ();
1498  log_sysop_abort (thread_p);
1499  return ER_FAILED;
1500  }
1501  if (bucket_page_p == NULL)
1502  {
1503  assert_release (false);
1504  log_sysop_abort (thread_p);
1505  return ER_FAILED;
1506  }
1507  (void) pgbuf_check_page_ptype (thread_p, bucket_page_p, PAGE_EHASH);
1508 
1509  if (ehash_connect_bucket (thread_p, ehid_p, bucket_header.local_depth, hash_key, bucket_vpid_p, is_temp) != NO_ERROR)
1510  {
1511  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1512  log_sysop_abort (thread_p);
1513  return ER_FAILED;
1514  }
1515 
1516  ehash_adjust_local_depth (thread_p, ehid_p, dir_root_page_p, dir_header_p, (int) bucket_header.local_depth, 1,
1517  is_temp);
1518 
1519  log_sysop_commit (thread_p);
1520 
1521  ins_result =
1522  ehash_insert_to_bucket (thread_p, ehid_p, &dir_header_p->overflow_file, is_temp, bucket_page_p,
1523  dir_header_p->key_type, key_p, value_p, existing_ovf_vpid_p);
1524 
1525  if (ins_result != EHASH_SUCCESSFUL_COMPLETION)
1526  {
1527  /* Slotted page module refuses to insert a short size record to an almost empty page. This should never happen. */
1529  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1530  return ER_FAILED;
1531  }
1532 
1533  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1534  return NO_ERROR;
1535 }
1536 
1537 static PAGE_PTR
1538 ehash_extend_bucket (THREAD_ENTRY * thread_p, EHID * ehid_p, PAGE_PTR dir_root_page_p, EHASH_DIR_HEADER * dir_header_p,
1539  PAGE_PTR bucket_page_p, void *key_p, EHASH_HASH_KEY hash_key, int *out_new_bit_p,
1540  VPID * bucket_vpid, bool is_temp)
1541 {
1542  VPID sibling_vpid;
1543  PAGE_PTR sibling_page_p = NULL;
1544  VPID null_vpid = { NULL_VOLID, NULL_PAGEID };
1545  int old_local_depth;
1546  int new_local_depth;
1547 
1548  if (!is_temp)
1549  {
1550  log_sysop_start (thread_p);
1551  }
1552 
1553  sibling_page_p =
1554  ehash_split_bucket (thread_p, dir_header_p, bucket_page_p, key_p, &old_local_depth, &new_local_depth,
1555  &sibling_vpid, is_temp);
1556  if (sibling_page_p == NULL)
1557  {
1558  if (!is_temp)
1559  {
1560  log_sysop_abort (thread_p);
1561  }
1562  return NULL;
1563  }
1564 
1565  /* Save the bit position of hash_key to be used later in deciding whether to insert the new key to original bucket or
1566  * to the new sibling bucket. */
1567  *out_new_bit_p = GETBIT (hash_key, new_local_depth);
1568 
1569  ehash_adjust_local_depth (thread_p, ehid_p, dir_root_page_p, dir_header_p, old_local_depth, -1, is_temp);
1570  ehash_adjust_local_depth (thread_p, ehid_p, dir_root_page_p, dir_header_p, new_local_depth, 2, is_temp);
1571 
1572  /* Check directory expansion condition */
1573  if (new_local_depth > dir_header_p->depth)
1574  {
1575  if (ehash_expand_directory (thread_p, ehid_p, new_local_depth, is_temp) != NO_ERROR)
1576  {
1577  pgbuf_unfix_and_init (thread_p, sibling_page_p);
1578  if (!is_temp)
1579  {
1580  log_sysop_abort (thread_p);
1581  }
1582  return NULL;
1583  }
1584  }
1585 
1586  /* Connect the buckets */
1587  if ((new_local_depth - old_local_depth) > 1)
1588  {
1589  /* First, set all of them to NULL_PAGEID */
1590  if (ehash_connect_bucket (thread_p, ehid_p, old_local_depth, hash_key, &null_vpid, is_temp) != NO_ERROR)
1591  {
1592  pgbuf_unfix_and_init (thread_p, sibling_page_p);
1593  if (!is_temp)
1594  {
1595  log_sysop_abort (thread_p);
1596  }
1597  return NULL;
1598  }
1599 
1600  /* Then, connect the Bucket page */
1601  hash_key = CLEARBIT (hash_key, new_local_depth);
1602  if (ehash_connect_bucket (thread_p, ehid_p, new_local_depth, hash_key, bucket_vpid, is_temp) != NO_ERROR)
1603  {
1604  pgbuf_unfix_and_init (thread_p, sibling_page_p);
1605  if (!is_temp)
1606  {
1607  log_sysop_abort (thread_p);
1608  }
1609  return NULL;
1610  }
1611  }
1612 
1613  /* Finally, connect the Sibling bucket page */
1614  hash_key = SETBIT (hash_key, new_local_depth);
1615  if (ehash_connect_bucket (thread_p, ehid_p, new_local_depth, hash_key, &sibling_vpid, is_temp) != NO_ERROR)
1616  {
1617  pgbuf_unfix_and_init (thread_p, sibling_page_p);
1618  if (!is_temp)
1619  {
1620  log_sysop_abort (thread_p);
1621  }
1622  return NULL;
1623  }
1624 
1625  if (!is_temp)
1626  {
1627  log_sysop_commit (thread_p);
1628  }
1629  return sibling_page_p;
1630 }
1631 
1632 static EHASH_RESULT
1634  EHASH_DIR_HEADER * dir_header_p, VPID * bucket_vpid_p, void *key_p,
1635  EHASH_HASH_KEY hash_key, int lock_type, bool is_temp, OID * value_p,
1636  VPID * existing_ovf_vpid_p)
1637 {
1638  PAGE_PTR bucket_page_p;
1639  PAGE_PTR sibling_page_p = NULL;
1640  PAGE_PTR target_bucket_page_p;
1641  int new_bit;
1642  EHASH_RESULT result;
1643 
1644  /* We need to put a X_LOCK on bucket page */
1645  bucket_page_p = ehash_fix_old_page (thread_p, &ehid_p->vfid, bucket_vpid_p, PGBUF_LATCH_WRITE);
1646  if (bucket_page_p == NULL)
1647  {
1648  return EHASH_ERROR_OCCURRED;
1649  }
1650 
1651  result =
1652  ehash_insert_to_bucket (thread_p, ehid_p, &dir_header_p->overflow_file, is_temp, bucket_page_p,
1653  dir_header_p->key_type, key_p, value_p, existing_ovf_vpid_p);
1654  if (result == EHASH_BUCKET_FULL)
1655  {
1656  if (lock_type == S_LOCK)
1657  {
1658  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1659  return EHASH_BUCKET_FULL;
1660  }
1661 
1662  sibling_page_p =
1663  ehash_extend_bucket (thread_p, ehid_p, dir_root_page_p, dir_header_p, bucket_page_p, key_p, hash_key, &new_bit,
1664  bucket_vpid_p, is_temp);
1665  if (sibling_page_p == NULL)
1666  {
1667  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1668  return EHASH_ERROR_OCCURRED;
1669  }
1670 
1671  /*
1672  * Try to insert the new key & assoc_value pair into one of the buckets.
1673  * The result of this attempt will determine if a recursive call is
1674  * needed to insert the new key.
1675  */
1676 
1677  if (new_bit)
1678  {
1679  target_bucket_page_p = sibling_page_p;
1680  }
1681  else
1682  {
1683  target_bucket_page_p = bucket_page_p;
1684  }
1685 
1686  result =
1687  ehash_insert_to_bucket (thread_p, ehid_p, &dir_header_p->overflow_file, is_temp, target_bucket_page_p,
1688  dir_header_p->key_type, key_p, value_p, existing_ovf_vpid_p);
1689  pgbuf_unfix_and_init (thread_p, sibling_page_p);
1690  }
1691 
1692  pgbuf_unfix_and_init (thread_p, bucket_page_p);
1693  return result;
1694 }
1695 
1696 /*
1697  * ehash_insert_helper () - Perform insertion
1698  * return: void * (NULL is returned in case of error)
1699  * ehid(in): identifier for the extendible hashing structure
1700  * key(in): key value
1701  * value_ptr(in): associated value to insert
1702  * lock_type(in): type of lock to acquire for accessing directory pages
1703  * existing_ovf_vpid(in):
1704  *
1705  */
1706 static void *
1707 ehash_insert_helper (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p, OID * value_p, int lock_type,
1708  VPID * existing_ovf_vpid_p)
1709 {
1710  EHASH_DIR_HEADER *dir_header_p;
1711  PAGE_PTR dir_root_page_p;
1712  VPID bucket_vpid;
1713 
1714  EHASH_HASH_KEY hash_key;
1715  int location;
1716  EHASH_RESULT result;
1717  bool is_temp = false;
1718 
1719  if (file_is_temp (thread_p, &ehid_p->vfid, &is_temp) != NO_ERROR)
1720  {
1721  ASSERT_ERROR ();
1722  return NULL;
1723  }
1724 
1725  dir_root_page_p =
1726  ehash_find_bucket_vpid_with_hash (thread_p, ehid_p, key_p,
1728  &bucket_vpid, &hash_key, &location);
1729  if (dir_root_page_p == NULL)
1730  {
1731  return NULL;
1732  }
1733 
1734  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
1735 
1736 #if defined(EHINSERTION_ORDER)
1737  fprintf (stdout, "Ex Hash %d|%d|%d Insert:", ehid_p->vfid.volid, ehid_p->vfid.fileid, ehid_p->pageid);
1738  eh_dump_key (dir_header_p->key_type, key_p, value_p);
1739 #endif /* EHINSERTION_ORDER */
1740 
1741  if (dir_header_p->key_type == DB_TYPE_STRING)
1742  {
1743  /* max length of class name is 255 */
1744  assert (strlen ((char *) key_p) < 256);
1745 
1746 #if defined (ENABLE_UNUSED_FUNCTION)
1747  /* Check if string is too long and no overflow file has been created */
1748  if ((strlen ((char *) key_p) + 1) > EHASH_MAX_STRING_SIZE && VFID_ISNULL (&dir_header_p->overflow_file))
1749  {
1750  if (lock_type == S_LOCK)
1751  {
1752  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1753  return ehash_insert_helper (thread_p, ehid_p, key_p, value_p, X_LOCK, existing_ovf_vpid_p);
1754  }
1755  else
1756  {
1757  if (ehash_create_overflow_file (thread_p, ehid_p, dir_root_page_p, dir_header_p, file_type) != NO_ERROR)
1758  {
1759  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1760  return NULL;
1761  }
1762  }
1763  }
1764 #endif
1765  }
1766 
1767  if (VPID_ISNULL (&bucket_vpid))
1768  {
1769  if (lock_type == S_LOCK)
1770  {
1771  /* release lock and call itself to obtain X_LOCK */
1772  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1773  return ehash_insert_helper (thread_p, ehid_p, key_p, value_p, X_LOCK, existing_ovf_vpid_p);
1774  }
1775  else
1776  {
1778  (thread_p, ehid_p, dir_root_page_p, dir_header_p, &bucket_vpid, location, hash_key, is_temp, key_p,
1779  value_p, existing_ovf_vpid_p) != NO_ERROR)
1780  {
1781  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1782  return NULL;
1783  }
1784  }
1785  }
1786  else
1787  {
1788  result =
1789  ehash_insert_bucket_after_extend_if_need (thread_p, ehid_p, dir_root_page_p, dir_header_p, &bucket_vpid, key_p,
1790  hash_key, lock_type, is_temp, value_p, existing_ovf_vpid_p);
1791  if (result == EHASH_ERROR_OCCURRED)
1792  {
1793  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1794  return NULL;
1795  }
1796  else if (result == EHASH_BUCKET_FULL)
1797  {
1798  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1799  return ehash_insert_helper (thread_p, ehid_p, key_p, value_p, X_LOCK, existing_ovf_vpid_p);
1800  }
1801  }
1802 
1803  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
1804  return (key_p);
1805 }
1806 
1807 /*
1808  * ehash_insert_to_bucket () - Insert (key, value) to bucket
1809  * return: EHASH_RESULT
1810  * ehid(in): identifier for the extendible hashing structure
1811  * overflow_file(in): Overflow file for extendible hash
1812  * is_temp(in): is extensible hash temporary?
1813  * buc_pgptr(in): bucket page to insert the key
1814  * key_type(in): type of the key
1815  * key_ptr(in): Pointer to the key
1816  * value_ptr(in): Pointer to the associated value
1817  * existing_ovf_vpid(in):
1818  *
1819  * Note: This function is used to insert a (key & assoc_value) pair
1820  * onto the given bucket. If the KEY already EXISTS in the bucket
1821  * the NEW ASSOCIATED VALUE REPLACES THE OLD ONE. Otherwise a
1822  * new entry is added to the bucket and the total number of
1823  * entries of this extendible hashing structure is incremented
1824  * by one. In the latter case, if the insertion is not possible
1825  * for some reason (e.g., the bucket does not have enough space
1826  * for the new record, etc.) an appropriate error code is returned.
1827  */
1828 static EHASH_RESULT
1829 ehash_insert_to_bucket (THREAD_ENTRY * thread_p, EHID * ehid_p, VFID * ovf_file_p, bool is_temp,
1830  PAGE_PTR bucket_page_p, DB_TYPE key_type, void *key_p, OID * value_p,
1831  VPID * existing_ovf_vpid_p)
1832 {
1833  char *bucket_record_p;
1834  RECDES bucket_recdes;
1835  RECDES old_bucket_recdes;
1836  RECDES log_recdes;
1837 #if defined (ENABLE_UNUSED_FUNCTION)
1838  RECDES ovf_recdes;
1839  VPID ovf_vpid;
1840  char *record_p;
1841 #endif
1842  char *log_record_p;
1843 
1844  PGSLOTID slot_no;
1845  int success;
1846  bool is_replaced_oid = false;
1847 
1848  /* Check if insertion is possible, or not */
1849  if (ehash_locate_slot (thread_p, bucket_page_p, key_type, key_p, &slot_no) == true)
1850  {
1851  /*
1852  * Key already exists. So, replace the associated value
1853  * MIGHT BE CHANGED to allow multiple values
1854  */
1855  (void) spage_get_record (thread_p, bucket_page_p, slot_no, &old_bucket_recdes, PEEK);
1856  bucket_record_p = (char *) old_bucket_recdes.data;
1857 
1858  /*
1859  * Store the original OID associated with the key before it is replaced
1860  * with the new OID value.
1861  */
1862  is_replaced_oid = true;
1863 
1864  if (ehash_allocate_recdes (&bucket_recdes, old_bucket_recdes.length, old_bucket_recdes.type) == NULL)
1865  {
1866  return EHASH_ERROR_OCCURRED;
1867  }
1868 
1869  memcpy (bucket_recdes.data, bucket_record_p, bucket_recdes.length);
1870  (void) ehash_write_oid_to_record (bucket_record_p, value_p);
1871  }
1872  else
1873  {
1874  /*
1875  * Key does not exist in the bucket, so create a record for it and
1876  * insert it into the bucket;
1877  */
1878 
1879  if (ehash_compose_record (key_type, key_p, value_p, &bucket_recdes) != NO_ERROR)
1880  {
1881  return EHASH_ERROR_OCCURRED;
1882  }
1883 
1884 #if defined (ENABLE_UNUSED_FUNCTION)
1885  /*
1886  * If this is a long string produce the prefix key record and see if it
1887  * is going to fit into this page. If it fits, produce the overflow pages.
1888  * If it does not return failure with SP_BUCKET_FULL value.
1889  */
1890  if (bucket_recdes.type == REC_BIGONE)
1891  {
1892  if (bucket_recdes.length > spage_max_space_for_new_record (thread_p, bucket_page_p))
1893  {
1894  ehash_free_recdes (&bucket_recdes);
1895  return EHASH_BUCKET_FULL;
1896  }
1897 
1898  ovf_recdes.data = (char *) key_p + EHASH_LONG_STRING_PREFIX_SIZE;
1899  ovf_recdes.area_size = strlen ((char *) key_p) + 1;
1900  ovf_recdes.length = ovf_recdes.area_size;
1901 
1902  /* Update the prefix key record; put overflow page id */
1903  record_p = bucket_recdes.data;
1904  record_p += sizeof (OID); /* Skip the associated OID */
1905 
1906  if (existing_ovf_vpid_p != NULL)
1907  /*
1908  * Overflow pages already exists for this key (i.e., we are
1909  * undoing a deletion of a long string
1910  * **** TODO: M2 Is this right ? ****
1911  */
1912  *(VPID *) record_p = *existing_ovf_vpid_p;
1913  else
1914  {
1915  /* Create the overflow pages */
1916  if (overflow_insert (thread_p, ovf_file_p, &ovf_vpid, &ovf_recdes, NULL) == NULL)
1917  {
1918  /*
1919  * overflow pages creation failed; do not insert the prefix
1920  * key record to the bucket; return with error
1921  */
1922  ehash_free_recdes (&bucket_recdes);
1923  return EHASH_ERROR_OCCURRED;
1924  }
1925  *(VPID *) record_p = ovf_vpid;
1926  }
1927  }
1928 #endif
1929 
1930  /* Try to put the record to the slotted page */
1931  success = spage_insert_at (thread_p, bucket_page_p, slot_no, &bucket_recdes);
1932  if (success != SP_SUCCESS)
1933  {
1934  /* Problem the record was not inserted to the page */
1935  ehash_free_recdes (&bucket_recdes);
1936 
1937 #if defined (ENABLE_UNUSED_FUNCTION)
1938  if (bucket_recdes.type == REC_BIGONE && existing_ovf_vpid_p == NULL)
1939  {
1940  /* if overflow pages has just been created delete them */
1941  (void) overflow_delete (thread_p, ovf_file_p, &ovf_vpid);
1942  }
1943 #endif
1944 
1945  if (success == SP_DOESNT_FIT)
1946  {
1947  /* There is not enough space on the slotted page for the new record */
1948  return EHASH_BUCKET_FULL;
1949  }
1950  else
1951  {
1953  return EHASH_ERROR_OCCURRED;
1954  }
1955  }
1956  }
1957 
1958  /***********************************
1959  Log this insertion operation
1960  ***********************************/
1961 
1962  /* Add the "ehid" to the record for no-page operation logging */
1963  if (ehash_allocate_recdes (&log_recdes, bucket_recdes.length + sizeof (EHID), REC_HOME) == NULL)
1964  {
1965  return EHASH_ERROR_OCCURRED;
1966  }
1967 
1968  log_record_p = log_recdes.data;
1969 
1970  /* First insert the Ext. Hashing identifier */
1971  log_record_p = ehash_write_ehid_to_record (log_record_p, ehid_p);
1972 
1973  /* Copy (the assoc-value, key) pair from the bucket record */
1974  memcpy (log_record_p, bucket_recdes.data, bucket_recdes.length);
1975 
1976  if (!is_temp)
1977  {
1978  if (is_replaced_oid)
1979  {
1980  /*
1981  * This insertion has actully replaced the original oid. The undo
1982  * logging should cause this original oid to be restored in the record.
1983  * The undo recovery function "eh_rvundo_delete" with the original oid
1984  * as parameter will do this.
1985  */
1986  log_append_undo_data2 (thread_p, RVEH_DELETE, &ehid_p->vfid, NULL, bucket_recdes.type, log_recdes.length,
1987  log_recdes.data);
1988  }
1989  else
1990  {
1991  /* insertion */
1992  log_append_undo_data2 (thread_p, RVEH_INSERT, &ehid_p->vfid, NULL, bucket_recdes.type, log_recdes.length,
1993  log_recdes.data);
1994  }
1995  }
1996 
1997  if (is_replaced_oid)
1998  {
1999  /*
2000  * This insertion has actually replaced the original oid. The redo logging
2001  * should cause this new oid to be written at its current physical
2002  * location.
2003  */
2004  if (!is_temp)
2005  {
2006  log_append_redo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, bucket_page_p,
2007  (int) (old_bucket_recdes.data - bucket_page_p), sizeof (OID), old_bucket_recdes.data);
2008  }
2009  }
2010  else
2011  {
2012  /* Store the rec_type as "short" to avoid alignment problems */
2013  log_recdes.area_size = log_recdes.length = bucket_recdes.length + sizeof (short);
2014 
2015  /*
2016  * If undo logging was done the log_recdes.data should have enough space
2017  * for the redo logging since the redo log record is shorter than
2018  * the undo log record. Otherwise, allocate space for redo log record.
2019  */
2020  if (log_recdes.data == NULL)
2021  {
2022  if (ehash_allocate_recdes (&log_recdes, log_recdes.length, REC_HOME) == NULL)
2023  {
2024  return EHASH_ERROR_OCCURRED;
2025  }
2026  }
2027  log_record_p = log_recdes.data;
2028 
2029  /* First insert the key_type identifier */
2030 
2031  *(short *) log_record_p = bucket_recdes.type;
2032  log_record_p += sizeof (short);
2033 
2034  /* Copy (the assoc-value, key) pair from the bucket record */
2035  memcpy (log_record_p, bucket_recdes.data, bucket_recdes.length);
2036 
2037  if (!is_temp)
2038  {
2039  log_append_redo_data2 (thread_p, RVEH_INSERT, &ehid_p->vfid, bucket_page_p, slot_no, log_recdes.length,
2040  log_recdes.data);
2041  }
2042  }
2043 
2044  if (bucket_recdes.data)
2045  {
2046  ehash_free_recdes (&bucket_recdes);
2047  }
2048 
2049  if (log_recdes.data)
2050  {
2051  ehash_free_recdes (&log_recdes);
2052  }
2053 
2054  pgbuf_set_dirty (thread_p, bucket_page_p, DONT_FREE);
2056 }
2057 
2058 static int
2059 ehash_write_key_to_record (RECDES * recdes_p, DB_TYPE key_type, void *key_p, short key_size, OID * value_p,
2060  bool is_long_str)
2061 {
2062  char *record_p;
2063 
2064  recdes_p->type = is_long_str ? REC_BIGONE : REC_HOME;
2065 
2066  record_p = recdes_p->data;
2067  record_p = ehash_write_oid_to_record (record_p, value_p);
2068 
2069  /* TODO: M2 64-bit ??? Is this really needed... Can we just move bytes.. we have the size of the key.. AT least all
2070  * data types but string_key */
2071  switch (key_type)
2072  {
2073  case DB_TYPE_STRING:
2074 #if defined (ENABLE_UNUSED_FUNCTION)
2075  if (is_long_str)
2076  {
2077  /* Key is a long string; produce just the prefix reord */
2078  /* Skip the overflow page id; will be filled by the caller function */
2079  record_p += sizeof (VPID);
2080  }
2081 #endif
2082  memcpy (record_p, (char *) key_p, key_size);
2083  record_p += key_size;
2084  break;
2085 
2086  case DB_TYPE_OBJECT:
2087  *(OID *) record_p = *(OID *) key_p;
2088  break;
2089 #if defined (ENABLE_UNUSED_FUNCTION)
2090  case DB_TYPE_DOUBLE:
2091  OR_MOVE_DOUBLE (key_p, record_p);
2092  break;
2093 
2094  case DB_TYPE_FLOAT:
2095  *(float *) record_p = *(float *) key_p;
2096  break;
2097 
2098  case DB_TYPE_INTEGER:
2099  *(int *) record_p = *(int *) key_p;
2100  break;
2101 
2102  case DB_TYPE_BIGINT:
2103  *(DB_BIGINT *) record_p = *(DB_BIGINT *) key_p;
2104  break;
2105 
2106  case DB_TYPE_SHORT:
2107  *(short *) record_p = *(short *) key_p;
2108  break;
2109 
2110  case DB_TYPE_DATE:
2111  *(DB_DATE *) record_p = *(DB_DATE *) key_p;
2112  break;
2113 
2114  case DB_TYPE_TIME:
2115  *(DB_TIME *) record_p = *(DB_TIME *) key_p;
2116  break;
2117 
2118  case DB_TYPE_TIMESTAMP:
2119  *(DB_UTIME *) record_p = *(DB_UTIME *) key_p;
2120  break;
2121 
2122  case DB_TYPE_DATETIME:
2123  *(DB_DATETIME *) record_p = *(DB_DATETIME *) key_p;
2124  break;
2125 
2126  case DB_TYPE_MONETARY:
2127  OR_MOVE_DOUBLE (&((DB_MONETARY *) key_p)->amount, record_p);
2128  break;
2129 #endif
2130  default:
2131  /* Unspecified key type: Directory header has been corrupted */
2133  ehash_free_recdes (recdes_p);
2134  return ER_EH_CORRUPTED;
2135  }
2136 
2137  return NO_ERROR;
2138 }
2139 
2140 /*
2141  * ehash_compose_record () -
2142  * return: NO_ERROR, or ER_FAILED
2143  * key_type(in): type of the key
2144  * key_ptr(in): Pointer to the key
2145  * value_ptr(in): Pointer to the associated value
2146  * recdes(in): Pointer to the Record descriptor to fill in
2147  *
2148  * Note: This function prepares a record and sets the fields of the
2149  * passed record descriptor to describe it. All of the records
2150  * prepared by this function contain the
2151  * (associated_value, key_value) pairs (in this order). However,
2152  * these two fields may optionally be preceeded with the "ehid"
2153  * information. The records with the "ehid" information are used
2154  * for logging purposes. The ones without ehid information are
2155  * used to insert entries into the bucket pages. The dynamic
2156  * memory area allocated by this function for the record should
2157  * be freed by the caller function.
2158  *
2159  * If the key is of string type and it is > EHASH_MAX_STRING_SIZE, we
2160  * only include a prefix to the key. The caller is responsible
2161  * for the overflow part.
2162  */
2163 static int
2164 ehash_compose_record (DB_TYPE key_type, void *key_p, OID * value_p, RECDES * recdes_p)
2165 {
2166  short key_size;
2167  int record_size;
2168  bool is_long_str = false;
2169 
2170  if (key_type == DB_TYPE_STRING)
2171  {
2172  key_size = (short) strlen ((char *) key_p) + 1; /* Plus one is for \0 */
2173 
2174  /* max length of class name is 255 */
2175  assert (key_size <= 256);
2176 
2177 #if defined (ENABLE_UNUSED_FUNCTION)
2178  /* Check if string is too long */
2179  if (key_size > EHASH_MAX_STRING_SIZE)
2180  {
2181  is_long_str = true;
2182  key_size = EHASH_LONG_STRING_PREFIX_SIZE;
2183  }
2184 #endif
2185 
2186  record_size = sizeof (OID) + key_size;
2187 
2188 #if defined (ENABLE_UNUSED_FUNCTION)
2189  /* If an overflow page is needed, reserve space for it */
2190  if (is_long_str)
2191  {
2192  record_size += sizeof (VPID);
2193  }
2194 #endif
2195  }
2196  else
2197  {
2198  key_size = ehash_get_key_size (key_type);
2199  if (key_size < 0)
2200  {
2201  return ER_FAILED;
2202  }
2203  record_size = sizeof (OID) + key_size;
2204  }
2205 
2206  if (ehash_allocate_recdes (recdes_p, record_size, REC_HOME) == NULL)
2207  {
2208  return ER_FAILED;
2209  }
2210 
2211  return ehash_write_key_to_record (recdes_p, key_type, key_p, key_size, value_p, is_long_str);
2212 }
2213 
2214 static int
2215 ehash_compare_key (THREAD_ENTRY * thread_p, char *bucket_record_p, DB_TYPE key_type, void *key_p, INT16 record_type,
2216  int *out_compare_result_p)
2217 {
2218  int compare_result;
2219 #if defined (ENABLE_UNUSED_FUNCTION)
2220  VPID *ovf_vpid_p;
2221  double d1, d2;
2222  float f1, f2;
2223  DB_DATETIME *dt1, *dt2;
2224  DB_BIGINT bi1, bi2;
2225 #endif
2226 
2227  switch (key_type)
2228  {
2229  case DB_TYPE_STRING:
2230 #if defined (ENABLE_UNUSED_FUNCTION)
2231  if (record_type == REC_BIGONE)
2232  {
2233  /* bucket record is a LOG key string */
2234  ovf_vpid_p = (VPID *) bucket_record_p;
2235  bucket_record_p += sizeof (VPID);
2236 
2237  compare_result = ehash_ansi_sql_strncmp ((char *) key_p, bucket_record_p, EHASH_LONG_STRING_PREFIX_SIZE);
2238  if (!compare_result)
2239  {
2240  /*
2241  * The prefix of the bucket string matches with the given key.
2242  * It is very likely that the whole key will match. So, retrive
2243  * the chopped portion of the bucket string and compare it with
2244  * the chopped portion of the key.
2245  */
2246  if (ehash_compare_overflow (thread_p, ovf_vpid_p, (char *) key_p + EHASH_LONG_STRING_PREFIX_SIZE,
2247  &compare_result) != NO_ERROR)
2248  {
2249  return ER_FAILED;
2250  }
2251  }
2252  }
2253  else
2254 #endif
2255  {
2256  compare_result = ansisql_strcmp ((char *) key_p, bucket_record_p);
2257  }
2258  break;
2259 
2260  case DB_TYPE_OBJECT:
2261  compare_result = oid_compare ((OID *) key_p, (OID *) bucket_record_p);
2262  break;
2263 #if defined (ENABLE_UNUSED_FUNCTION)
2264  case DB_TYPE_DOUBLE:
2265  OR_MOVE_DOUBLE (key_p, &d1);
2266  OR_MOVE_DOUBLE (bucket_record_p, &d2);
2267 
2268  if (d1 == d2)
2269  {
2270  compare_result = 0;
2271  }
2272  else if (d1 > d2)
2273  {
2274  compare_result = 1;
2275  }
2276  else
2277  {
2278  compare_result = -1;
2279  }
2280  break;
2281 
2282  case DB_TYPE_FLOAT:
2283  f1 = *((float *) key_p);
2284  f2 = *((float *) bucket_record_p);
2285 
2286  if (f1 == f2)
2287  {
2288  compare_result = 0;
2289  }
2290  else if (f1 > f2)
2291  {
2292  compare_result = 1;
2293  }
2294  else
2295  {
2296  compare_result = -1;
2297  }
2298  break;
2299 
2300  case DB_TYPE_INTEGER:
2301  compare_result = *(int *) key_p - *(int *) bucket_record_p;
2302  break;
2303 
2304  case DB_TYPE_BIGINT:
2305  bi1 = *((DB_BIGINT *) key_p);
2306  bi2 = *((DB_BIGINT *) bucket_record_p);
2307 
2308  if (bi1 == bi2)
2309  {
2310  compare_result = 0;
2311  }
2312  else if (bi1 > bi2)
2313  {
2314  compare_result = 1;
2315  }
2316  else
2317  {
2318  compare_result = -1;
2319  }
2320  break;
2321 
2322  case DB_TYPE_SHORT:
2323  compare_result = *(short *) key_p - *(short *) bucket_record_p;
2324  break;
2325 
2326  case DB_TYPE_DATE:
2327  compare_result = *(DB_DATE *) key_p - *(DB_DATE *) bucket_record_p;
2328  break;
2329 
2330  case DB_TYPE_TIME:
2331  compare_result = *(DB_TIME *) key_p - *(DB_TIME *) bucket_record_p;
2332  break;
2333 
2334  case DB_TYPE_TIMESTAMP:
2335  compare_result = *(DB_UTIME *) key_p - *(DB_UTIME *) bucket_record_p;
2336  break;
2337 
2338  case DB_TYPE_DATETIME:
2339  dt1 = (DB_DATETIME *) key_p;
2340  dt2 = (DB_DATETIME *) bucket_record_p;
2341 
2342  if (dt1->date < dt2->date)
2343  {
2344  compare_result = -1;
2345  }
2346  else if (dt1->date > dt2->date)
2347  {
2348  compare_result = 1;
2349  }
2350  else if (dt1->time < dt2->time)
2351  {
2352  compare_result = -1;
2353  }
2354  else if (dt1->time > dt2->time)
2355  {
2356  compare_result = 1;
2357  }
2358  else
2359  {
2360  compare_result = 0;
2361  }
2362  break;
2363 
2364  case DB_TYPE_MONETARY:
2365  OR_MOVE_DOUBLE (&((DB_MONETARY *) key_p)->amount, &d1);
2366  OR_MOVE_DOUBLE (&((DB_MONETARY *) bucket_record_p)->amount, &d2);
2367 
2368  if (d1 == d2)
2369  {
2370  compare_result = 0;
2371  }
2372  else if (d1 > d2)
2373  {
2374  compare_result = 1;
2375  }
2376  else
2377  {
2378  compare_result = -1;
2379  }
2380  break;
2381 #endif
2382  default:
2383  /* Unspecified key type: Directory header has been corrupted */
2385  return ER_EH_CORRUPTED;
2386  }
2387 
2388  *out_compare_result_p = compare_result;
2389  return NO_ERROR;
2390 }
2391 
2392 static bool
2393 ehash_binary_search_bucket (THREAD_ENTRY * thread_p, PAGE_PTR bucket_page_p, PGSLOTID num_record, DB_TYPE key_type,
2394  void *key_p, PGSLOTID * out_position_p)
2395 {
2396  char *bucket_record_p;
2397  RECDES recdes;
2398  PGSLOTID low, high;
2399  PGSLOTID middle;
2400  int compare_result;
2401 
2402  low = 1;
2403  high = num_record;
2404 
2405  do
2406  {
2407  middle = (high + low) >> 1;
2408 
2409  if (spage_get_record (thread_p, bucket_page_p, middle, &recdes, PEEK) != S_SUCCESS)
2410  {
2412  return false;
2413  }
2414 
2415  bucket_record_p = (char *) recdes.data;
2416  bucket_record_p += sizeof (OID);
2417 
2418  if (ehash_compare_key (thread_p, bucket_record_p, key_type, key_p, recdes.type, &compare_result) != NO_ERROR)
2419  {
2420  return false;
2421  }
2422 
2423  if (compare_result == 0)
2424  {
2425  *out_position_p = middle;
2426  return true;
2427  }
2428 
2429  if (compare_result < 0)
2430  {
2431  high = middle - 1;
2432  }
2433  else
2434  {
2435  low = middle + 1;
2436  }
2437  }
2438  while (high >= low);
2439 
2440  if (high < middle)
2441  {
2442  *out_position_p = middle;
2443  }
2444  else
2445  {
2446  /* Key is NOT in bucket; it should be located at the right of middle key. */
2447  *out_position_p = middle + 1;
2448  }
2449 
2450  return false;
2451 }
2452 
2453 /*
2454  * ehash_locate_slot () - Locate the slot for the key
2455  * return: int
2456  * buc_pgptr(in): pointer to the bucket page
2457  * key_type(in):
2458  * key(in): key value to search
2459  * position(out): set to the location of the slot that contains the key if
2460  * it exists, or that would contain the key if it does not.
2461  *
2462  * Note: This function locates a specific key in a bucket
2463  * (namely, the slot number in the page). Currently this search
2464  * is done by using the binary search method on the real key
2465  * values. An alternative method is to use another hashing
2466  * mechanism on the pseudo keys. If the key does not exist,
2467  * position is set to the SLOTID value where it could be
2468  * inserted and false is returned.
2469  */
2470 static bool
2471 ehash_locate_slot (THREAD_ENTRY * thread_p, PAGE_PTR bucket_page_p, DB_TYPE key_type, void *key_p,
2472  PGSLOTID * out_position_p)
2473 {
2474  RECDES recdes;
2475  PGSLOTID num_record;
2476  PGSLOTID first_slot_id = -1;
2477 
2478  num_record = spage_number_of_records (bucket_page_p) - 1;
2479 
2480  /*
2481  * If the bucket does not contain any records other than the header,
2482  * then return immediately.
2483  */
2484  if (num_record < 1)
2485  {
2486  if (spage_next_record (bucket_page_p, &first_slot_id, &recdes, PEEK) != S_SUCCESS)
2487  {
2489  *out_position_p = 1;
2490  return false;
2491  }
2492 
2493  *out_position_p = first_slot_id + 1;
2494  return false;
2495  }
2496 
2497  return ehash_binary_search_bucket (thread_p, bucket_page_p, num_record, key_type, key_p, out_position_p);
2498 }
2499 
2500 static int
2501 ehash_get_pseudo_key (THREAD_ENTRY * thread_p, RECDES * recdes_p, DB_TYPE key_type, EHASH_HASH_KEY * out_hash_key_p)
2502 {
2503  EHASH_HASH_KEY hash_key;
2504  char *bucket_record_p;
2505 #if defined (ENABLE_UNUSED_FUNCTION)
2506  char *whole_key_p;
2507 #endif
2508 
2509 #if defined (ENABLE_UNUSED_FUNCTION)
2510  if (recdes_p->type == REC_BIGONE)
2511  {
2512  /*
2513  * The record contains a long string Compose the whole key and find
2514  * pseudo key by using the whole key
2515  */
2516  whole_key_p = ehash_compose_overflow (thread_p, recdes_p);
2517  if (whole_key_p == NULL)
2518  {
2519  return ER_FAILED;
2520  }
2521  hash_key = ehash_hash (whole_key_p, key_type);
2522  free_and_init (whole_key_p);
2523  }
2524  else
2525 #endif
2526  {
2527  bucket_record_p = (char *) recdes_p->data;
2528  bucket_record_p += sizeof (OID);
2529  hash_key = ehash_hash (bucket_record_p, key_type);
2530  }
2531 
2532  *out_hash_key_p = hash_key;
2533  return NO_ERROR;
2534 }
2535 
2536 static int
2537 ehash_find_first_bit_position (THREAD_ENTRY * thread_p, EHASH_DIR_HEADER * dir_header_p, PAGE_PTR bucket_page_p,
2538  EHASH_BUCKET_HEADER * bucket_header_p, void *key_p, int num_recs, PGSLOTID first_slot_id,
2539  int *out_old_local_depth_p, int *out_new_local_depth_p)
2540 {
2541  int bit_position;
2542  unsigned int i;
2543  unsigned int difference = 0, check_bit = 0;
2544  EHASH_HASH_KEY first_hash_key, next_hash_key;
2545  PGSLOTID slot_id;
2546  RECDES recdes;
2547 
2548  check_bit = 1 << (EHASH_HASH_KEY_BITS - bucket_header_p->local_depth - 1);
2549 
2550  /* Get the first pseudo key */
2551  first_hash_key = ehash_hash (key_p, dir_header_p->key_type);
2552 
2553  for (slot_id = first_slot_id + 1; slot_id < num_recs; slot_id++)
2554  {
2555  if (spage_get_record (thread_p, bucket_page_p, slot_id, &recdes, PEEK) != S_SUCCESS)
2556  {
2558  return ER_EH_CORRUPTED;
2559  }
2560 
2561  if (ehash_get_pseudo_key (thread_p, &recdes, dir_header_p->key_type, &next_hash_key) != NO_ERROR)
2562  {
2563  return ER_FAILED;
2564  }
2565 
2566  difference = (first_hash_key ^ next_hash_key) | difference;
2567  if (difference & check_bit)
2568  {
2569  break;
2570  }
2571  }
2572 
2573  /* Initilize bit_position to one greater than the old local depth */
2574  bit_position = *out_old_local_depth_p + 1;
2575 
2576  /* Find out the correct bit_position that the keys differ */
2577  for (i = bucket_header_p->local_depth + 1; i <= EHASH_HASH_KEY_BITS; i++)
2578  {
2579  if (difference & check_bit)
2580  {
2581  bit_position = i;
2582  break;
2583  }
2584 
2585  /* Shift the check bit position to right */
2586  check_bit >>= 1;
2587  }
2588 
2589  /* Update the bucket header and the variable parameter to new local depth */
2590  bucket_header_p->local_depth = bit_position;
2591  *out_new_local_depth_p = bit_position;
2592 
2593  return NO_ERROR;
2594 }
2595 
2596 static int
2598  PAGE_PTR bucket_page_p, EHASH_BUCKET_HEADER * bucket_header_p, int num_recs,
2599  PGSLOTID first_slot_id, PAGE_PTR sibling_page_p)
2600 {
2601  PGSLOTID slot_id, sibling_slot_id;
2602  RECDES recdes;
2603  EHASH_HASH_KEY hash_key;
2604  int i, success;
2605 
2606  for (slot_id = i = first_slot_id + 1; i < num_recs; i++)
2607  {
2608  if (spage_get_record (thread_p, bucket_page_p, slot_id, &recdes, PEEK) != S_SUCCESS)
2609  {
2610  return ER_FAILED;
2611  }
2612 
2613  if (ehash_get_pseudo_key (thread_p, &recdes, dir_header_p->key_type, &hash_key) != NO_ERROR)
2614  {
2615  return ER_FAILED;
2616  }
2617 
2618  if (GETBIT (hash_key, bucket_header_p->local_depth))
2619  {
2620  success = spage_insert (thread_p, sibling_page_p, &recdes, &sibling_slot_id);
2621  if (success != SP_SUCCESS)
2622  {
2623 #ifdef EHASH_DEBUG
2625  "Unable to move the record from the bucket to empty sibling bucket. Slotted page module"
2626  " refuses to insert a short size record to an empty page. This should never happen.");
2627 #endif
2629 
2630  return ER_FAILED;
2631  }
2632  (void) spage_delete (thread_p, bucket_page_p, slot_id);
2633  /*
2634  * slotid is not changed since the current slot is deleted and the
2635  * next slot will take its slotid.
2636  */
2637  }
2638  else
2639  {
2640  /* skip the slot */
2641  slot_id++;
2642  }
2643  }
2644 
2645  return NO_ERROR;
2646 }
2647 
2648 /*
2649  * ehash_split_bucket () - Split a bucket into two
2650  * return: PAGE_PTR (Sibling Pageptr), NULL in case of error in split.
2651  * dir_header(in): directory header;
2652  * buc_pgptr(in): pointer to bucket page to split
2653  * key(in): key value to insert after split
2654  * old_local_depth(in): old local depth before the split operation; to be set
2655  * new_local_depth(in): new local depth after the split operation; to be set
2656  * sib_vpid(in): vpid of the sibling bucket; to be set
2657  * is_temp(in): true if file is temporary (logging not required)
2658  *
2659  * Note: This function splits the given bucket into two buckets.
2660  * First, the new local depth of the bucket is found. Then a
2661  * sibling bucket is allocated, and the records are
2662  * redistributed. The contents of the sibling bucket is logged
2663  * for recovery purposes. (Note that the contents of the original
2664  * bucket page that is splitting is logged in the insertion
2665  * function after this function returns. Thus, this function
2666  * does not release the buffer that contains this page). Finally,
2667  * the given (key & assoc_value) pair is tried to be inserted
2668  * onto the correct bucket. The result of this attempt is
2669  * returned. (Note that this split may cause only one very
2670  * small record separated from the others, and the new record
2671  * may still be too big to fit into the bucket.) In addition to
2672  * the return value, three parameters are set so that the
2673  * directory can be updated to reflect this split.
2674  */
2675 static PAGE_PTR
2676 ehash_split_bucket (THREAD_ENTRY * thread_p, EHASH_DIR_HEADER * dir_header_p, PAGE_PTR bucket_page_p, void *key_p,
2677  int *out_old_local_depth_p, int *out_new_local_depth_p, VPID * sibling_vpid_p, bool is_temp)
2678 {
2679  EHASH_BUCKET_HEADER *bucket_header_p;
2680  VFID bucket_vfid;
2681  PAGE_PTR sibling_page_p;
2682  RECDES recdes;
2683  PGSLOTID first_slot_id = -1;
2684  int num_records;
2685  char init_bucket_data[3];
2686 
2687  int error_code = NO_ERROR;
2688 
2689  if (!is_temp)
2690  {
2691  /* Log the contents of the bucket page before the split operation */
2692  log_append_undo_data2 (thread_p, RVEH_REPLACE, &dir_header_p->bucket_file, bucket_page_p, 0, DB_PAGESIZE,
2693  bucket_page_p);
2694  }
2695 
2696  num_records = spage_number_of_records (bucket_page_p);
2697  /* Retrieve the bucket header and update it */
2698  if (spage_next_record (bucket_page_p, &first_slot_id, &recdes, PEEK) != S_SUCCESS)
2699  {
2701  return NULL;
2702  }
2703 
2704  bucket_header_p = (EHASH_BUCKET_HEADER *) recdes.data;
2705  *out_old_local_depth_p = bucket_header_p->local_depth;
2706 
2707  if (ehash_find_first_bit_position (thread_p, dir_header_p, bucket_page_p, bucket_header_p, key_p, num_records,
2708  first_slot_id, out_old_local_depth_p, out_new_local_depth_p) != NO_ERROR)
2709  {
2710  return NULL;
2711  }
2712 
2713  bucket_vfid = dir_header_p->bucket_file;
2714  init_bucket_data[0] = dir_header_p->alignment;
2715  init_bucket_data[1] = *out_new_local_depth_p;
2716  init_bucket_data[2] = is_temp;
2717 
2718  error_code = file_alloc (thread_p, &bucket_vfid, ehash_initialize_bucket_new_page, init_bucket_data, sibling_vpid_p,
2719  &sibling_page_p);
2720  if (error_code != NO_ERROR)
2721  {
2722  ASSERT_ERROR ();
2723  return NULL;
2724  }
2725  if (sibling_page_p == NULL)
2726  {
2727  assert_release (false);
2728  return NULL;
2729  }
2730 
2731  if (ehash_distribute_records_into_two_bucket (thread_p, dir_header_p, bucket_page_p, bucket_header_p, num_records,
2732  first_slot_id, sibling_page_p) != NO_ERROR)
2733  {
2734  pgbuf_unfix_and_init (thread_p, sibling_page_p);
2735  if (file_dealloc (thread_p, &bucket_vfid, sibling_vpid_p, FILE_EXTENDIBLE_HASH) != NO_ERROR)
2736  {
2737  ASSERT_ERROR ();
2738  }
2739  VPID_SET_NULL (sibling_vpid_p);
2740  return NULL;
2741  }
2742 
2743  if (!is_temp)
2744  {
2745  /*
2746  * TODO: We are logging too much. It is unlikely that the page is full.
2747  * we just distribute the keys among two buckets. It may be better
2748  * to use crumbs to log different portions of the page.
2749  */
2750  log_append_redo_data2 (thread_p, RVEH_REPLACE, &dir_header_p->bucket_file, bucket_page_p, 0, DB_PAGESIZE,
2751  bucket_page_p);
2752  log_append_redo_data2 (thread_p, RVEH_REPLACE, &dir_header_p->bucket_file, sibling_page_p, 0, DB_PAGESIZE,
2753  sibling_page_p);
2754  }
2755 
2756  pgbuf_set_dirty (thread_p, sibling_page_p, DONT_FREE);
2757  pgbuf_set_dirty (thread_p, bucket_page_p, DONT_FREE);
2758 
2759  return sibling_page_p;
2760 }
2761 
2762 /*
2763  * ehash_expand_directory () - Expand directory
2764  * return: NO_ERROR, or ER_FAILED
2765  * ehid(in): identifier for the extendible hashing structure to expand
2766  * new_depth(in): requested new depth
2767  * is_temp(in): true if file is temporary (logging not required)
2768  *
2769  * Note: This function expands the given extendible hashing directory
2770  * as many times as necessary to attain the requested depth.
2771  * This is performed with a single pass on the original
2772  * directory (starting from the last pointer and working through
2773  * the first one).
2774  * During this operation, the directory is locked with X lock to
2775  * prevent others from accessing wrong information. If the
2776  * original directory is not large enough some new pages are
2777  * allocated for the new directory. If the disk becomes full
2778  * during this operation, or any system error occurs, the
2779  * directory expansion is canceled and an error code is
2780  * returned. For recovery purposes, the contents of the
2781  * directory pages are logged during the expansion operation.
2782  */
2783 static int
2784 ehash_expand_directory (THREAD_ENTRY * thread_p, EHID * ehid_p, int new_depth, bool is_temp)
2785 {
2786  int old_pages;
2787  int old_ptrs;
2788  int new_pages;
2789  int new_ptrs;
2790  int check_pages;
2791 
2792  PAGE_PTR dir_header_page_p;
2793  EHASH_DIR_HEADER *dir_header_p;
2794  int end_offset;
2795  int new_end_offset;
2796  int needed_pages;
2797  int i, j;
2798  int exp_times;
2799 
2800  PAGE_PTR old_dir_page_p;
2801  PGLENGTH old_dir_offset;
2802  int old_dir_nth_page;
2803 
2804  PAGE_PTR new_dir_page_p;
2805  PGLENGTH new_dir_offset;
2806  int new_dir_nth_page;
2807 
2808  int error_code = NO_ERROR;
2809 
2810  dir_header_page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_WRITE);
2811  if (dir_header_page_p == NULL)
2812  {
2813  return ER_FAILED;
2814  }
2815  dir_header_p = (EHASH_DIR_HEADER *) dir_header_page_p;
2816 
2817  error_code = file_get_num_user_pages (thread_p, &ehid_p->vfid, &old_pages);
2818  if (error_code != NO_ERROR)
2819  {
2820  ASSERT_ERROR ();
2821  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2822  return error_code;
2823  }
2824  old_pages -= 1; /* The first page starts with 0 */
2825  old_ptrs = 1 << dir_header_p->depth;
2826  exp_times = 1 << (new_depth - dir_header_p->depth);
2827 
2828  new_ptrs = old_ptrs * exp_times;
2829  end_offset = old_ptrs - 1; /* Dir first pointer has an offset of 0 */
2830  ehash_dir_locate (&check_pages, &end_offset);
2831 
2832 #ifdef EHASH_DEBUG
2833  if (check_pages != old_pages)
2834  {
2835  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2837  ehid_p->pageid);
2838  return ER_FAILED;
2839  }
2840 #endif
2841 
2842  /* Find how many pages the expanded directory will occupy */
2843  new_end_offset = new_ptrs - 1;
2844  ehash_dir_locate (&new_pages, &new_end_offset);
2845  needed_pages = new_pages - old_pages;
2846  if (needed_pages > 0)
2847  {
2848  error_code = file_alloc_multiple (thread_p, &ehid_p->vfid, ehash_initialize_dir_new_page, &is_temp, needed_pages,
2849  NULL);
2850  if (error_code != NO_ERROR)
2851  {
2852  ASSERT_ERROR ();
2853  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2854  return error_code;
2855  }
2856  }
2857 
2858  /*****************************
2859  Perform expansion
2860  ******************************/
2861 
2862  /* Initialize source variables */
2863  old_dir_nth_page = old_pages; /* The last page of the old directory */
2864 
2865  old_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, old_dir_nth_page, PGBUF_LATCH_WRITE);
2866  if (old_dir_page_p == NULL)
2867  {
2868  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2869  return ER_FAILED;
2870  }
2871  old_dir_offset = end_offset;
2872 
2873  /* Initialize destination variables */
2874  new_dir_nth_page = new_pages; /* The last page of the new directory */
2875 
2876  new_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, new_dir_nth_page, PGBUF_LATCH_WRITE);
2877  if (new_dir_page_p == NULL)
2878  {
2879  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2880  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
2881  return ER_FAILED;
2882  }
2883  new_dir_offset = new_end_offset;
2884 
2885  if (new_dir_nth_page <= old_pages && !is_temp)
2886  {
2887  /*
2888  * This page is part of old directory.
2889  * Log the initial content of this original directory page
2890  */
2891  log_append_undo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE, new_dir_page_p);
2892  }
2893 
2894  /* Copy old directory records to new (expanded) area */
2895  for (j = old_ptrs; j > 0; j--)
2896  {
2897  if (old_dir_offset < 0)
2898  {
2899  /*
2900  * We reached the end of the old directory page.
2901  * The next bucket pointer is in the previous directory page
2902  */
2903  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
2904 
2905  /* get another page */
2906  old_dir_nth_page--;
2907 
2908  old_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, old_dir_nth_page, PGBUF_LATCH_WRITE);
2909  if (old_dir_page_p == NULL)
2910  {
2911  /* Fetch error; so return */
2912  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2913  pgbuf_unfix_and_init (thread_p, new_dir_page_p);
2914  return ER_FAILED;
2915  }
2916 
2917  /*
2918  * Set the olddir_offset to the offset of the last pointer in the
2919  * current source directory page.
2920  */
2921  if (old_dir_nth_page)
2922  {
2923  /* This is not the first (root) directory page */
2924  old_dir_offset = EHASH_LAST_OFFSET_IN_NON_FIRST_PAGE;
2925  }
2926  else
2927  {
2928  /* This is the first (root) directory page */
2929  old_dir_offset = EHASH_LAST_OFFSET_IN_FIRST_PAGE;
2930  }
2931  }
2932 
2933  /* Copy the next directory record "exp_times" times */
2934  for (i = 1; i <= exp_times; i++)
2935  {
2936  if (new_dir_offset < 0)
2937  {
2938  /*
2939  * There is not any more entries in the new directory page.
2940  * Log this updated directory page, and get next one
2941  */
2942  if (!is_temp)
2943  {
2944  log_append_redo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE,
2945  new_dir_page_p);
2946  }
2947  pgbuf_set_dirty (thread_p, new_dir_page_p, FREE);
2948 
2949  /* get another page */
2950  new_dir_nth_page--;
2951 
2952  new_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, new_dir_nth_page, PGBUF_LATCH_WRITE);
2953  if (new_dir_page_p == NULL)
2954  {
2955  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
2956  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
2957  return ER_FAILED;
2958  }
2959 
2960  if (new_dir_nth_page <= old_pages && !is_temp)
2961  {
2962  /* Log the initial content of this original directory page */
2963  log_append_undo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE,
2964  new_dir_page_p);
2965  }
2966 
2967  /* Set the newdir_offset to the offset of the last pointer in the current destination directory page. */
2968  if (new_dir_nth_page != 0)
2969  {
2970  /* This is not the first (root) dir page */
2971  new_dir_offset = EHASH_LAST_OFFSET_IN_NON_FIRST_PAGE;
2972  }
2973  else
2974  {
2975  /* This is the first (root) dir page */
2976  new_dir_offset = EHASH_LAST_OFFSET_IN_FIRST_PAGE;
2977  }
2978  }
2979 
2980  if ((char *) new_dir_page_p + new_dir_offset != (char *) old_dir_page_p + old_dir_offset)
2981  {
2982  memcpy ((char *) new_dir_page_p + new_dir_offset, (char *) old_dir_page_p + old_dir_offset,
2983  sizeof (EHASH_DIR_RECORD));
2984  }
2985 
2986  /* Advance the destination pointer to new spot */
2987  new_dir_offset -= sizeof (EHASH_DIR_RECORD);
2988  }
2989 
2990  /* Advance the source pointer to new spot */
2991  old_dir_offset -= sizeof (EHASH_DIR_RECORD);
2992  }
2993 
2994  /*
2995  * Update the directory header
2996  */
2997  dir_header_p->depth = new_depth;
2998 
2999  if (!is_temp)
3000  {
3001  /* Log the first directory page which also contains the directory header */
3002  log_append_redo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE, new_dir_page_p);
3003  }
3004 
3005  /* Release the old and new directory pages */
3006  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
3007  pgbuf_set_dirty (thread_p, new_dir_page_p, FREE);
3008 
3009 #ifdef EHASH_DEBUG
3010  {
3011  DKNPAGES npages_bucket = 0;
3012  DKNPAGES npages_ehash = 0;
3013  if (file_get_num_user_pages (thread_p, &dir_header_p->bucket_file, &npages_bucket) == NO_ERROR
3014  && file_get_num_user_pages (thread_p, &ehid_p->vfid, &npages_ehash) == NO_ERROR)
3015  {
3016  if (new_ptrs / npages > EHASH_BALANCE_FACTOR || npages_ehash > npages_bucket)
3017  {
3019  "WARNING: Ext. Hash EHID=%d|%d|%d is unbalanced.\n Num_bucket_pages = %d, "
3020  "num_dir_pages = %d,\n num_dir_pointers = %d, directory_depth = %d.\n"
3021  " You may want to consider another hash function.\n", ehid_p->vfid.volid,
3022  ehid_p->vfid.fileid, ehid_p->pageid, npages_bucket, npages_ehash,
3023  new_ptrs, dir_header_p->depth);
3024  }
3025  }
3026  }
3027 #endif /* EHASH_DEBUG */
3028 
3029  /* Release the root page holding the directory header */
3030  pgbuf_unfix_and_init (thread_p, dir_header_page_p);
3031 
3032  return NO_ERROR;
3033 }
3034 
3035 /*
3036  * ehash_connect_bucket () - Connect bucket to directory
3037  * return: int NO_ERROR, or ER_FAILED
3038  * ehid(in): identifier for the extendible hashing structure
3039  * local_depth(in): local depth of the bucket; determines how many directory
3040  * pointers are set to point to the given bucket identifier
3041  * hash_key(in): pseudo key that led to the bucket page; determines which
3042  * pointers to be updated
3043  * bucket_vpid(in): Identifier of the bucket page to connect
3044  * is_temp(in): true if temporary (logging is not required)
3045  *
3046  * Note: This function connects the given bucket to the directory of
3047  * specified extendible hashing structure. All the directory
3048  * pointers whose number have the same value as the hash_key
3049  * for the first "local_depth" bits are updated with "bucket_vpid".
3050  * Since these pointers are successive to each other, it is
3051  * known that a contagious area (possibly over several pages)
3052  * on the directory will be updated. Thus, this function is
3053  * implemented in the following way: First, the directory pages
3054  * to be updated are determined. Then, these pages are retrieved,
3055  * updated and released one at a time. For recovery purposes, the
3056  * contents of these pages are logged before and after they are updated.
3057  */
3058 static int
3059 ehash_connect_bucket (THREAD_ENTRY * thread_p, EHID * ehid_p, int local_depth, EHASH_HASH_KEY hash_key,
3060  VPID * bucket_vpid_p, bool is_temp)
3061 {
3062  EHASH_DIR_HEADER dir_header;
3063  EHASH_DIR_RECORD *dir_record_p;
3064  EHASH_REPETITION repetition;
3065  PAGE_PTR page_p;
3066  int location;
3067  int first_page, last_page;
3068  int first_ptr_offset, last_ptr_offset;
3069  int start_offset, end_offset;
3070  int diff;
3071  int i, j;
3072  int bef_length;
3073  unsigned int set_bits;
3074  unsigned int clear_bits;
3075 
3076  repetition.vpid = *bucket_vpid_p;
3077 
3078  page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_READ);
3079  if (page_p == NULL)
3080  {
3081  return ER_FAILED;
3082  }
3083 
3084  memcpy (&dir_header, page_p, EHASH_DIR_HEADER_SIZE);
3085  pgbuf_unfix_and_init (thread_p, page_p);
3086 
3087  /* First find out how many page entries will be updated in the directory */
3088  location = GETBITS (hash_key, 1, dir_header.depth);
3089 
3090  diff = dir_header.depth - local_depth;
3091  if (diff != 0)
3092  {
3093  /* There are more than one pointers that need to be updated */
3094  for (set_bits = 0, i = 0; i < diff; i++)
3095  {
3096  set_bits = 2 * set_bits + 1;
3097  }
3098  clear_bits = ~set_bits;
3099 
3100  first_ptr_offset = location & clear_bits;
3101  last_ptr_offset = location | set_bits;
3102 
3103  ehash_dir_locate (&first_page, &first_ptr_offset);
3104  ehash_dir_locate (&last_page, &last_ptr_offset);
3105  }
3106  else
3107  {
3108  /* There is only one pointer that needs to be updated */
3109  first_ptr_offset = location;
3110  ehash_dir_locate (&first_page, &first_ptr_offset);
3111  last_page = first_page;
3112  last_ptr_offset = first_ptr_offset;
3113  }
3114 
3115  /* Go over all of these pages */
3116  for (i = first_page; i <= last_page; i++)
3117  {
3118  page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, i, PGBUF_LATCH_WRITE);
3119  if (page_p == NULL)
3120  {
3121  return ER_FAILED;
3122  }
3123 
3124  if (i == first_page)
3125  {
3126  start_offset = first_ptr_offset;
3127  }
3128  else
3129  {
3130  start_offset = 0;
3131  }
3132 
3133  if (i == last_page)
3134  {
3135  end_offset = last_ptr_offset + sizeof (EHASH_DIR_RECORD);
3136  }
3137  else
3138  {
3139  end_offset = DB_PAGESIZE;
3140  }
3141 
3142  /* Log this page */
3143  bef_length = end_offset - start_offset;
3144  repetition.count = bef_length / sizeof (EHASH_DIR_RECORD);
3145 
3146  if (!is_temp)
3147  {
3148  log_append_undoredo_data2 (thread_p, RVEH_CONNECT_BUCKET, &ehid_p->vfid, page_p, start_offset, bef_length,
3149  sizeof (EHASH_REPETITION), (char *) page_p + start_offset, &repetition);
3150  }
3151 
3152  /* Update the directory page */
3153  dir_record_p = (EHASH_DIR_RECORD *) ((char *) page_p + start_offset);
3154  for (j = 0; j < repetition.count; j++)
3155  {
3156  dir_record_p->bucket_vpid = *bucket_vpid_p;
3157  dir_record_p++;
3158  }
3159  pgbuf_set_dirty (thread_p, page_p, FREE);
3160  }
3161 
3162  return NO_ERROR;
3163 }
3164 
3165 /*
3166  * ehash_find_depth () - Find depth
3167  * return: char (will return 0 in case of error)
3168  * ehid(in): identifier for the extendible hashing structure
3169  * location(in): directory entry whose neighbooring area should be checked
3170  * bucket_vpid(in): vpid of the bucket pointed by the specified directory entry
3171  * sib_vpid(in): vpid of the sibling bucket
3172  *
3173  */
3174 static char
3175 ehash_find_depth (THREAD_ENTRY * thread_p, EHID * ehid_p, int location, VPID * bucket_vpid_p, VPID * sibling_vpid_p)
3176 {
3177  EHASH_DIR_HEADER dir_header;
3178  EHASH_DIR_RECORD *dir_record_p;
3179  PAGE_PTR page_p;
3180  int loc;
3181  int rel_loc;
3182  int iterations;
3183  int page_no;
3184  int prev_page_no;
3185  int i;
3186  unsigned int clear;
3187  char check_depth = 2;
3188  bool is_stop = false;
3189  int check_bit;
3190 
3191  prev_page_no = 0;
3192  page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_WRITE);
3193  if (page_p == NULL)
3194  {
3195  return 0;
3196  }
3197  memcpy (&dir_header, page_p, EHASH_DIR_HEADER_SIZE);
3198 
3199  while ((check_depth <= dir_header.depth) && !is_stop)
3200  {
3201  /*
3202  * Find the base location for this iteration. The base location differs from
3203  * the original location at the check_depth bit (it has the opposite value
3204  * for this bit position) and at the remaining least significant bit
3205  * positions (it has 0 for these bit positions).
3206  */
3207  clear = 1;
3208  for (i = 1; i < check_depth - 1; i++)
3209  {
3210  clear <<= 1;
3211  clear += 1;
3212  }
3213 
3214  clear = ~clear;
3215  loc = location & clear;
3216 
3217  check_bit = GETBIT (loc, EHASH_HASH_KEY_BITS - check_depth + 1);
3218  if (check_bit != 0)
3219  {
3220  loc = CLEARBIT (loc, EHASH_HASH_KEY_BITS - check_depth + 1);
3221  }
3222  else
3223  {
3224  loc = SETBIT (loc, EHASH_HASH_KEY_BITS - check_depth + 1);
3225  }
3226 
3227  /* "loc" is the base_location now */
3228 
3229  iterations = 1 << (check_depth - 2);
3230 
3231  for (i = 0; i < iterations; i++)
3232  {
3233  rel_loc = loc | (i << 1);
3234  ehash_dir_locate (&page_no, &rel_loc);
3235  if (page_no != prev_page_no)
3236  {
3237  pgbuf_unfix_and_init (thread_p, page_p);
3238  page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, page_no, PGBUF_LATCH_WRITE);
3239  if (page_p == NULL)
3240  {
3241  return 0;
3242  }
3243  prev_page_no = page_no;
3244  }
3245  dir_record_p = (EHASH_DIR_RECORD *) ((char *) page_p + rel_loc);
3246 
3247  if (!((VPID_ISNULL (&dir_record_p->bucket_vpid)) || VPID_EQ (&dir_record_p->bucket_vpid, bucket_vpid_p)
3248  || VPID_EQ (&dir_record_p->bucket_vpid, sibling_vpid_p)))
3249  {
3250  is_stop = true;
3251  break;
3252  }
3253  }
3254 
3255  if (!is_stop)
3256  {
3257  check_depth++;
3258  }
3259  }
3260 
3261  pgbuf_unfix_and_init (thread_p, page_p);
3262  return (check_depth - 1);
3263 }
3264 
3265 static EHASH_RESULT
3266 ehash_check_merge_possible (THREAD_ENTRY * thread_p, EHID * ehid_p, EHASH_DIR_HEADER * dir_header_p,
3267  VPID * bucket_vpid_p, PAGE_PTR bucket_page_p, int location, int lock_type,
3268  int *out_old_local_depth_p, VPID * sibling_vpid_p, PAGE_PTR * out_sibling_page_p,
3269  PGSLOTID * out_first_slot_id_p, int *out_num_records_p, int *out_location_p)
3270 {
3271  EHASH_BUCKET_HEADER *bucket_header_p;
3272  EHASH_BUCKET_HEADER sibling_bucket_header;
3273  PAGE_PTR sibling_page_p;
3274 
3275  RECDES recdes;
3276  PGSLOTID first_slot_id = -1;
3277 
3278  int num_records;
3279  int transfer;
3280  int already;
3281  int check_bit;
3282  int loc = location;
3283 
3284  if (spage_next_record (bucket_page_p, &first_slot_id, &recdes, PEEK) != S_SUCCESS)
3285  {
3287  return EHASH_ERROR_OCCURRED;
3288  }
3289 
3290  bucket_header_p = (EHASH_BUCKET_HEADER *) recdes.data;
3291 
3292  *out_old_local_depth_p = bucket_header_p->local_depth;
3293  if (bucket_header_p->local_depth == 0)
3294  {
3295  /* Special case: there is only one bucket; so do not try to merge */
3296  return EHASH_NO_SIBLING_BUCKET;
3297  }
3298 
3299  /* find the location of sibling bucket pointer */
3300  check_bit = GETBIT (loc, EHASH_HASH_KEY_BITS - dir_header_p->depth + bucket_header_p->local_depth);
3301  if (check_bit == 0)
3302  {
3303  loc = SETBIT (loc, EHASH_HASH_KEY_BITS - dir_header_p->depth + bucket_header_p->local_depth);
3304  }
3305  else
3306  {
3307  loc = CLEARBIT (loc, EHASH_HASH_KEY_BITS - dir_header_p->depth + bucket_header_p->local_depth);
3308  }
3309 
3310  /*
3311  * Now, "loc" is the index of directory entry pointing to the sibling bucket
3312  * (of course, if the sibling bucket exists). So, find its absolute location.
3313  */
3314  if (ehash_find_bucket_vpid (thread_p, ehid_p, dir_header_p, loc, PGBUF_LATCH_READ, sibling_vpid_p) != NO_ERROR)
3315  {
3316  return EHASH_ERROR_OCCURRED;
3317  }
3318 
3319  if (VPID_ISNULL (sibling_vpid_p))
3320  {
3321  return EHASH_NO_SIBLING_BUCKET;
3322  }
3323 
3324  if (VPID_EQ (sibling_vpid_p, bucket_vpid_p))
3325  {
3326  /* Special case: there is only one bucket; so do not try to merge */
3327  return EHASH_NO_SIBLING_BUCKET;
3328  }
3329 
3330  sibling_page_p =
3331  ehash_fix_old_page (thread_p, &ehid_p->vfid, sibling_vpid_p,
3332  lock_type == S_LOCK ? PGBUF_LATCH_READ : PGBUF_LATCH_WRITE);
3333 
3334  if (sibling_page_p == NULL)
3335  {
3336  return EHASH_ERROR_OCCURRED;
3337  }
3338 
3339  /* retrieve the bucket header */
3340  recdes.area_size = sizeof (EHASH_BUCKET_HEADER);
3341  recdes.data = (char *) &sibling_bucket_header;
3342 
3343  first_slot_id = -1;
3344  if (spage_next_record (sibling_page_p, &first_slot_id, &recdes, COPY) != S_SUCCESS)
3345  {
3347  pgbuf_unfix_and_init (thread_p, sibling_page_p);
3348 
3349  return EHASH_ERROR_OCCURRED;
3350  }
3351 
3352  if (sibling_bucket_header.local_depth != bucket_header_p->local_depth)
3353  {
3354  pgbuf_unfix_and_init (thread_p, sibling_page_p);
3355 
3356  return EHASH_NO_SIBLING_BUCKET;
3357  }
3358 
3359  /* Check free space of sibling */
3360 
3361  num_records = spage_number_of_records (bucket_page_p);
3362 
3363  if (num_records > 1)
3364  {
3365  /* Bucket page contains records that needs to be moved to the sibling page */
3366  transfer =
3367  (DB_PAGESIZE - (sizeof (EHASH_BUCKET_HEADER) + 2) - spage_max_space_for_new_record (thread_p, bucket_page_p));
3368 
3369  already = (DB_PAGESIZE - spage_max_space_for_new_record (thread_p, sibling_page_p));
3370 
3371  if ((already + transfer) > EHASH_OVERFLOW_THRESHOLD)
3372  {
3373  pgbuf_unfix_and_init (thread_p, sibling_page_p);
3375  }
3376  }
3377 
3378  if (lock_type == S_LOCK)
3379  {
3380  pgbuf_unfix_and_init (thread_p, sibling_page_p);
3381  }
3382  else
3383  {
3384  *out_sibling_page_p = sibling_page_p;
3385  *out_first_slot_id_p = first_slot_id;
3386  *out_num_records_p = num_records;
3387  *out_location_p = loc;
3388  }
3389 
3391 }
3392 
3393 /*
3394  * ehash_delete () - Delete key from ext. hashing
3395  * return: void * (NULL is returned in case of error)
3396  * ehid(in): identifier for the extendible hashing structure
3397  * key(in): Key value to remove
3398  *
3399  * Note: Deletes the given key value (together with its assoc_value)
3400  * from the specified extendible hashing structure. If the key
3401  * does not exist then it returns an error code.
3402  * After it removes the entry from the bucket page, it produces
3403  * an operational log for this deletion operation. Then, it
3404  * checks the condition of the bucket. If the bucket is empty
3405  * or underflown, it releases the locks on the extendible
3406  * hashing structure and calls another function to merge the
3407  * bucket with its sibling bucket.
3408  */
3409 void *
3410 ehash_delete (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p)
3411 {
3412  EHASH_DIR_HEADER *dir_header_p;
3413  PAGE_PTR dir_root_page_p;
3414  PAGE_PTR bucket_page_p;
3415  VPID ovf_vpid VPID_INITIALIZER;
3416  VPID bucket_vpid;
3417  VPID sibling_vpid;
3418 
3419  RECDES bucket_recdes;
3420  RECDES log_recdes_undo;
3421  RECDES log_recdes_redo;
3422  char *log_undo_record_p;
3423  char *log_redo_record_p;
3424 
3425  int location;
3426  int old_local_depth;
3427 
3428  EHASH_RESULT result;
3429  PGSLOTID max_free;
3430  PGSLOTID slot_no;
3431  bool is_long_str = false;
3432  bool is_temp = false;
3433 
3434  bool do_merge;
3435 
3436  if (ehid_p == NULL || key_p == NULL)
3437  {
3438  return NULL;
3439  }
3440 
3441  if (file_is_temp (thread_p, &ehid_p->vfid, &is_temp) != NO_ERROR)
3442  {
3443  ASSERT_ERROR ();
3444  return NULL;
3445  }
3446 
3447  dir_root_page_p =
3448  ehash_find_bucket_vpid_with_hash (thread_p, ehid_p, key_p, PGBUF_LATCH_READ, PGBUF_LATCH_WRITE, &bucket_vpid, NULL,
3449  &location);
3450  if (dir_root_page_p == NULL)
3451  {
3452  return NULL;
3453  }
3454 
3455  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
3456 
3457  if (bucket_vpid.pageid == NULL_PAGEID)
3458  {
3459  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3461  return NULL;
3462  }
3463  else
3464  {
3465  bucket_page_p = ehash_fix_old_page (thread_p, &ehid_p->vfid, &bucket_vpid, PGBUF_LATCH_WRITE);
3466  if (bucket_page_p == NULL)
3467  {
3468  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3469  return NULL;
3470  }
3471 
3472  /* Try to delete key from buc_page */
3473 
3474  /* Check if deletion possible, or not */
3475  if (ehash_locate_slot (thread_p, bucket_page_p, dir_header_p->key_type, key_p, &slot_no) == false)
3476  {
3477  /* Key does not exist, so return errorcode */
3478  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3479  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3481  return NULL;
3482  }
3483  else
3484  {
3485  /* Key exists in the bucket */
3486  (void) spage_get_record (thread_p, bucket_page_p, slot_no, &bucket_recdes, PEEK);
3487 
3488  /* Prepare the undo log record */
3489  if (ehash_allocate_recdes (&log_recdes_undo, bucket_recdes.length + sizeof (EHID), REC_HOME) == NULL)
3490  {
3491  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3492  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3493  return NULL;
3494  }
3495 
3496  log_undo_record_p = log_recdes_undo.data;
3497 
3498  /* First insert the Ext. Hashing identifier */
3499  log_undo_record_p = ehash_write_ehid_to_record (log_undo_record_p, ehid_p);
3500 
3501  /* Copy (the assoc-value, key) pair from the bucket record */
3502  memcpy (log_undo_record_p, bucket_recdes.data, bucket_recdes.length);
3503 
3504  /* Prepare the redo log record */
3505  if (ehash_allocate_recdes (&log_recdes_redo, bucket_recdes.length + sizeof (short), REC_HOME) == NULL)
3506  {
3507  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3508  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3509  return NULL;
3510  }
3511 
3512  log_redo_record_p = log_recdes_redo.data;
3513 
3514  /* First insert the key type */
3515  *(short *) log_redo_record_p = bucket_recdes.type;
3516  log_redo_record_p += sizeof (short);
3517 
3518  /* Copy (the assoc-value, key) pair from the bucket record */
3519  memcpy (log_redo_record_p, bucket_recdes.data, bucket_recdes.length);
3520 
3521 #if defined (ENABLE_UNUSED_FUNCTION)
3522  /* Check if the record contains a long string */
3523  if (bucket_recdes.type == REC_BIGONE)
3524  {
3525  ovf_vpid = *(VPID *) (bucket_recdes.data + sizeof (OID));
3526  is_long_str = true;
3527  /* Overflow pages needs to be removed, too. But, that shoud be done after the bucket record deletion has
3528  * been logged. This way, we will have the overflow pages available when logical undo-delete recovery
3529  * operation is called. Here just save the overflow page(s) id. */
3530  }
3531 #endif
3532 
3533  /* Delete the bucket record from the bucket */
3534  (void) spage_delete (thread_p, bucket_page_p, slot_no);
3535  pgbuf_set_dirty (thread_p, bucket_page_p, DONT_FREE);
3536 
3537  /* Check the condition of bucket after the deletion */
3538  if (spage_number_of_records (bucket_page_p) <= 1)
3539  {
3540  /* Bucket became empty after this deletion */
3541  result = EHASH_BUCKET_EMPTY;
3542  }
3543  else
3544  {
3545  max_free = spage_max_space_for_new_record (thread_p, bucket_page_p);
3546 
3547  /* Check if deletion had caused the Bucket to underflow, or not */
3548  if ((DB_PAGESIZE - max_free) < EHASH_UNDERFLOW_THRESHOLD)
3549  {
3550  /* Yes bucket has underflown */
3551  result = EHASH_BUCKET_UNDERFLOW;
3552  }
3553  else
3554  {
3555  /* Bucket is just fine */
3556  result = EHASH_SUCCESSFUL_COMPLETION;
3557  }
3558  }
3559 
3560  /* Log this deletion operation */
3561 
3562  if (!is_temp)
3563  {
3564  log_append_undo_data2 (thread_p, RVEH_DELETE, &ehid_p->vfid, NULL, bucket_recdes.type,
3565  log_recdes_undo.length, log_recdes_undo.data);
3566 
3567  log_append_redo_data2 (thread_p, RVEH_DELETE, &ehid_p->vfid, bucket_page_p, slot_no,
3568  log_recdes_redo.length, log_recdes_redo.data);
3569  }
3570  ehash_free_recdes (&log_recdes_undo);
3571  ehash_free_recdes (&log_recdes_redo);
3572 
3573  /* Remove the overflow pages, if any. */
3574  if (is_long_str)
3575  {
3576  (void) overflow_delete (thread_p, &dir_header_p->overflow_file, &ovf_vpid);
3577  }
3578 
3579  /* Check for the merge condition */
3580  do_merge = false; /* init */
3581  if (result == EHASH_BUCKET_EMPTY || result == EHASH_BUCKET_UNDERFLOW)
3582  {
3584  (thread_p, ehid_p, dir_header_p, &bucket_vpid, bucket_page_p, location, S_LOCK, &old_local_depth,
3585  &sibling_vpid, NULL, NULL, NULL, NULL) == EHASH_SUCCESSFUL_COMPLETION)
3586  {
3587  do_merge = true;
3588  }
3589  }
3590 
3591  /* Release directory and bucket and return */
3592  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3593  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3594 
3595  /* do merge routine */
3596  if (do_merge)
3597  {
3598  ehash_merge (thread_p, ehid_p, key_p, is_temp);
3599  }
3600 
3601  return (key_p);
3602  }
3603  }
3604 }
3605 
3606 static void
3607 ehash_shrink_directory_if_need (THREAD_ENTRY * thread_p, EHID * ehid_p, EHASH_DIR_HEADER * dir_header_p, bool is_temp)
3608 {
3609  int i;
3610 
3611  /* Check directory shrink condition */
3612  i = dir_header_p->depth;
3613  while (dir_header_p->local_depth_count[i] == 0)
3614  {
3615  i--;
3616  }
3617 
3618  if ((dir_header_p->depth - i) > 1)
3619  {
3620  ehash_shrink_directory (thread_p, ehid_p, i + 1, is_temp);
3621  }
3622 }
3623 
3624 static void
3625 ehash_adjust_local_depth (THREAD_ENTRY * thread_p, EHID * ehid_p, PAGE_PTR dir_root_page_p,
3626  EHASH_DIR_HEADER * dir_header_p, int depth, int delta, bool is_temp)
3627 {
3628  int redo_inc, undo_inc, offset;
3629 
3630  /* Update the directory header for local depth counters */
3631  dir_header_p->local_depth_count[depth] += delta;
3632  pgbuf_set_dirty (thread_p, dir_root_page_p, DONT_FREE);
3633 
3634  /* Log this change on the directory header */
3635  offset = CAST_BUFLEN ((char *) (dir_header_p->local_depth_count + depth) - (char *) dir_root_page_p);
3636  redo_inc = delta;
3637  undo_inc = -delta;
3638 
3639  if (!is_temp)
3640  {
3641  log_append_undoredo_data2 (thread_p, RVEH_INC_COUNTER, &ehid_p->vfid, dir_root_page_p, offset, sizeof (int),
3642  sizeof (int), &undo_inc, &redo_inc);
3643  }
3644 }
3645 
3646 static int
3647 ehash_merge_permanent (THREAD_ENTRY * thread_p, EHID * ehid_p, PAGE_PTR dir_root_page_p,
3648  EHASH_DIR_HEADER * dir_header_p, PAGE_PTR bucket_page_p, PAGE_PTR sibling_page_p,
3649  VPID * bucket_vpid_p, VPID * sibling_vpid_p, int num_records, int location,
3650  PGSLOTID first_slot_id, int *out_new_local_depth_p, bool is_temp)
3651 {
3652  PGSLOTID slot_id;
3653  RECDES recdes;
3654  char *bucket_record_p;
3655  PGSLOTID new_slot_id;
3656  bool is_record_exist;
3657  int success;
3658  EHASH_BUCKET_HEADER sibling_bucket_header;
3659  char found_depth;
3660 
3661  if (!is_temp)
3662  {
3663  log_append_undo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, sibling_page_p, 0, DB_PAGESIZE, sibling_page_p);
3664  }
3665 
3666  /* Move records of original page into the sibling bucket */
3667  for (slot_id = 1; slot_id < num_records; slot_id++)
3668  {
3669  spage_get_record (thread_p, bucket_page_p, slot_id, &recdes, PEEK);
3670  bucket_record_p = (char *) recdes.data;
3671  bucket_record_p += sizeof (OID);
3672 
3673 #if defined (ENABLE_UNUSED_FUNCTION)
3674  if (recdes.type == REC_BIGONE) /* string type */
3675  {
3676  bucket_record_p = ehash_compose_overflow (thread_p, &recdes);
3677  if (bucket_record_p == NULL)
3678  {
3679  return ER_FAILED;
3680  }
3681  }
3682 #endif
3683 
3684  is_record_exist =
3685  ehash_locate_slot (thread_p, sibling_page_p, dir_header_p->key_type, (void *) bucket_record_p, &new_slot_id);
3686  if (is_record_exist == false)
3687  {
3688  success = spage_insert_at (thread_p, sibling_page_p, new_slot_id, &recdes);
3689  if (success != SP_SUCCESS)
3690  {
3692 #if defined (ENABLE_UNUSED_FUNCTION)
3693  if (recdes.type == REC_BIGONE)
3694  {
3695  free_and_init (bucket_record_p);
3696  }
3697 #endif
3698  return ER_FAILED;
3699  }
3700  }
3701  else
3702  {
3704 #if defined (ENABLE_UNUSED_FUNCTION)
3705  if (recdes.type == REC_BIGONE)
3706  {
3707  free_and_init (bucket_record_p);
3708  }
3709 #endif
3710  return ER_FAILED;
3711  }
3712 
3713 #if defined (ENABLE_UNUSED_FUNCTION)
3714  if (recdes.type == REC_BIGONE)
3715  {
3716  free_and_init (bucket_record_p);
3717  }
3718 #endif
3719  }
3720 
3721  found_depth = ehash_find_depth (thread_p, ehid_p, location, bucket_vpid_p, sibling_vpid_p);
3722  if (found_depth == 0)
3723  {
3724  return ER_FAILED;
3725  }
3726 
3727  /* Update the sibling header */
3728  sibling_bucket_header.local_depth = dir_header_p->depth - found_depth;
3729  *out_new_local_depth_p = sibling_bucket_header.local_depth;
3730 
3731  /* Set the record descriptor to the sib_header */
3732  recdes.data = (char *) &sibling_bucket_header;
3733  recdes.area_size = sizeof (EHASH_BUCKET_HEADER);
3734  recdes.length = sizeof (EHASH_BUCKET_HEADER);
3735  (void) spage_update (thread_p, sibling_page_p, first_slot_id, &recdes);
3736 
3737  pgbuf_set_dirty (thread_p, sibling_page_p, DONT_FREE);
3738 
3739  if (!is_temp)
3740  {
3741  log_append_redo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, sibling_page_p, 0, DB_PAGESIZE, sibling_page_p);
3742  }
3743 
3744  return NO_ERROR;
3745 }
3746 
3747 /*
3748  * ehash_merge () - If possible, merge two buckets
3749  * return:
3750  * ehid(in): identifier for the extendible hashing structure
3751  * key(in): key value that has just been removed; used to locate the bucket.
3752  * is_temp(in): true if temporary (logging is not required)
3753  *
3754  * Note: This function checks to determine if it is possible (and
3755  * desirable) to merge the bucket (that contained the given key
3756  * before the deletion) with its sibling bucket. If so, it starts
3757  * a permanent system operation and performs the merge. And then,
3758  * it checks the size of the directory. If the directory is too
3759  * big for the remaining buckets, it shrinks the directory to
3760  * a more tolerable size before concluding the system permanent operation.
3761  */
3762 static void
3763 ehash_merge (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p, bool is_temp)
3764 {
3765  EHASH_DIR_HEADER *dir_header_p;
3766  PAGE_PTR dir_root_page_p;
3767  VPID bucket_vpid;
3768  PAGE_PTR bucket_page_p;
3769  PAGE_PTR sibling_page_p;
3770  VPID sibling_vpid;
3771  int location;
3772  int old_local_depth, new_local_depth;
3773  EHASH_HASH_KEY hash_key;
3774  int bucket_status;
3775  EHASH_RESULT check_result;
3776  PGSLOTID max_free;
3777  VPID null_vpid = { NULL_VOLID, NULL_PAGEID };
3778  PGSLOTID first_slot_id = -1;
3779  int num_records, sibling_location;
3780 
3781  dir_root_page_p =
3782  ehash_find_bucket_vpid_with_hash (thread_p, ehid_p, key_p, PGBUF_LATCH_WRITE, PGBUF_LATCH_WRITE, &bucket_vpid,
3783  &hash_key, &location);
3784  if (dir_root_page_p == NULL)
3785  {
3786  return;
3787  }
3788  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
3789 
3790  if (bucket_vpid.pageid != NULL_PAGEID)
3791  {
3792  bucket_page_p = ehash_fix_old_page (thread_p, &ehid_p->vfid, &bucket_vpid, PGBUF_LATCH_WRITE);
3793  if (bucket_page_p == NULL)
3794  {
3795  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3796  return;
3797  }
3798 
3799  /* Check the status of bucket to see if merge is still needed */
3800  if (spage_number_of_records (bucket_page_p) <= 1)
3801  {
3802  bucket_status = EHASH_BUCKET_EMPTY;
3803  }
3804  else
3805  {
3806  max_free = spage_max_space_for_new_record (thread_p, bucket_page_p);
3807 
3808  /* Check if the Bucket is underflown, or not */
3809  if ((DB_PAGESIZE - max_free) < EHASH_UNDERFLOW_THRESHOLD)
3810  {
3811  bucket_status = EHASH_BUCKET_UNDERFLOW;
3812  }
3813  else
3814  {
3815  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3816  bucket_status = NO_ERROR;
3817  }
3818  }
3819 
3820  if (bucket_status == EHASH_BUCKET_EMPTY || bucket_status == EHASH_BUCKET_UNDERFLOW)
3821  {
3822  check_result =
3823  ehash_check_merge_possible (thread_p, ehid_p, dir_header_p, &bucket_vpid, bucket_page_p, location, X_LOCK,
3824  &old_local_depth, &sibling_vpid, &sibling_page_p, &first_slot_id, &num_records,
3825  &sibling_location);
3826 
3827  switch (check_result)
3828  {
3830  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3831 
3832  if ((bucket_status == EHASH_BUCKET_EMPTY) && (old_local_depth != 0))
3833  {
3834  if (!is_temp)
3835  {
3836  log_sysop_start (thread_p);
3837  }
3838 
3839  if (file_dealloc (thread_p, &dir_header_p->bucket_file, &bucket_vpid, FILE_EXTENDIBLE_HASH)
3840  != NO_ERROR)
3841  {
3842  ASSERT_ERROR ();
3843  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3844  if (!is_temp)
3845  {
3846  log_sysop_abort (thread_p);
3847  }
3848  return;
3849  }
3850 
3851  /* Set all pointers to the bucket to NULL */
3852  if (ehash_connect_bucket (thread_p, ehid_p, old_local_depth, hash_key, &null_vpid, is_temp) !=
3853  NO_ERROR)
3854  {
3855  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3856  if (!is_temp)
3857  {
3858  log_sysop_abort (thread_p);
3859  }
3860  return;
3861  }
3862 
3863  ehash_adjust_local_depth (thread_p, ehid_p, dir_root_page_p, dir_header_p, old_local_depth, -1,
3864  is_temp);
3865  ehash_shrink_directory_if_need (thread_p, ehid_p, dir_header_p, is_temp);
3866  if (!is_temp)
3867  {
3868  log_sysop_commit (thread_p);
3869  }
3870  }
3871  break;
3872 
3874  /* If the sib_bucket does not have room for the remaining records of buc_page, the record has already
3875  * been deleted, so just release bucket page */
3876  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3877  break;
3878 
3880  /* Perform actual merge operation */
3881  if (!is_temp)
3882  {
3883  log_sysop_start (thread_p);
3884  }
3885 
3886  if (ehash_merge_permanent (thread_p, ehid_p, dir_root_page_p, dir_header_p, bucket_page_p, sibling_page_p,
3887  &bucket_vpid, &sibling_vpid, num_records, sibling_location, first_slot_id,
3888  &new_local_depth, is_temp) != NO_ERROR)
3889  {
3890  pgbuf_unfix_and_init (thread_p, sibling_page_p);
3891  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3892  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3893  if (!is_temp)
3894  {
3895  log_sysop_abort (thread_p);
3896  }
3897  return;
3898  }
3899 
3900  pgbuf_unfix_and_init (thread_p, sibling_page_p);
3901  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3902 
3903  if (file_dealloc (thread_p, &dir_header_p->bucket_file, &bucket_vpid, FILE_EXTENDIBLE_HASH) != NO_ERROR)
3904  {
3905  ASSERT_ERROR ();
3906  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3907  if (!is_temp)
3908  {
3909  log_sysop_abort (thread_p);
3910  }
3911  return;
3912  }
3913 
3914  ehash_adjust_local_depth (thread_p, ehid_p, dir_root_page_p, dir_header_p, old_local_depth, -2, is_temp);
3915  ehash_adjust_local_depth (thread_p, ehid_p, dir_root_page_p, dir_header_p, new_local_depth, 1, is_temp);
3916  ehash_shrink_directory_if_need (thread_p, ehid_p, dir_header_p, is_temp);
3917 
3918  /* Update some directory pointers */
3919  if (ehash_connect_bucket (thread_p, ehid_p, new_local_depth, hash_key, &sibling_vpid, is_temp)
3920  != NO_ERROR)
3921  {
3922  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3923  if (!is_temp)
3924  {
3925  log_sysop_abort (thread_p);
3926  }
3927  return;
3928  }
3929 
3930  if (!is_temp)
3931  {
3932  log_sysop_commit (thread_p);
3933  }
3934  break;
3935 
3936  case EHASH_ERROR_OCCURRED:
3937  /* There happened an error in the merge operation; so return */
3938 
3939  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3940  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3941  return;
3942 
3943  default:
3944  /* An undefined merge result was returned; This should never happen */
3946  pgbuf_unfix_and_init (thread_p, bucket_page_p);
3947  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3948  return;
3949  }
3950  }
3951  }
3952 
3953  /* Release directory and return */
3954  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
3955 }
3956 
3957 /*
3958  * ehash_shrink_directory () - Shrink directory
3959  * return:
3960  * ehid(in): identifier for the extendible hashing structure to shrink
3961  * new_depth(in): requested new depth of directory
3962  * is_temp(in): true if temporary file (logging is not required)
3963  *
3964  * Note: This function shrinks the directory of specified extendible
3965  * hashing structure as many times as necessary to attain
3966  * the requested depth. During this operation, the directory is
3967  * locked with X_LOCK to prevent others from accessing wrong
3968  * information. If the original directory is large enough, some
3969  * pages are deallocated from the new directory.
3970  * The remaining portion of the directory is logged and updated
3971  * before the extra part is deallocated.
3972  */
3973 static void
3974 ehash_shrink_directory (THREAD_ENTRY * thread_p, EHID * ehid_p, int new_depth, bool is_temp)
3975 {
3976  int old_pages;
3977  int old_ptrs;
3978  int new_pages;
3979  int new_ptrs;
3980 
3981  EHASH_DIR_HEADER *dir_header_p;
3982  int end_offset;
3983  int new_end_offset;
3984  int check_pages;
3985  int i;
3986  int times;
3987 
3988  PAGE_PTR old_dir_page_p;
3989  int old_dir_offset;
3990  int old_dir_nth_page;
3991  int prev_pg_no;
3992 
3993  PAGE_PTR new_dir_page_p;
3994  PGLENGTH new_dir_offset;
3995  int new_dir_nth_page;
3996  int ret = NO_ERROR;
3997 
3998  dir_header_p = (EHASH_DIR_HEADER *) ehash_fix_nth_page (thread_p, &ehid_p->vfid, 0, PGBUF_LATCH_WRITE);
3999  if (dir_header_p == NULL)
4000  {
4001  return;
4002  }
4003 
4004  if (dir_header_p->depth < new_depth)
4005  {
4006 #ifdef EHASH_DEBUG
4007  er_log_debug (ARG_FILE_LINE, "WARNING in eh_shrink_dir:The directory has a depth of %d , shrink is cancelled ",
4008  dir_header_p->depth);
4009 #endif
4010  pgbuf_unfix (thread_p, (PAGE_PTR) dir_header_p);
4011  return;
4012  }
4013 
4014  old_ptrs = 1 << dir_header_p->depth;
4015  times = 1 << (dir_header_p->depth - new_depth);
4016  ret = file_get_num_user_pages (thread_p, &ehid_p->vfid, &old_pages);
4017  if (ret != NO_ERROR)
4018  {
4019 #ifdef EHASH_DEBUG
4020  er_log_debug (ARG_FILE_LINE, "WARNING: error reading user page number from file, shrink is cancelled.\n");
4021 #endif
4022  ASSERT_ERROR ();
4023  pgbuf_unfix (thread_p, (PAGE_PTR) dir_header_p);
4024  return;
4025  }
4026  old_pages -= 1; /* The first page starts with 0 */
4027 
4028  new_ptrs = old_ptrs / times; /* Calculate how many pointers will remain */
4029 
4030  end_offset = old_ptrs - 1; /* Directory first pointer has an offset of 0 */
4031  ehash_dir_locate (&check_pages, &end_offset);
4032 #ifdef EHASH_DEBUG
4033  if (check_pages != old_pages)
4034  {
4036  ehid_p->pageid);
4037  return;
4038  }
4039 #endif
4040 
4041  /* Perform shrink */
4042 
4043  prev_pg_no = 0;
4044  old_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, prev_pg_no, PGBUF_LATCH_WRITE);
4045  if (old_dir_page_p == NULL)
4046  {
4047  return;
4048  }
4049 
4050  new_dir_nth_page = 0;
4051  new_dir_page_p = (PAGE_PTR) dir_header_p;
4052  new_dir_offset = EHASH_DIR_HEADER_SIZE;
4053 
4054  if (!is_temp)
4055  {
4056  log_append_undo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE, new_dir_page_p);
4057  }
4058 
4059  dir_header_p->depth = new_depth; /* Decrease the directory global depth */
4060 
4061  /* Copy old directory records to new (shrunk) area */
4062  for (i = 1; i < new_ptrs; i++)
4063  {
4064  /* Location 0 will not change */
4065 
4066  /* Locate the next source pointer */
4067  old_dir_offset = i * times;
4068  ehash_dir_locate (&old_dir_nth_page, &old_dir_offset);
4069 
4070  if (old_dir_nth_page != prev_pg_no)
4071  {
4072  /* We reached the end of the source page. The next bucket pointer is in the next Dir page */
4073 
4074  prev_pg_no = old_dir_nth_page;
4075  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
4076 
4077  /* set olddir_pgptr to the new pgptr */
4078  old_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, old_dir_nth_page, PGBUF_LATCH_WRITE);
4079  if (old_dir_page_p == NULL)
4080  {
4081  pgbuf_set_dirty (thread_p, new_dir_page_p, FREE);
4082  return;
4083  }
4084  }
4085 
4086  /* Advance the destination pointer to new spot */
4087  new_dir_offset += sizeof (EHASH_DIR_RECORD);
4088 
4089  if ((DB_PAGESIZE - new_dir_offset) < SSIZEOF (EHASH_DIR_RECORD))
4090  {
4091  /* There is no more place in the directory page for new bucket pointers */
4092 
4093  if (!is_temp)
4094  {
4095  /* Log this updated directory page */
4096  log_append_redo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE,
4097  new_dir_page_p);
4098  }
4099 
4100  pgbuf_set_dirty (thread_p, new_dir_page_p, FREE);
4101 
4102  /* get another page */
4103  new_dir_nth_page++;
4104 
4105  /* set newdir_pgptr to the new pgptr */
4106  new_dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, new_dir_nth_page, PGBUF_LATCH_WRITE);
4107  if (new_dir_page_p == NULL)
4108  {
4109  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
4110  return;
4111  }
4112  new_dir_offset = 0;
4113 
4114  if (!is_temp)
4115  {
4116  /* Log the initial content of this directory page */
4117  log_append_undo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE,
4118  new_dir_page_p);
4119  }
4120  }
4121 
4122  /* Perform the copy operation */
4123  memcpy ((char *) new_dir_page_p + new_dir_offset, (char *) old_dir_page_p + old_dir_offset,
4124  sizeof (EHASH_DIR_RECORD));
4125  }
4126 
4127  if (!is_temp)
4128  {
4129  /* Log this updated directory page */
4130  log_append_redo_data2 (thread_p, RVEH_REPLACE, &ehid_p->vfid, new_dir_page_p, 0, DB_PAGESIZE, new_dir_page_p);
4131  }
4132 
4133  /* Release the source and destination pages */
4134  pgbuf_unfix_and_init (thread_p, old_dir_page_p);
4135  pgbuf_set_dirty (thread_p, new_dir_page_p, FREE);
4136 
4137  /* remove unwanted part of directory. */
4138  new_end_offset = new_ptrs - 1;
4139  ehash_dir_locate (&new_pages, &new_end_offset);
4140  ret = file_numerable_truncate (thread_p, &ehid_p->vfid, new_pages + 1);
4141  if (ret != NO_ERROR)
4142  {
4143  ASSERT_ERROR ();
4144  }
4145 }
4146 
4147 static EHASH_HASH_KEY
4148 ehash_hash_string_type (char *key_p, char *original_key_p)
4149 {
4150  EHASH_HASH_KEY hash_key = 0;
4151  char copy_psekey[12];
4152  int i;
4153  int length;
4154  int times;
4155  int remaining;
4156  int Int;
4157  char Char;
4158  int byte;
4159  char *p = NULL;
4160  char *new_key_p = NULL;
4161 
4162  length = (int) strlen (key_p);
4163 
4164  if (length > 0)
4165  {
4166  /* Eliminate any trailing space characters */
4167  if (char_isspace (*(char *) (key_p + length - 1)))
4168  {
4169  for (p = key_p + length - 1; char_isspace (*p) && (p > key_p); p--)
4170  {
4171  ;
4172  }
4173  length = (int) (p - key_p + 1);
4174  key_p = new_key_p = (char *) malloc (length + 1); /* + 1 is for \0 */
4175  if (key_p == NULL)
4176  {
4177  return 0;
4178  }
4179  memcpy (key_p, original_key_p, length);
4180  *(char *) (key_p + length) = '\0';
4181  }
4182 
4183  /* Takes the floor of division */
4184  times = length / sizeof (EHASH_HASH_KEY);
4185 
4186  /* Apply Folding method */
4187  for (i = 1; i <= times; i++)
4188  {
4189  memcpy (&Int, key_p, sizeof (int));
4190  hash_key += Int; /* Add next word of original key */
4191 
4192  key_p += sizeof (EHASH_HASH_KEY);
4193  }
4194 
4195  remaining = length - times * sizeof (EHASH_HASH_KEY);
4196 
4197  /* Go over the remaining chars of key */
4198  for (i = 1; i <= remaining; i++)
4199  {
4200  /* shift left to align with previous content */
4201  Int = (int) *key_p++ << i;
4202  hash_key += Int;
4203  }
4204 
4205  if (new_key_p)
4206  {
4207  free_and_init (new_key_p);
4208  }
4209  }
4210 
4211  /* Copy the hash_key to an aux. area, to be further hashed into individual bytes */
4212  memcpy (&copy_psekey, &hash_key, sizeof (EHASH_HASH_KEY));
4213  copy_psekey[sizeof (EHASH_HASH_KEY)] = '\0';
4214 
4215  hash_key = 0;
4216  byte = mht_1strhash (copy_psekey, 509);
4217  hash_key = hash_key + (byte << (8 * (sizeof (EHASH_HASH_KEY) - 1)));
4218  byte = mht_2strhash (copy_psekey, 509);
4219  hash_key = hash_key + (byte << (8 * (sizeof (EHASH_HASH_KEY) - 2)));
4220  byte = mht_3strhash (copy_psekey, 509);
4221  hash_key = hash_key + (byte << (8 * (sizeof (EHASH_HASH_KEY) - 3)));
4222 
4223  /* Go over the chars of the given pseudo key */
4224  Char = '\0';
4225  key_p = (char *) &copy_psekey;
4226  for (i = 0; (unsigned int) i < sizeof (EHASH_HASH_KEY); i++)
4227  {
4228  Char += (char) *key_p++;
4229  }
4230 
4231  /* Change the first byte of the pseudo key to the SUM of all of them */
4232  return hash_key + Char;
4233 }
4234 
4235 static EHASH_HASH_KEY
4237 {
4238  EHASH_HASH_KEY hash_key = 0;
4239  unsigned int i;
4240  int Int;
4241  char Char;
4242 
4243  for (i = 0; i < sizeof (double) / sizeof (int); i++)
4244  {
4245  memcpy (&Int, key_p, sizeof (int));
4246  hash_key += htonl (Int); /* Add next word of original key */
4247  key_p += sizeof (int);
4248  }
4249 
4250  /* Go over the chars of the given pseudo key */
4251  Char = '\0';
4252  key_p = (char *) &hash_key;
4253  for (i = 0; i < sizeof (EHASH_HASH_KEY); i++)
4254  {
4255  Char ^= (char) *key_p++;
4256  }
4257 
4258  /* Change the first byte of the pseudo key to the summation of all of them */
4259  memcpy (&hash_key, &Char, sizeof (char));
4260 
4261  return hash_key;
4262 }
4263 
4264 #if defined (ENABLE_UNUSED_FUNCTION)
4265 static EHASH_HASH_KEY
4266 ehash_hash_four_bytes_type (char *key_p)
4267 {
4268  EHASH_HASH_KEY hash_key = 0;
4269  unsigned int i;
4270  unsigned short Short, *Short2;
4271  char Char;
4272 
4273  Short = ntohs (*(unsigned short *) key_p); /* Get the left half of the int */
4274  Short2 = (unsigned short *) key_p;
4275  Short2++;
4276 
4277  Short += ntohs (*Short2);
4278  hash_key = (EHASH_HASH_KEY) (Short << EHASH_SHORT_BITS);
4279  hash_key += (EHASH_HASH_KEY) Short;
4280 
4281  /* Go over the chars of the given pseudo key */
4282  Char = '\0';
4283  key_p = (char *) &hash_key;
4284  for (i = 0; i < sizeof (EHASH_HASH_KEY); i++)
4285  {
4286  Char += (char) *key_p++;
4287  }
4288 
4289  /* Change the first byte of the pseudo key to the SUM of all of them */
4290  memcpy (&hash_key, &Char, sizeof (char));
4291 
4292  return hash_key;
4293 }
4294 
4295 static EHASH_HASH_KEY
4296 ehash_hash_two_bytes_type (char *key_p)
4297 {
4298  EHASH_HASH_KEY hash_key = 0;
4299  unsigned int i;
4300  unsigned short Short;
4301  char Char;
4302 
4303  Short = ntohs (*(unsigned short *) key_p);
4304  hash_key = (EHASH_HASH_KEY) (Short << EHASH_SHORT_BITS);
4305  hash_key += (EHASH_HASH_KEY) Short;
4306 
4307  /* Go over the chars of the given pseudo key */
4308  Char = '\0';
4309  key_p = (char *) &hash_key;
4310  for (i = 0; i < sizeof (EHASH_HASH_KEY); i++)
4311  {
4312  Char += (char) *key_p++;
4313  }
4314 
4315  /* Change the first byte of the pseudo key to the summation of all of them */
4316  memcpy (&hash_key, &Char, sizeof (char));
4317 
4318  return hash_key;
4319 }
4320 #endif
4321 
4322 /*
4323  * ehash_hash () - Hash function
4324  * return: EHASH_HASH_KEY
4325  * orig_key(in): original key to encode into a pseudo key
4326  * key_type(in): type of the key
4327  *
4328  * Note: This function converts the given original key into a pseudo
4329  * key. Since the original key is presented as a character
4330  * string, its conversion into a int-compatible type is essential
4331  * prior to performing any operation on it.
4332  * Depending on the "key_type", the original key might be
4333  * folded (for DB_TYPE_STRING, DB_TYPE_OBJECT and DB_TYPE_DOUBLE) or duplicated
4334  * (for DB_TYPE_SHORT) into a computer word.
4335  * This function does not change the value of parameter
4336  * orig_key, as it might be on a bucket.
4337  */
4338 static EHASH_HASH_KEY
4339 ehash_hash (void *original_key_p, DB_TYPE key_type)
4340 {
4341  char *key = (char *) original_key_p;
4342  EHASH_HASH_KEY hash_key = 0;
4343 
4344  switch (key_type)
4345  {
4346  case DB_TYPE_STRING:
4347  hash_key = ehash_hash_string_type (key, (char *) original_key_p);
4348  break;
4349 
4350  case DB_TYPE_OBJECT:
4351  hash_key = ehash_hash_eight_bytes_type (key);
4352  break;
4353 
4354 #if defined (ENABLE_UNUSED_FUNCTION)
4355  case DB_TYPE_BIGINT:
4356  case DB_TYPE_DOUBLE:
4357  case DB_TYPE_MONETARY:
4358  if (key_type == DB_TYPE_MONETARY)
4359  {
4360  key = (char *) &(((DB_MONETARY *) original_key_p)->amount);
4361  }
4362 
4363  hash_key = ehash_hash_eight_bytes_type (key);
4364  break;
4365 
4366  case DB_TYPE_FLOAT:
4367  case DB_TYPE_DATE:
4368  case DB_TYPE_TIME:
4369  case DB_TYPE_TIMESTAMP:
4370  case DB_TYPE_DATETIME:
4371  case DB_TYPE_INTEGER:
4372  hash_key = ehash_hash_four_bytes_type (key);
4373  break;
4374 
4375  case DB_TYPE_SHORT:
4376  hash_key = ehash_hash_two_bytes_type (key);
4377  break;
4378 #endif
4379  default:
4381  }
4382 
4383  return hash_key;
4384 }
4385 
4386 /* TODO: check not use */
4387 #if 0
4388 /*
4389  * Utility functions
4390  */
4391 
4392 /*
4393  * eh_size () - Find how many pages are used
4394  * return: int
4395  * ehid(in): identifier of the extendible hashing structure
4396  * num_bucket_pages(in):
4397  * num_dir_pages(in):
4398  * Note: Returns the total number of pages allocated for the directory
4399  * bucket and overflow pages of the specified extendible hashing
4400  * structure. If an error occurs in finding this information, -1 is returned.
4401  */
4402 int
4403 eh_size (EHID * ehid, int *num_bucket_pages, int *num_dir_pages)
4404 {
4405  EHASH_DIR_HEADER *dir_header;
4406  PAGE_PTR dir_pgptr;
4407 
4408  if (ehid == NULL)
4409  {
4410  return 0;
4411  }
4412 
4413  /* Access root page of the directory */
4414  dir_pgptr = ehash_fix_ehid_page (ehid, PGBUF_LATCH_READ);
4415  if (dir_pgptr == NULL)
4416  {
4417  *num_bucket_pages = -1;
4418  *num_dir_pages = -1;
4419  return -1;
4420  }
4421 
4422  dir_header = (EHASH_DIR_HEADER *) dir_pgptr;
4423 
4424  *num_bucket_pages = file_get_numpages (&dir_header->bucket_file);
4425  *num_dir_pages = file_get_numpages (&ehid->vfid);
4426 
4427  /* If there is an overflow file then also consider its pages */
4428  if (dir_header->overflow_file.fileid != NULL_FILEID)
4429  {
4430  *num_bucket_pages += file_get_numpages (&dir_header->overflow_file);
4431  }
4432 
4433  pgbuf_unfix_and_init (thread_p, dir_pgptr);
4434 
4435  return (*num_bucket_pages + *num_dir_pages);
4436 }
4437 
4438 /*
4439  * eh_count () - Find how many entries are kept in the ext. hashing structure
4440  * return: int
4441  * ehid(in): identifier of the extendible hashing structure
4442  *
4443  * Note: Find outs and returns the total number of entries (i.e., the
4444  * key and assoc_value pairs) stored in the specified extendible
4445  * hashing structure. If an error occurs in finding this
4446  * information, -1 is returned.
4447  *
4448  * Note that the total number of entries of an extendible hashing
4449  * structure is not a stored piece of information. (Keeping a
4450  * counter for this purpose would put an extra overhead to
4451  * insertion and deletion operations). Instead, it is calculated
4452  * by this function whenever it is desired. It is calculated by
4453  * fetching all of the bucket pages of the extendible hashing
4454  * structure and adding up the number of entries existing in each
4455  * bucket. Consequently, this function is quite expensive, and it
4456  * should not be called very often.
4457  */
4458 int
4459 eh_count (EHID * ehid)
4460 {
4461  int num_entries; /* Total number of entries contained in the extendible hashing structure; will be the
4462  * return value */
4463  int numpages; /* Number of bucket pages */
4464 
4465  /* Local variables for the directory */
4466  EHASH_DIR_HEADER *dir_header;
4467  PAGE_PTR dir_pgptr;
4468 
4469  /* Local variables for the buckets */
4470  PAGE_PTR buc_pgptr;
4471  int buc_pg_no;
4472 
4473  if (ehid == NULL)
4474  {
4475  return 0;
4476  }
4477 
4478  /* Access root page of the directory */
4479  dir_pgptr = ehash_fix_ehid_page (ehid, PGBUF_LATCH_READ);
4480  if (dir_pgptr == NULL)
4481  {
4482  return -1;
4483  }
4484  dir_header = (EHASH_DIR_HEADER *) dir_pgptr;
4485 
4486  numpages = file_get_numpages (&dir_header->bucket_file);
4487  num_entries = 0;
4488 
4489  for (buc_pg_no = 0; buc_pg_no < numpages; buc_pg_no++)
4490  {
4491  /* set buc_pgptr to the next bucket page */
4492 
4493  buc_pgptr = ehhash_fix_nth_page (&dir_header->bucket_file, buc_pg_no, PGBUF_LATCH_READ);
4494  if (buc_pgptr == NULL)
4495  {
4496  pgbuf_unfix_and_init (thread_p, dir_pgptr);
4497  return -1;
4498  }
4499 
4500  /* Add the number of entries kept in this bucket page to the global sum */
4501 
4502  num_entries += spage_number_of_records (buc_pgptr) - 1; /* Minus 1 is for the first record (page header) */
4503  pgbuf_unfix_and_init (thread_p, buc_pgptr);
4504  }
4505 
4506  pgbuf_unfix_and_init (thread_p, dir_pgptr);
4507 
4508  return num_entries;
4509 }
4510 
4511 /*
4512  * eh_depth () - Find depth of extendible hash structure
4513  * return: int
4514  * ehid(in): identifier of the extendible hashing structure
4515  *
4516  * Note: Find outs the depth of the extendible hash structure.
4517  */
4518 int
4519 eh_depth (EHID * ehid)
4520 {
4521  int depth;
4522  PAGE_PTR dir_pgptr;
4523  EHASH_DIR_HEADER *dir_header;
4524 
4525  if (ehid == NULL)
4526  {
4527  return -1;
4528  }
4529 
4530  /* Access root page of the directory */
4531  dir_pgptr = ehash_fix_ehid_page (ehid, PGBUF_LATCH_READ);
4532  if (dir_pgptr == NULL)
4533  {
4534  return -1;
4535  }
4536 
4537  dir_header = (EHASH_DIR_HEADER *) dir_pgptr;
4538  depth = dir_header->depth;
4539 
4540  pgbuf_unfix_and_init (thread_p, dir_pgptr);
4541  return depth;
4542 }
4543 
4544 /*
4545  * eh_capacity () - Find storage capacity of given extendible hash
4546  * return: NO_ERROR, or ER_FAILED
4547  * ehid(in): identifier of the extendible hashing structure
4548  * num_recs(out): Number of records
4549  * avg_reclength(out): Avergage length of extendible hash records
4550  * num_bucket_pages(out): Number of leaf/bucket pages
4551  * num_dir_pages(out): Number of directory pages
4552  * dir_depth(out): Depth of directory
4553  * avg_freespace_per_page(out): Average free space per page
4554  * avg_overhead_per_page(out): Average overhead in each page
4555  *
4556  * Note: Find the current storage facts/capacity for given extendible hash.
4557  */
4558 int
4559 eh_capacity (THREAD_ENTRY * thread_p, EHID * ehid, int *num_recs, int *avg_reclength, int *num_bucket_pages,
4560  int *num_dir_pages, int *dir_depth, int *avg_freespace_per_page, int *avg_overhead_per_page)
4561 {
4562  EHASH_DIR_HEADER *dir_header;
4563  PAGE_PTR dir_pgptr = NULL;
4564  PAGE_PTR buc_pgptr = NULL;
4565  int buc_pg_no;
4566  int num_slots;
4567  PGSLOTID slotid;
4568 
4569  *num_recs = 0;
4570  *num_bucket_pages = 0;
4571  *num_dir_pages = 0;
4572  *dir_depth = 0;
4573  *avg_freespace_per_page = 0;
4574  *avg_reclength = 0;
4575  *avg_overhead_per_page = 0;
4576 
4577  if (ehid == NULL)
4578  {
4579  return ER_FAILED;
4580  }
4581 
4582  dir_pgptr = ehash_fix_ehid_page (ehid, PGBUF_LATCH_READ);
4583  if (dir_pgptr == NULL)
4584  {
4585  return ER_FAILED;
4586  }
4587 
4588  dir_header = (EHASH_DIR_HEADER *) dir_pgptr;
4589  *num_dir_pages = file_get_numpages (&ehid->vfid);
4590  *dir_depth = dir_header->depth;
4591 
4592  *num_bucket_pages = file_get_numpages (&dir_header->bucket_file);
4593  if (*num_bucket_pages == -1)
4594  {
4595  goto exit_on_error;
4596  }
4597 
4598  for (buc_pg_no = 0; buc_pg_no < *num_bucket_pages; buc_pg_no++)
4599  {
4600  buc_pgptr = ehash_fix_nth_page (thread_p, &dir_header->bucket_file, buc_pg_no, PGBUF_LATCH_READ);
4601  if (buc_pgptr == NULL)
4602  {
4603  goto exit_on_error;
4604  }
4605 
4606  num_slots = spage_number_of_records (buc_pgptr);
4607  *avg_freespace_per_page += spage_get_free_space (buc_pgptr);
4608  *avg_overhead_per_page += num_slots * spage_slot_size ();
4609 
4610  for (slotid = 0; slotid < num_slots; slotid++)
4611  {
4612  *num_recs += 1;
4613  if (slotid != 0)
4614  {
4615  *avg_reclength += spage_get_record_length (thread_p, buc_pgptr, slotid);
4616  }
4617  else
4618  {
4619  *avg_overhead_per_page += spage_get_record_length (thread_p, buc_pgptr, slotid);
4620  }
4621  }
4622  pgbuf_unfix_and_init (thread_p, buc_pgptr);
4623  }
4624 
4625  pgbuf_unfix_and_init (thread_p, dir_pgptr);
4626  return NO_ERROR;
4627 
4628 exit_on_error:
4629  if (dir_pgptr != NULL)
4630  {
4631  pgbuf_unfix_and_init (thread_p, dir_pgptr);
4632  }
4633 
4634  if (buc_pgptr != NULL)
4635  {
4636  pgbuf_unfix_and_init (thread_p, buc_pgptr);
4637  }
4638 
4639  *num_recs = -1;
4640  *num_bucket_pages = -1;
4641  *num_dir_pages = -1;
4642  *dir_depth = -1;
4643  *avg_freespace_per_page = -1;
4644  *avg_reclength = -1;
4645  *avg_overhead_per_page = -1;
4646 
4647  return ER_FAILED;
4648 }
4649 #endif
4650 
4651 static int
4652 ehash_apply_each (THREAD_ENTRY * thread_p, EHID * ehid_p, RECDES * recdes_p, DB_TYPE key_type, char *bucket_record_p,
4653  OID * assoc_value_p, int *out_apply_error, int (*apply_function) (THREAD_ENTRY * thread_p, void *key,
4654  void *data, void *args), void *args)
4655 {
4656 #if defined (ENABLE_UNUSED_FUNCTION)
4657  char *long_str;
4658 #endif
4659  char *str_next_key_p, *temp_p;
4660  int key_size;
4661  char next_key[sizeof (DB_MONETARY)];
4662  int i;
4663  void *key_p = &next_key;
4664 
4665  switch (key_type)
4666  {
4667  case DB_TYPE_STRING:
4668 #if defined (ENABLE_UNUSED_FUNCTION)
4669  if (recdes_p->type == REC_BIGONE)
4670  {
4671  /* This is a long string */
4672  long_str = ehash_compose_overflow (thread_p, recdes_p);
4673  if (long_str == NULL)
4674  {
4675  *out_apply_error = ER_FAILED;
4676  return NO_ERROR;
4677  }
4678  key_p = long_str;
4679  }
4680  else
4681 #endif
4682  {
4683  /* Short String */
4684  key_size = recdes_p->length - CAST_BUFLEN (bucket_record_p - recdes_p->data);
4685 
4686  str_next_key_p = (char *) malloc (key_size);
4687  if (str_next_key_p == NULL)
4688  {
4690  return ER_OUT_OF_VIRTUAL_MEMORY;
4691  }
4692 
4693  temp_p = str_next_key_p;
4694  for (i = 0; i < key_size; i++)
4695  {
4696  *temp_p++ = *bucket_record_p++;
4697  }
4698 
4699  key_p = str_next_key_p;
4700  }
4701 
4702  break;
4703 
4704  case DB_TYPE_OBJECT:
4705  memcpy (&next_key, bucket_record_p, sizeof (OID));
4706  break;
4707 #if defined (ENABLE_UNUSED_FUNCTION)
4708  case DB_TYPE_DOUBLE:
4709  OR_MOVE_DOUBLE (bucket_record_p, &next_key);
4710  break;
4711 
4712  case DB_TYPE_FLOAT:
4713  *((float *) &next_key) = *(float *) bucket_record_p;
4714  break;
4715 
4716  case DB_TYPE_INTEGER:
4717  *((int *) &next_key) = *(int *) bucket_record_p;
4718  break;
4719 
4720  case DB_TYPE_BIGINT:
4721  *((DB_BIGINT *) (&next_key)) = *(DB_BIGINT *) bucket_record_p;
4722  break;
4723 
4724  case DB_TYPE_SHORT:
4725  *((short *) &next_key) = *(short *) bucket_record_p;
4726  break;
4727 
4728  case DB_TYPE_DATE:
4729  *((DB_DATE *) (&next_key)) = *(DB_DATE *) bucket_record_p;
4730  break;
4731 
4732  case DB_TYPE_TIME:
4733  *((DB_TIME *) (&next_key)) = *(DB_TIME *) bucket_record_p;
4734  break;
4735 
4736  case DB_TYPE_TIMESTAMP:
4737  *((DB_UTIME *) (&next_key)) = *(DB_UTIME *) bucket_record_p;
4738  break;
4739 
4740  case DB_TYPE_DATETIME:
4741  *((DB_DATETIME *) (&next_key)) = *(DB_DATETIME *) bucket_record_p;
4742  break;
4743 
4744  case DB_TYPE_MONETARY:
4745  OR_MOVE_DOUBLE (&((DB_MONETARY *) bucket_record_p)->amount, &((DB_MONETARY *) (&next_key))->amount);
4746  ((DB_MONETARY *) (&next_key))->type = ((DB_MONETARY *) bucket_record_p)->type;
4747  break;
4748 #endif
4749  default:
4750  /* Unspecified key type: Directory header has been corrupted */
4752  ehid_p->pageid);
4753  return ER_EH_ROOT_CORRUPTED;
4754  }
4755 
4756  *out_apply_error = (*apply_function) (thread_p, key_p, assoc_value_p, args);
4757 
4758  if (key_type == DB_TYPE_STRING)
4759  {
4760  free_and_init (key_p);
4761  }
4762 
4763  return NO_ERROR;
4764 }
4765 
4766 /*
4767  * ehash_map () - Apply function to all entries
4768  * return: int NO_ERROR, or ER_FAILED
4769  * ehid(in): identifier of the extendible hashing structure
4770  * fun(in): function to call on key, data, and arguments
4771  * args(in):
4772  *
4773  */
4774 int
4775 ehash_map (THREAD_ENTRY * thread_p, EHID * ehid_p,
4776  int (*apply_function) (THREAD_ENTRY * thread_p, void *key, void *data, void *args), void *args)
4777 {
4778  EHASH_DIR_HEADER *dir_header_p;
4779  int num_pages, bucket_page_no, num_records;
4780  PAGE_PTR dir_page_p, bucket_page_p = NULL;
4781  char *bucket_record_p;
4782  RECDES recdes;
4783  PGSLOTID slot_id, first_slot_id = NULL_SLOTID;
4784  OID assoc_value;
4785  int apply_error_code = NO_ERROR;
4786 
4787  if (ehid_p == NULL)
4788  {
4789  return ER_FAILED;
4790  }
4791 
4792  dir_page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_READ);
4793  if (dir_page_p == NULL)
4794  {
4795  return ER_FAILED;
4796  }
4797 
4798  dir_header_p = (EHASH_DIR_HEADER *) dir_page_p;
4799  if (file_get_num_user_pages (thread_p, &dir_header_p->bucket_file, &num_pages) != NO_ERROR)
4800  {
4801  return ER_FAILED;
4802  }
4803 
4804  /* Retrieve each bucket page and apply the given function to its entries */
4805  for (bucket_page_no = 0; apply_error_code == NO_ERROR && bucket_page_no < num_pages; bucket_page_no++)
4806  {
4807  first_slot_id = NULL_SLOTID;
4808  bucket_page_p = ehash_fix_nth_page (thread_p, &dir_header_p->bucket_file, bucket_page_no, PGBUF_LATCH_READ);
4809  if (bucket_page_p == NULL)
4810  {
4811  pgbuf_unfix_and_init (thread_p, dir_page_p);
4812  return ER_FAILED;
4813  }
4814 
4815  num_records = spage_number_of_records (bucket_page_p);
4816 
4817  (void) spage_next_record (bucket_page_p, &first_slot_id, &recdes, PEEK);
4818 
4819  /* Skip the first slot since it contains the bucket header */
4820  for (slot_id = first_slot_id + 1; apply_error_code == NO_ERROR && slot_id < num_records; slot_id++)
4821  {
4822  (void) spage_get_record (thread_p, bucket_page_p, slot_id, &recdes, PEEK);
4823  bucket_record_p = (char *) recdes.data;
4824  bucket_record_p = ehash_read_oid_from_record (bucket_record_p, &assoc_value);
4825 
4826  if (ehash_apply_each (thread_p, ehid_p, &recdes, dir_header_p->key_type, bucket_record_p, &assoc_value,
4827  &apply_error_code, apply_function, args) != NO_ERROR)
4828  {
4829  pgbuf_unfix_and_init (thread_p, bucket_page_p);
4830  pgbuf_unfix_and_init (thread_p, dir_page_p);
4831  return ER_FAILED;
4832  }
4833  }
4834  pgbuf_unfix_and_init (thread_p, bucket_page_p);
4835  }
4836 
4837  pgbuf_unfix_and_init (thread_p, dir_page_p);
4838 
4839  return apply_error_code;
4840 }
4841 
4842 /*
4843  * Debugging functions
4844  */
4845 
4846 /*
4847  * ehash_dump () - Dump directory & all buckets
4848  * return:
4849  * ehid(in): identifier for the extendible hashing structure to dump
4850  *
4851  * Note: A debugging function. Dumps the contents of the directory
4852  * and the buckets of specified ext. hashing structure.
4853  */
4854 void
4855 ehash_dump (THREAD_ENTRY * thread_p, EHID * ehid_p)
4856 {
4857  EHASH_DIR_HEADER *dir_header_p;
4858  EHASH_DIR_RECORD *dir_record_p;
4859  int num_pages;
4860  int num_ptrs;
4861 
4862  int check_pages;
4863  int end_offset;
4864  int i;
4865 
4866  PAGE_PTR dir_page_p, dir_root_page_p;
4867  PGLENGTH dir_offset;
4868  int dir_page_no;
4869  int dir_ptr_no;
4870 
4871  PAGE_PTR bucket_page_p;
4872  int bucket_page_no;
4873 
4874  if (ehid_p == NULL)
4875  {
4876  return;
4877  }
4878 
4879  dir_root_page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_READ);
4880  if (dir_root_page_p == NULL)
4881  {
4882  return;
4883  }
4884 
4885  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
4886 
4887  if (file_get_num_user_pages (thread_p, &ehid_p->vfid, &num_pages) != NO_ERROR)
4888  {
4889  ASSERT_ERROR ();
4890  return;
4891  }
4892  num_pages -= 1; /* The first page starts with 0 */
4893 
4894  num_ptrs = 1 << dir_header_p->depth;
4895  end_offset = num_ptrs - 1; /* Directory first pointer has an offset of 0 */
4896  ehash_dir_locate (&check_pages, &end_offset);
4897 
4898 #ifdef EHASH_DEBUG
4899  if (check_pages != num_pages)
4900  {
4901  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
4903  ehid_p->pageid);
4904  return;
4905  }
4906 #endif
4907 
4908 
4909  printf ("*********************************************************\n");
4910  printf ("* DIRECTORY *\n");
4911  printf ("* *\n");
4912  printf ("* Depth : %d *\n", dir_header_p->depth);
4913  printf ("* Key type : ");
4914 
4915  switch (dir_header_p->key_type)
4916  {
4917  case DB_TYPE_STRING:
4918  printf (" string *\n");
4919  break;
4920 
4921  case DB_TYPE_OBJECT:
4922  printf (" OID *\n");
4923  break;
4924 #if defined (ENABLE_UNUSED_FUNCTION)
4925  case DB_TYPE_DOUBLE:
4926  printf (" double *\n");
4927  break;
4928 
4929  case DB_TYPE_FLOAT:
4930  printf (" float *\n");
4931  break;
4932 
4933  case DB_TYPE_INTEGER:
4934  printf (" int *\n");
4935  break;
4936 
4937  case DB_TYPE_BIGINT:
4938  printf (" BIGINT *\n");
4939  break;
4940 
4941  case DB_TYPE_SHORT:
4942  printf (" short *\n");
4943  break;
4944 
4945  case DB_TYPE_DATE:
4946  printf (" date *\n");
4947  break;
4948 
4949  case DB_TYPE_TIME:
4950  printf (" time *\n");
4951  break;
4952 
4953  case DB_TYPE_TIMESTAMP:
4954  printf (" utime *\n");
4955  break;
4956 
4957  case DB_TYPE_DATETIME:
4958  printf (" datetime *\n");
4959  break;
4960 
4961  case DB_TYPE_MONETARY:
4962  printf (" monetary *\n");
4963  break;
4964 #endif
4965  default:
4966  /* Unspecified key type: Directory header has been corrupted */
4968  ehid_p->pageid);
4969  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
4970  return;
4971  }
4972 
4973  printf ("* Key size : %d *\n", ehash_get_key_size (dir_header_p->key_type));
4974  printf ("* *\n");
4975  printf ("* LOCAL DEPTH COUNTERS *\n");
4976  for (i = 0; (unsigned int) i <= EHASH_HASH_KEY_BITS; i++)
4977  {
4978  if (dir_header_p->local_depth_count[i] != 0)
4979  {
4980  fprintf (stdout, "* There are %d ", dir_header_p->local_depth_count[i]);
4981  fprintf (stdout, " buckets with local depth %d *\n", i);
4982  }
4983  }
4984 
4985  printf ("* *\n");
4986  printf ("* POINTERS *\n");
4987  printf ("* *\n");
4988 
4989  /* Print directory */
4990 
4991  dir_offset = EHASH_DIR_HEADER_SIZE;
4992  dir_page_no = 0;
4993  dir_ptr_no = 0;
4994 
4995  dir_page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_READ);
4996  if (dir_page_p == NULL)
4997  {
4998  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
4999  return;
5000  }
5001 
5002  for (i = 0; i < num_ptrs; i++)
5003  {
5004  if (DB_PAGESIZE - dir_offset < SSIZEOF (EHASH_DIR_RECORD))
5005  {
5006  /* We reached the end of the directory page. The next bucket pointer is in the next directory page. */
5007 
5008  /* Release previous page, and unlock it */
5009  pgbuf_unfix_and_init (thread_p, dir_page_p);
5010 
5011  dir_page_no++;
5012 
5013  /* Get another page */
5014  dir_page_p = ehash_fix_nth_page (thread_p, &ehid_p->vfid, dir_page_no, PGBUF_LATCH_READ);
5015  if (dir_page_p == NULL)
5016  {
5017  return;
5018  }
5019 
5020  dir_offset = 0;
5021  }
5022 
5023  /* Print out the next directory record */
5024  dir_record_p = (EHASH_DIR_RECORD *) ((char *) dir_page_p + dir_offset);
5025 
5026  if (VPID_ISNULL (&dir_record_p->bucket_vpid))
5027  {
5028  printf ("* Dir loc : %d points to bucket page id: NULL *\n", dir_ptr_no);
5029  }
5030  else
5031  {
5032  printf ("* Dir loc : %d points to vol:%d bucket pgid: %d *\n", dir_ptr_no,
5033  dir_record_p->bucket_vpid.volid, dir_record_p->bucket_vpid.pageid);
5034  }
5035 
5036  dir_ptr_no++;
5037  dir_offset += sizeof (EHASH_DIR_RECORD);
5038  }
5039 
5040  /* Release last page */
5041  pgbuf_unfix_and_init (thread_p, dir_page_p);
5042 
5043  printf ("* *\n");
5044  printf ("*********************************************************\n");
5045 
5046  /* Print buckets */
5047 
5048  if (file_get_num_user_pages (thread_p, &dir_header_p->bucket_file, &num_pages) != NO_ERROR)
5049  {
5050  ASSERT_ERROR ();
5051  return;
5052  }
5053  num_pages -= 1;
5054 
5055  for (bucket_page_no = 0; bucket_page_no <= num_pages; bucket_page_no++)
5056  {
5057  bucket_page_p = ehash_fix_nth_page (thread_p, &dir_header_p->bucket_file, bucket_page_no, PGBUF_LATCH_READ);
5058  if (bucket_page_p == NULL)
5059  {
5060  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5061  return;
5062  }
5063 
5064  printf ("\n\n");
5065  ehash_dump_bucket (thread_p, bucket_page_p, dir_header_p->key_type);
5066  pgbuf_unfix_and_init (thread_p, bucket_page_p);
5067  }
5068 
5069  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5070  return;
5071 }
5072 
5073 #if defined(ENABLE_UNUSED_FUNCTION)
5074 /*
5075  * ehash_print_bucket () - Retrieve the bucket and print its contents
5076  * return:
5077  * ehid(in): identifier for the extendible hashing structure
5078  * nth_ptr(in): which pointer of the directory points to the bucket
5079  *
5080  * Note: A debugging function. Prints out the contents of the bucket
5081  * pointed by the "nth_ptr" pointer.
5082  */
5083 void
5084 ehash_print_bucket (THREAD_ENTRY * thread_p, EHID * ehid_p, int nth_ptr)
5085 {
5086  EHASH_DIR_HEADER *dir_header_p;
5087  PAGE_PTR dir_root_page_p;
5088  VPID bucket_vpid;
5089  PAGE_PTR bucket_page_p;
5090 
5091  dir_root_page_p = ehash_fix_ehid_page (thread_p, ehid_p, PGBUF_LATCH_READ);
5092  if (dir_root_page_p == NULL)
5093  {
5094  return;
5095  }
5096 
5097  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
5098 
5099  if (ehash_find_bucket_vpid (thread_p, ehid_p, dir_header_p, nth_ptr, PGBUF_LATCH_WRITE, &bucket_vpid) != NO_ERROR)
5100  {
5101  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5102  return;
5103  }
5104 
5105  bucket_page_p = ehash_fix_old_page (thread_p, &ehid_p->vfid, &bucket_vpid, PGBUF_LATCH_READ);
5106  if (bucket_page_p == NULL)
5107  {
5108  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5109  return;
5110  }
5111  ehash_dump_bucket (thread_p, bucket_page_p, dir_header_p->key_type);
5112 
5113  pgbuf_unfix_and_init (thread_p, bucket_page_p);
5114  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5115 
5116  return;
5117 }
5118 #endif
5119 
5120 /*
5121  * ehash_dump_bucket () - Print the bucket's contents
5122  * return:
5123  * buc_pgptr(in): bucket page whose contents is going to be dumped
5124  * key_type(in): type of the key
5125  *
5126  * Note: A debugging function. Prints out the contents of the given bucket.
5127  */
5128 static void
5129 ehash_dump_bucket (THREAD_ENTRY * thread_p, PAGE_PTR bucket_page_p, DB_TYPE key_type)
5130 {
5131  EHASH_BUCKET_HEADER *bucket_header_p;
5132  char *bucket_record_p;
5133  RECDES recdes;
5134  PGSLOTID slot_id, first_slot_id = -1;
5135  int key_size;
5136  OID assoc_value;
5137  int num_records;
5138  int i;
5139 #if defined (ENABLE_UNUSED_FUNCTION)
5140  VPID *ovf_vpid_p;
5141  int hour, minute, second, millisecond, month, day, year;
5142  double d;
5143 #endif
5144 
5145  (void) spage_next_record (bucket_page_p, &first_slot_id, &recdes, PEEK);
5146  bucket_header_p = (EHASH_BUCKET_HEADER *) recdes.data;
5147 
5148  printf ("************************************************************\n");
5149  printf ("* local_depth : %d *\n", bucket_header_p->local_depth);
5150  printf ("* no. records : %d *\n",
5151  spage_number_of_records (bucket_page_p) - 1);
5152  printf ("* *\n");
5153  printf ("* No Key Assoc Value *\n");
5154  printf ("* ==== ===================== ================== *\n");
5155 
5156  num_records = spage_number_of_records (bucket_page_p);
5157 
5158  for (slot_id = 1; slot_id < num_records; slot_id++)
5159  {
5160  printf ("* %2d", slot_id);
5161 
5162  spage_get_record (thread_p, bucket_page_p, slot_id, &recdes, PEEK);
5163  bucket_record_p = (char *) recdes.data;
5164  bucket_record_p = ehash_read_oid_from_record (bucket_record_p, &assoc_value);
5165 
5166  switch (key_type)
5167  {
5168 
5169  case DB_TYPE_STRING:
5170 #if defined (ENABLE_UNUSED_FUNCTION)
5171  if (recdes.type == REC_BIGONE)
5172  {
5173  ovf_vpid_p = (VPID *) bucket_record_p;
5174  bucket_record_p += sizeof (VPID);
5175 
5176  printf (" ");
5177  for (i = 0; i < EHASH_LONG_STRING_PREFIX_SIZE; i++)
5178  {
5179  putchar (*(bucket_record_p++));
5180  }
5181  printf (" ovf: %4d | %1d ", ovf_vpid_p->pageid, ovf_vpid_p->volid);
5182  }
5183  else
5184 #endif
5185  {
5186  key_size = recdes.length - CAST_BUFLEN (bucket_record_p - recdes.data);
5187  printf (" %s", bucket_record_p);
5188  for (i = 0; i < (29 - key_size); i++)
5189  {
5190  printf (" ");
5191  }
5192  }
5193  break;
5194 
5195  case DB_TYPE_OBJECT:
5196  printf (" (%5d,%5d,%5d) ", ((OID *) bucket_record_p)->volid, ((OID *) bucket_record_p)->pageid,
5197  ((OID *) bucket_record_p)->slotid);
5198  break;
5199 #if defined (ENABLE_UNUSED_FUNCTION)
5200  case DB_TYPE_DOUBLE:
5201  OR_MOVE_DOUBLE (bucket_record_p, &d);
5202  printf (" %20f ", d);
5203  break;
5204 
5205  case DB_TYPE_FLOAT:
5206  printf (" %20f ", *(float *) bucket_record_p);
5207  break;
5208 
5209  case DB_TYPE_INTEGER:
5210  printf (" %14d ", *(int *) bucket_record_p);
5211  break;
5212 
5213  case DB_TYPE_BIGINT:
5214  printf (" %19lld ", (long long) (*(DB_BIGINT *) bucket_record_p));
5215  break;
5216 
5217  case DB_TYPE_SHORT:
5218  printf (" %14d ", *(short *) bucket_record_p);
5219  break;
5220 
5221  case DB_TYPE_DATE:
5222  db_date_decode ((DB_DATE *) bucket_record_p, &month, &day, &year);
5223  fprintf (stdout, " %2d/%2d/%4d ", month, day, year);
5224  break;
5225 
5226  case DB_TYPE_TIME:
5227  db_time_decode ((DB_TIME *) bucket_record_p, &hour, &minute, &second);
5228  fprintf (stdout, " %2d:%2d:%2d ", hour, minute, second);
5229  break;
5230 
5231  case DB_TYPE_TIMESTAMP:
5232  {
5233  DB_DATE tmp_date;
5234  DB_TIME tmp_time;
5235 
5236  db_timestamp_decode_ses ((DB_UTIME *) bucket_record_p, &tmp_date, &tmp_time);
5237  db_date_decode (&tmp_date, &month, &day, &year);
5238  db_time_decode (&tmp_time, &hour, &minute, &second);
5239  printf (" %2d:%2d:%2d %2d/%2d/%4d ", hour, minute, second, month, day, year);
5240  }
5241  break;
5242 
5243  case DB_TYPE_DATETIME:
5244  db_datetime_decode ((DB_DATETIME *) bucket_record_p, &month, &day, &year, &hour, &minute, &second,
5245  &millisecond);
5246  printf (" %2d:%2d:%2d.%03d %2d/%2d/%4d ", hour, minute, second, millisecond, month, day, year);
5247  break;
5248 
5249  case DB_TYPE_MONETARY:
5250  OR_MOVE_DOUBLE (bucket_record_p, &d);
5251  printf (" %14f type %d ", d, ((DB_MONETARY *) bucket_record_p)->type);
5252  break;
5253 #endif
5254  default:
5255  /* Unspecified key type: Directory header has been corrupted */
5257  return;
5258  }
5259  printf ("(%5d,%5d,%5d) *\n", assoc_value.volid, assoc_value.pageid, assoc_value.slotid);
5260 
5261  }
5262 
5263  printf ("*********************************************************\n");
5264 }
5265 
5266 /* TODO: check not use */
5267 #if 0
5268 /*
5269  * Specialty functions
5270  */
5271 
5272 /*
5273  * xeh_find () -
5274  * return: error code
5275  * ehid(in): hash table identifier
5276  * value(in): : key value
5277  * oid(in): oid registered under key value if any (returned)
5278  *
5279  * Note: This was designed for the maintenance of the UNIQUE integrity
5280  * constraint. The only difference between this and ehash_search is that
5281  * the DB_TYPE is supplied as a parameter.
5282  * This is necessary so that the network interface can determine how
5283  * long the key value is and pack it appropriately into the transfer buffer.
5284  */
5285 EH_SEARCH
5286 xeh_find (EHID * ehid, void *value, OID * oid)
5287 {
5288  EH_SEARCH status;
5289  OID current;
5290 
5291  status = ehash_search (ehid, value, &current);
5292 
5293  if (status == EH_KEY_FOUND)
5294  {
5295  *oid = current;
5296  }
5297 
5298  return (status);
5299 }
5300 #endif
5301 
5302 /*
5303  * Recovery functions
5304  */
5305 
5306 /*
5307  * ehash_rv_init_bucket_redo () - Redo the initilization of a bucket page
5308  * return: int
5309  * recv(in): Recovery structure
5310  *
5311  * Note: Redo the initilization of a bucket page. The data area of the
5312  * recovery structure contains the alignment value and the
5313  * local depth of the bucket page.
5314  */
5315 int
5317 {
5318  char alignment;
5319  EHASH_BUCKET_HEADER bucket_header;
5320  RECDES bucket_recdes;
5321  const char *record_p;
5322  PGSLOTID slot_id;
5323  int success;
5324 
5325  record_p = recv_p->data;
5326 
5327  alignment = *(char *) record_p;
5328  record_p += sizeof (char);
5329  bucket_header.local_depth = *(char *) record_p;
5330 
5331  pgbuf_set_page_ptype (thread_p, recv_p->pgptr, PAGE_EHASH);
5332 
5333  /*
5334  * Initilize the bucket to contain variable-length records
5335  * on ordered slots.
5336  */
5337  spage_initialize (thread_p, recv_p->pgptr, UNANCHORED_KEEP_SEQUENCE, alignment, DONT_SAFEGUARD_RVSPACE);
5338 
5339  /* Set the record descriptor to the Bucket header */
5340  bucket_recdes.data = (char *) &bucket_header;
5341  bucket_recdes.area_size = bucket_recdes.length = sizeof (EHASH_BUCKET_HEADER);
5342  bucket_recdes.type = REC_HOME;
5343 
5344  success = spage_insert (thread_p, recv_p->pgptr, &bucket_recdes, &slot_id);
5345  if (success != SP_SUCCESS)
5346  {
5347  /* Slotted page module refuses to insert a short size record to an empty page. This should never happen. */
5348  if (success != SP_ERROR)
5349  {
5351  }
5352  assert (er_errid () != NO_ERROR);
5353  return er_errid ();
5354  }
5355 
5356  return NO_ERROR;
5357 }
5358 
5359 /*
5360  * ehash_rv_init_dir_redo () - Redo the initilization of a directory
5361  * return: int
5362  * recv(in): Recovery structure
5363  */
5364 int
5366 {
5367  pgbuf_set_page_ptype (thread_p, recv_p->pgptr, PAGE_EHASH);
5368 
5369  return log_rv_copy_char (thread_p, recv_p);
5370 }
5371 
5372 /*
5373  * ehash_rv_insert_redo () - Redo the insertion of an entry to a bucket page
5374  * return: int
5375  * recv(in): Recovery structure
5376  *
5377  * Note: Redo the insertion of a (key, assoc-value) entry to a specific
5378  * extendible hashing structure. The data area of the recovery
5379  * structure contains the key type, and the entry to be inserted.
5380  */
5381 int
5383 {
5384  PGSLOTID slot_id;
5385  RECDES recdes;
5386  int success;
5387 
5388  slot_id = recv_p->offset;
5389  recdes.type = *(short *) (recv_p->data);
5390  recdes.data = (char *) (recv_p->data) + sizeof (short);
5391  recdes.area_size = recdes.length = recv_p->length - sizeof (recdes.type);
5392 
5393  success = spage_insert_for_recovery (thread_p, recv_p->pgptr, slot_id, &recdes);
5394  pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
5395 
5396  if (success != SP_SUCCESS)
5397  {
5399  return er_errid ();
5400  }
5401 
5402  return NO_ERROR;
5403 }
5404 
5405 
5406 /*
5407  * ehash_rv_insert_undo () - Undo the insertion of an entry to ext. hash
5408  * return: int
5409  * recv(in): Recovery structure
5410  *
5411  * Note: Undo the insertion of a (key, assoc-value) entry to a
5412  * specific extendible hashing structure. The data area of the
5413  * recovery structure contains the ext. hashing identifier, and
5414  * the entry to be deleted. Note that this function uses the
5415  * recovery delete function (not the original one) in order to
5416  * avoid possible merge operations.
5417  */
5418 int
5420 {
5421  EHID ehid;
5422  char *record_p = (char *) recv_p->data;
5423 #if defined (ENABLE_UNUSED_FUNCTION)
5424  short record_type = recv_p->offset;
5425 #endif
5426 
5427  record_p = ehash_read_ehid_from_record (record_p, &ehid);
5428  record_p += sizeof (OID);
5429 
5430 #if defined (ENABLE_UNUSED_FUNCTION)
5431  if (record_type == REC_BIGONE)
5432  {
5433  /* This record is for a long string. At the monent it seems more logical to compose the whole key here rather
5434  * than at the logging time. But, if the overflow pages cannot be guaranteed to exist when this function is
5435  * called, then the whole key would need to be logged and this portion of the code becomes unnecessary. */
5436  RECDES recdes;
5437  char *long_str;
5438  int error;
5439 
5440  /* Compose the whole key */
5441  recdes.data = (char *) recv_p->data + sizeof (EHID);
5442  recdes.length = recdes.area_size = recv_p->length - sizeof (EHID);
5443  long_str = ehash_compose_overflow (thread_p, &recdes);
5444 
5445  if (long_str == NULL)
5446  {
5447  assert (er_errid () != NO_ERROR);
5448  error = er_errid ();
5449  return error;
5450  }
5451 
5452  error = ehash_rv_delete (thread_p, &ehid, (void *) long_str);
5453  free_and_init (long_str);
5454  return error;
5455  }
5456  else
5457 #endif
5458  {
5459  return ehash_rv_delete (thread_p, &ehid, (void *) record_p);
5460  }
5461 }
5462 
5463 /*
5464  * ehash_rv_delete_redo () - Redo the deletion of an entry to ext. hash
5465  * return: int
5466  * recv(in): Recovery structure
5467  *
5468  * Note: Redo the deletion of a (key, assoc-value) entry from a
5469  * specific extendible hashing structure. The data area of the
5470  * recovery structure contains the key type followed by the
5471  * entry to be deleted.
5472  */
5473 int
5475 {
5476  PGSLOTID slot_id;
5477  RECDES recdes;
5478  RECDES existing_recdes;
5479 
5480  slot_id = recv_p->offset;
5481  recdes.type = *(short *) (recv_p->data);
5482  recdes.data = (char *) (recv_p->data) + sizeof (short);
5483  recdes.area_size = recdes.length = recv_p->length - sizeof (recdes.type);
5484 
5485  if (spage_get_record (thread_p, recv_p->pgptr, slot_id, &existing_recdes, PEEK) == S_SUCCESS)
5486  /*
5487  * There is a record in the specified slot. Check if it is the same
5488  * record
5489  */
5490  if ((existing_recdes.type == recdes.type) && (existing_recdes.length == recdes.length)
5491  && (memcmp (existing_recdes.data, recdes.data, recdes.length) == 0))
5492  {
5493  /* The record exist in the correct slot in the page. So, delete this slot from the page */
5494  (void) spage_delete (thread_p, recv_p->pgptr, slot_id);
5495  pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
5496 
5497  }
5498 
5499  return NO_ERROR;
5500 }
5501 
5502 /*
5503  * ehash_rv_delete_undo () - Undo the deletion of an entry from ext. hash
5504  * return: int
5505  * recv(in): Recovery structure
5506  *
5507  * Note: Undo the deletion of a (key, assoc-value) entry from a
5508  * specific extendible hashing structure. The data area of the
5509  * recovery structure contains the ext. hashing identifier,
5510  * followed by the entry to be inserted back.
5511  */
5512 int
5514 {
5515  EHID ehid;
5516  OID oid;
5517  char *record_p = (char *) recv_p->data;
5518  int error = NO_ERROR;
5519 #if defined (ENABLE_UNUSED_FUNCTION)
5520  short record_type = recv_p->offset;
5521 #endif
5522 
5523  record_p = ehash_read_ehid_from_record (record_p, &ehid);
5524  record_p = ehash_read_oid_from_record (record_p, &oid);
5525 
5526  /* Now rec_ptr is pointing to the key value */
5527 #if defined (ENABLE_UNUSED_FUNCTION)
5528  if (record_type == REC_BIGONE)
5529  {
5530  /* This record is for a long string. At the monent it seems more logical to compose the whole key here rather
5531  * than at the logging time. But, if the overflow pages cannot be guaranteed to exist when this function is
5532  * called, then the whole key would need to be logged and this portion of the code becomes unnecessary. */
5533  RECDES recdes;
5534  VPID *vpid = (VPID *) record_p;
5535  char *long_str;
5536 
5537  /* Compose the whole key */
5538  recdes.data = (char *) recv_p->data + sizeof (EHID);
5539  recdes.length = recdes.area_size = recv_p->length - sizeof (EHID);
5540  long_str = ehash_compose_overflow (thread_p, &recdes);
5541  if (long_str == NULL)
5542  {
5543  assert (er_errid () != NO_ERROR);
5544  return er_errid ();
5545  }
5546 
5547  if (ehash_insert_helper (thread_p, &ehid, (void *) long_str, &oid, S_LOCK, vpid) == NULL)
5548  {
5549  assert (er_errid () != NO_ERROR);
5550  error = er_errid ();
5551  }
5552 
5553  free_and_init (long_str);
5554  }
5555  else
5556 #endif
5557  {
5558  if (ehash_insert_helper (thread_p, &ehid, (void *) record_p, &oid, S_LOCK, NULL) == NULL)
5559  {
5560  assert (er_errid () != NO_ERROR);
5561  error = er_errid ();
5562  }
5563  }
5564 
5565  return (error);
5566 }
5567 
5568 /*
5569  * ehash_rv_delete () - Recovery delete function
5570  * return: int
5571  * ehid(in): identifier for the extendible hashing structure
5572  * key(in): Key value to remove
5573  *
5574  * Note: This is the recovery version of the "ehash_delete" function. Just
5575  * like the original function, this one also deletes the given
5576  * key value (together with its assoc_value) from the specified
5577  * extendible hashing structure, and returns an error code if the
5578  * key does not exist. However, unlike "ehash_delete", it does not
5579  * check the condition of bucket and invoke "eh_try_to_merge"
5580  * when possible. Nor does it produce undo log information. (It
5581  * merely produces redo log to produce the compansating log
5582  * record for this operation. This is to minimize the recovery
5583  * and transaction abort activities.)
5584  */
5585 static int
5586 ehash_rv_delete (THREAD_ENTRY * thread_p, EHID * ehid_p, void *key_p)
5587 {
5588  EHASH_DIR_HEADER *dir_header_p;
5589  PAGE_PTR dir_root_page_p;
5590  VPID bucket_vpid;
5591  PAGE_PTR bucket_page_p;
5592  PGSLOTID slot_no;
5593 
5594  RECDES bucket_recdes;
5595  RECDES log_recdes_redo;
5596  char *log_redo_record_p;
5597  int error = NO_ERROR;
5598 
5599  dir_root_page_p =
5600  ehash_find_bucket_vpid_with_hash (thread_p, ehid_p, key_p, PGBUF_LATCH_READ, PGBUF_LATCH_WRITE, &bucket_vpid, NULL,
5601  NULL);
5602  if (dir_root_page_p == NULL)
5603  {
5604  assert (er_errid () != NO_ERROR);
5605  return er_errid ();
5606  }
5607  dir_header_p = (EHASH_DIR_HEADER *) dir_root_page_p;
5608 
5609  if (bucket_vpid.pageid == NULL_PAGEID)
5610  {
5611  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5613  return ER_EH_UNKNOWN_KEY;
5614  }
5615  else
5616  {
5617  bucket_page_p = ehash_fix_old_page (thread_p, &ehid_p->vfid, &bucket_vpid, PGBUF_LATCH_WRITE);
5618  if (bucket_page_p == NULL)
5619  {
5620  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5621 
5622  assert (er_errid () != NO_ERROR);
5623  return er_errid ();
5624  }
5625 
5626  /* Try to delete key from buc_page */
5627 
5628  /* Check if deletion possible, or not */
5629  if (ehash_locate_slot (thread_p, bucket_page_p, dir_header_p->key_type, key_p, &slot_no) == false)
5630  {
5631  /* Key does not exist, so return errorcode */
5632  pgbuf_unfix_and_init (thread_p, bucket_page_p);
5633  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5635  return ER_EH_UNKNOWN_KEY;
5636  }
5637  else
5638  {
5639  /* Key exists in the bucket */
5640 
5641  /* Put the redo (compansating) log record */
5642  (void) spage_get_record (thread_p, bucket_page_p, slot_no, &bucket_recdes, PEEK);
5643 
5644  /* Prepare the redo log record */
5645  if (ehash_allocate_recdes (&log_recdes_redo, bucket_recdes.length + sizeof (short), REC_HOME) == NULL)
5646  {
5647  /*
5648  * Will not be able to log a compensating log record... continue
5649  * anyhow...without the log...since we are doing recovery anyhow
5650  */
5651  error = ER_OUT_OF_VIRTUAL_MEMORY;
5652  }
5653  else
5654  {
5655  log_redo_record_p = log_recdes_redo.data;
5656 
5657  /* First insert the key type */
5658  *(short *) log_redo_record_p = bucket_recdes.type;
5659  log_redo_record_p += sizeof (short);
5660 
5661  /* Copy (the assoc-value, key) pair from the bucket record */
5662  memcpy (log_redo_record_p, bucket_recdes.data, bucket_recdes.length);
5663 
5664  /* why is redo logged inside an undo? */
5665  log_append_redo_data2 (thread_p, RVEH_DELETE, &ehid_p->vfid, bucket_page_p, slot_no,
5666  log_recdes_redo.length, log_recdes_redo.data);
5667 
5668  ehash_free_recdes (&log_recdes_redo);
5669  }
5670 
5671  /* Delete the bucket record from the bucket; */
5672  (void) spage_delete (thread_p, bucket_page_p, slot_no);
5673  pgbuf_set_dirty (thread_p, bucket_page_p, DONT_FREE);
5674 
5675  /* Do not remove the overflow pages here */
5676  /* Since they will be removed by their own undo functions */
5677  }
5678  }
5679 
5680  pgbuf_unfix_and_init (thread_p, bucket_page_p);
5681  pgbuf_unfix_and_init (thread_p, dir_root_page_p);
5682 
5683  return error;
5684 }
5685 
5686 /*
5687  * ehash_rv_increment () - Recovery increment counter
5688  * return: int
5689  * recv(in): Recovery structure
5690  *
5691  * Note: This function increments the value of an (integer) counter
5692  * during the recovery (or transaction abort) time. The location
5693  * of the counter and the amount of increment is passed as the
5694  * first and second element, respectively, on the data area of
5695  * the recovery structure.
5696  */
5697 int
5699 {
5700  int inc_cnt;
5701 
5702  inc_cnt = *(int *) recv_p->data;
5703 
5704  *(int *) ((char *) recv_p->pgptr + recv_p->offset) += inc_cnt;
5705 
5706  pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
5707  return NO_ERROR;
5708 }
5709 
5710 /*
5711  * ehash_rv_connect_bucket_redo () - Recovery connect bucket
5712  * return: int
5713  * recv(in): Recovery structure
5714  *
5715  * Note: This function sets a number of directory pointers (on the
5716  * same directory page) to the specified bucket pageid. The
5717  * bucket pageid, and the number of pointers to be updated are
5718  * passed as the first and the second elements, respectively,
5719  * of the data area of the recovery stucture.
5720  */
5721 int
5723 {
5724  EHASH_REPETITION repetition;
5725  EHASH_DIR_RECORD *dir_record_p;
5726  int i;
5727 
5728  repetition = *(EHASH_REPETITION *) recv_p->data;
5729 
5730  /* Update the directory page */
5731  dir_record_p = (EHASH_DIR_RECORD *) ((char *) recv_p->pgptr + recv_p->offset);
5732  for (i = 0; i < repetition.count; i++)
5733  {
5734 
5735  if (!VPID_EQ (&(dir_record_p->bucket_vpid), &repetition.vpid))
5736  {
5737  /* Yes, correction is needed */
5738  dir_record_p->bucket_vpid = repetition.vpid;
5739  pgbuf_set_dirty (thread_p, recv_p->pgptr, DONT_FREE);
5740  }
5741 
5742  dir_record_p++;
5743  }
5744 
5745  return NO_ERROR;
5746 }
5747 
5748 static char *
5749 ehash_read_oid_from_record (char *record_p, OID * oid_p)
5750 {
5751  oid_p->pageid = *(PAGEID *) record_p;
5752  record_p += sizeof (PAGEID);
5753 
5754  oid_p->volid = *(VOLID *) record_p;
5755  record_p += sizeof (VOLID);
5756 
5757  oid_p->slotid = *(PGSLOTID *) record_p;
5758  record_p += sizeof (PGSLOTID);
5759 
5760  return record_p;
5761 }
5762 
5763 static char *
5764 ehash_write_oid_to_record (char *record_p, OID * oid_p)
5765 {
5766  *(PAGEID *) record_p = oid_p->pageid;
5767  record_p += sizeof (PAGEID);
5768 
5769  *(VOLID *) record_p = oid_p->volid;
5770  record_p += sizeof (VOLID);
5771 
5772  *(PGSLOTID *) record_p = oid_p->slotid;
5773  record_p += sizeof (PGSLOTID);
5774 
5775  return record_p;
5776 }
5777 
5778 static char *
5779 ehash_read_ehid_from_record (char *record_p, EHID * ehid_p)
5780 {
5781  ehid_p->pageid = *(PAGEID *) record_p;
5782  record_p += sizeof (PAGEID);
5783 
5784  ehid_p->vfid.fileid = *(FILEID *) record_p;
5785  record_p += sizeof (FILEID);
5786 
5787  ehid_p->vfid.volid = *(VOLID *) record_p;
5788  record_p += (sizeof (EHID) - sizeof (PAGEID) - sizeof (FILEID));
5789 
5790  return record_p;
5791 }
5792 
5793 static char *
5794 ehash_write_ehid_to_record (char *record_p, EHID * ehid_p)
5795 {
5796  *(PAGEID *) record_p = ehid_p->pageid;
5797  record_p += sizeof (PAGEID);
5798 
5799  *(FILEID *) record_p = ehid_p->vfid.fileid;
5800  record_p += sizeof (FILEID);
5801 
5802  *(VOLID *) record_p = ehid_p->vfid.volid;
5803  record_p += (sizeof (EHID) - sizeof (PAGEID) - sizeof (FILEID));
5804 
5805  return record_p;
5806 }
PGLENGTH offset
Definition: recovery.h:201
static void ehash_free_recdes(RECDES *recdes)
int file_destroy(THREAD_ENTRY *thread_p, const VFID *vfid, bool is_temp)
char * PAGE_PTR
int char_isspace(int c)
Definition: chartype.c:109
int ehash_rv_init_bucket_redo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
static EHASH_RESULT ehash_insert_to_bucket(THREAD_ENTRY *thread_p, EHID *ehid, VFID *ovf_file, bool is_temp, PAGE_PTR buc_pgptr, DB_TYPE key_type, void *key_ptr, OID *value_ptr, VPID *existing_ovf_vpid)
struct db_datetime DB_DATETIME
Definition: dbtype_def.h:773
int file_numerable_find_nth(THREAD_ENTRY *thread_p, const VFID *vfid, int nth, bool auto_alloc, FILE_INIT_PAGE_FUNC f_init, void *f_init_args, VPID *vpid_nth)
#define NO_ERROR
Definition: error_code.h:46
int ehash_rv_init_dir_redo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
int area_size
#define ER_EH_ROOT_CORRUPTED
Definition: error_code.h:116
static int ehash_compare_key(THREAD_ENTRY *thread_p, char *bucrec_ptr, DB_TYPE key_type, void *key, INT16 rec_type, int *out_compare_result)
static int ehash_connect_bucket(THREAD_ENTRY *thread_p, EHID *ehid, int local_depth, EHASH_HASH_KEY hash_key, VPID *buc_vpid, bool is_temp)
#define GETBIT(word, pos)
#define EHASH_LAST_OFFSET_IN_FIRST_PAGE
void log_append_undoredo_data2(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, const VFID *vfid, PAGE_PTR pgptr, PGLENGTH offset, int undo_length, int redo_length, const void *undo_data, const void *redo_data)
Definition: log_manager.c:1861
int file_init_temp_page_type(THREAD_ENTRY *thread_p, PAGE_PTR page, void *args)
static char ehash_find_depth(THREAD_ENTRY *thread_p, EHID *ehid, int location, VPID *vpid, VPID *sib_vpid)
#define ASSERT_ERROR()
unsigned int mht_1strhash(const void *key, const unsigned int ht_size)
Definition: memory_hash.c:447
static PAGE_PTR ehash_extend_bucket(THREAD_ENTRY *thread_p, EHID *ehid, PAGE_PTR dir_Rpgptr, EHASH_DIR_HEADER *dir_header, PAGE_PTR buc_pgptr, void *key, EHASH_HASH_KEY hash_key, int *new_bit, VPID *buc_vpid, bool is_temp)
INT32 pageid
int file_dealloc(THREAD_ENTRY *thread_p, const VFID *vfid, const VPID *vpid, FILE_TYPE file_type_hint)
PAGEID FILEID
#define EHASH_MAX_STRING_SIZE
int spage_insert(THREAD_ENTRY *thread_p, PAGE_PTR page_p, RECDES *record_descriptor_p, PGSLOTID *out_slot_id_p)
PAGEID DKNPAGES
unsigned int htonl(unsigned int from)
DB_TYPE
Definition: dbtype_def.h:670
int file_alloc_multiple(THREAD_ENTRY *thread_p, const VFID *vfid, FILE_INIT_PAGE_FUNC f_init, void *f_init_args, int npages, VPID *vpids_out)
int file_numerable_truncate(THREAD_ENTRY *thread_p, const VFID *vfid, DKNPAGES npages)
#define ER_FAILED
Definition: error_code.h:47
static PAGE_PTR ehash_split_bucket(THREAD_ENTRY *thread_p, EHASH_DIR_HEADER *dir_header, PAGE_PTR buc_pgptr, void *key, int *old_local_depth, int *new_local_depth, VPID *sib_vpid, bool is_temp)
void spage_initialize(THREAD_ENTRY *thread_p, PAGE_PTR page_p, INT16 slot_type, unsigned short alignment, bool is_saving)
int db_datetime_decode(const DB_DATETIME *datetime, int *month, int *day, int *year, int *hour, int *minute, int *second, int *millisecond)
Definition: db_date.c:4574
static PAGE_PTR ehash_fix_ehid_page(THREAD_ENTRY *thread_p, EHID *ehid, PGBUF_LATCH_MODE latch_mode)
int ehash_rv_insert_redo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
static int ehash_find_first_bit_position(THREAD_ENTRY *thread_p, EHASH_DIR_HEADER *dir_header, PAGE_PTR buc_pgptr, EHASH_BUCKET_HEADER *buc_header, void *key, int num_recs, PGSLOTID first_slotid, int *old_local_depth, int *new_local_depth)
struct db_monetary DB_MONETARY
Definition: dbtype_def.h:828
FILE_TYPE
Definition: file_manager.h:38
static int ehash_rv_delete(THREAD_ENTRY *thread_p, EHID *ehid, void *key)
#define pgbuf_unfix(thread_p, pgptr)
Definition: page_buffer.h:276
EHID * xehash_create(THREAD_ENTRY *thread_p, EHID *ehid_p, DB_TYPE key_type, int exp_num_entries, OID *class_oid_p, int attr_id, bool is_tmp)
int ansisql_strcmp(const char *s, const char *t)
Definition: memory_alloc.c:79
static void ehash_dir_locate(int *out_page_no_p, int *out_offset_p)
#define EHASH_HASH_KEY_BITS
#define ASSERT_ERROR_AND_SET(error_code)
static int ehash_get_pseudo_key(THREAD_ENTRY *thread_p, RECDES *recdes, DB_TYPE key_type, EHASH_HASH_KEY *out_hash_key)
#define FIND_OFFSET(hash_key, depth)
#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
SCAN_CODE overflow_get(THREAD_ENTRY *thread_p, const VPID *ovf_vpid, RECDES *recdes, MVCC_SNAPSHOT *mvcc_snapshot)
void log_append_undo_data2(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, const VFID *vfid, PAGE_PTR pgptr, PGLENGTH offset, int length, const void *data)
Definition: log_manager.c:1933
INT16 VOLID
void log_sysop_start(THREAD_ENTRY *thread_p)
Definition: log_manager.c:3578
void ehash_dump(THREAD_ENTRY *thread_p, EHID *ehid_p)
#define OID_SET_NULL(oidp)
Definition: oid.h:85
#define NULL_SLOTID
char * data
int32_t pageid
Definition: dbtype_def.h:879
unsigned int EHASH_HASH_KEY
static void ehash_adjust_local_depth(THREAD_ENTRY *thread_p, EHID *ehid, PAGE_PTR dir_Rpgptr, EHASH_DIR_HEADER *dir_header, int depth, int delta, bool is_temp)
#define diff
Definition: mprec.h:352
int er_errid(void)
static char * ehash_allocate_recdes(RECDES *recdes, int size, short type)
#define SP_SUCCESS
Definition: slotted_page.h:50
#define VPID_INITIALIZER
Definition: dbtype_def.h:894
PAGE_TYPE
int do_merge(PARSER_CONTEXT *parser, PT_NODE *statement)
#define er_log_debug(...)
int ehash_rv_connect_bucket_redo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
static EHID * ehash_create_helper(THREAD_ENTRY *thread_p, EHID *ehid, DB_TYPE key_type, int exp_num_entries, OID *class_oid, int attr_id, bool istmp)
#define COPY_OID(dest_oid_ptr, src_oid_ptr)
Definition: oid.h:63
#define OR_MONETARY_SIZE
int file_create_ehash_dir(THREAD_ENTRY *thread_p, int npages, bool is_tmp, FILE_EHASH_DES *des_ehash, VFID *vfid)
SCAN_CODE spage_get_record(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES *record_descriptor_p, int is_peeking)
#define ER_EH_INVALID_KEY_TYPE
Definition: error_code.h:114
static void * ehash_insert_helper(THREAD_ENTRY *thread_p, EHID *ehid, void *key, OID *value_ptr, int lock_type, VPID *existing_ovf_vpid)
#define EHASH_NUM_NON_FIRST_PAGES
#define DONT_SAFEGUARD_RVSPACE
Definition: slotted_page.h:54
#define VFID_ISNULL(vfid_ptr)
Definition: file_manager.h:72
int ehash_rv_init_dir_new_page_redo(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
void THREAD_ENTRY
static short ehash_get_key_size(DB_TYPE key_type)
#define NULL_PAGEID
#define pgbuf_unfix_and_init(thread_p, pgptr)
Definition: page_buffer.h:63
static bool ehash_binary_search_bucket(THREAD_ENTRY *thread_p, PAGE_PTR buc_pgptr, PGSLOTID num_record, DB_TYPE key_type, void *key, PGSLOTID *position)
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)
#define FREE(PTR)
Definition: cas_common.h:56
void log_append_redo_data2(THREAD_ENTRY *thread_p, LOG_RCVINDEX rcvindex, const VFID *vfid, PAGE_PTR pgptr, PGLENGTH offset, int length, const void *data)
Definition: log_manager.c:1995
#define EHASH_SHORT_BITS
static int ehash_insert_to_bucket_after_create(THREAD_ENTRY *thread_p, EHID *ehid, PAGE_PTR dir_Rpgptr, EHASH_DIR_HEADER *dir_header, VPID *buc_vpid, int location, EHASH_HASH_KEY hash_key, bool is_temp, void *key, OID *value_ptr, VPID *existing_ovf_vpid)
#define EHASH_LAST_OFFSET_IN_NON_FIRST_PAGE
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
int spage_max_space_for_new_record(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
Definition: slotted_page.c:984
int ehash_rv_increment(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
int log_rv_copy_char(THREAD_ENTRY *thread_p, LOG_RCV *rcv)
Definition: log_manager.c:8696
PAGE_PTR pgptr
Definition: recovery.h:199
#define assert(x)
struct ehash_bucket_header EHASH_BUCKET_HEADER
static int ehash_distribute_records_into_two_bucket(THREAD_ENTRY *thread_p, EHASH_DIR_HEADER *dir_header, PAGE_PTR buc_pgptr, EHASH_BUCKET_HEADER *buc_header, int num_recs, PGSLOTID first_slotid, PAGE_PTR sib_pgptr)
int32_t fileid
Definition: dbtype_def.h:886
bool pgbuf_check_page_ptype(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, PAGE_TYPE ptype)
int ehash_rv_insert_undo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
int file_get_num_user_pages(THREAD_ENTRY *thread_p, const VFID *vfid, int *n_user_pages_out)
#define ER_GENERIC_ERROR
Definition: error_code.h:49
PGBUF_LATCH_MODE
Definition: page_buffer.h:176
#define ER_EH_CORRUPTED
Definition: error_code.h:115
DB_IDENTIFIER OID
Definition: dbtype_def.h:967
#define EHASH_DIR_HEADER_SIZE
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
static EHASH_RESULT ehash_check_merge_possible(THREAD_ENTRY *thread_p, EHID *ehid, EHASH_DIR_HEADER *dir_header, VPID *buc_vpid, PAGE_PTR buc_pgptr, int location, int lock_type, int *old_local_depth, VPID *sib_vpid, PAGE_PTR *out_sib_pgptr, PGSLOTID *out_first_slotid, int *out_num_recs, int *out_loc)
#define ER_EH_UNKNOWN_EXT_HASH
Definition: error_code.h:111
int local_depth_count[EHASH_HASH_KEY_BITS+1]
unsigned int mht_3strhash(const void *key, const unsigned int ht_size)
Definition: memory_hash.c:483
static PAGE_PTR ehash_fix_nth_page(THREAD_ENTRY *thread_p, const VFID *vfid, int offset, PGBUF_LATCH_MODE mode)
void * ehash_insert(THREAD_ENTRY *thread_p, EHID *ehid_p, void *key_p, OID *value_p)
static enum scanner_mode mode
#define VPID_EQ(vpid_ptr1, vpid_ptr2)
Definition: dbtype_def.h:915
#define VFID_INITIALIZER
Definition: dbtype_def.h:890
short volid
Definition: dbtype_def.h:880
static PAGE_PTR ehash_find_bucket_vpid_with_hash(THREAD_ENTRY *thread_p, EHID *ehid, void *key, PGBUF_LATCH_MODE root_latch, PGBUF_LATCH_MODE bucket_latch, VPID *out_vpid, EHASH_HASH_KEY *out_hash_key, int *out_location)
static int ehash_write_key_to_record(RECDES *recdes, DB_TYPE key_type, void *key_ptr, short key_size, OID *value_ptr, bool long_str)
static EHASH_HASH_KEY ehash_hash_eight_bytes_type(char *key)
int length
Definition: recovery.h:202
struct ehid EHID
#define GETBITS(value, pos, n)
static void ehash_dump_bucket(THREAD_ENTRY *thread_p, PAGE_PTR buc_pgptr, DB_TYPE key_type)
#define NULL
Definition: freelistheap.h:34
int file_alloc(THREAD_ENTRY *thread_p, const VFID *vfid, FILE_INIT_PAGE_FUNC f_init, void *f_init_args, VPID *vpid_out, PAGE_PTR *page_out)
PGNSLOTS spage_number_of_records(PAGE_PTR page_p)
Definition: slotted_page.c:860
static EHASH_HASH_KEY ehash_hash(void *orig_key, DB_TYPE key_type)
if(extra_options)
Definition: dynamic_load.c:958
#define SETBIT(word,pos)
#define EHASH_LONG_STRING_PREFIX_SIZE
int ehash_map(THREAD_ENTRY *thread_p, EHID *ehid_p, int(*apply_function)(THREAD_ENTRY *thread_p, void *key, void *data, void *args), void *args)
static int success()
struct ehash_dir_record EHASH_DIR_RECORD
#define ER_PB_BAD_PAGEID
Definition: error_code.h:67
static bool ehash_locate_slot(THREAD_ENTRY *thread_p, PAGE_PTR page, DB_TYPE key_type, void *key, PGSLOTID *position)
#define pgbuf_fix(thread_p, vpid, fetch_mode, requestmode, condition)
Definition: page_buffer.h:255
VFID vfid
#define NULL_FILEID
static void ehash_shrink_directory_if_need(THREAD_ENTRY *thread_p, EHID *ehid, EHASH_DIR_HEADER *dir_header, bool is_temp)
#define CEIL_PTVDIV(dividend, divisor)
Definition: memory_alloc.h:50
int db_timestamp_decode_ses(const DB_TIMESTAMP *utime, DB_DATE *date, DB_TIME *timeval)
Definition: db_date.c:764
void * ehash_delete(THREAD_ENTRY *thread_p, EHID *ehid_p, void *key_p)
#define VFID_COPY(vfid_ptr1, vfid_ptr2)
Definition: file_manager.h:69
void log_sysop_abort(THREAD_ENTRY *thread_p)
Definition: log_manager.c:4017
#define max(a, b)
static int ehash_merge_permanent(THREAD_ENTRY *thread_p, EHID *ehid, PAGE_PTR dir_Rpgptr, EHASH_DIR_HEADER *dir_header, PAGE_PTR buc_pgptr, PAGE_PTR sib_pgptr, VPID *buc_vpid, VPID *sib_vpid, int num_recs, int loc, PGSLOTID first_slotid, int *out_new_local_depth, bool is_temp)
int64_t DB_BIGINT
Definition: dbtype_def.h:751
#define d1
EHASH_RESULT
#define CAST_BUFLEN
Definition: porting.h:471
static void error(const char *msg)
Definition: gencat.c:331
int file_init_page_type(THREAD_ENTRY *thread_p, PAGE_PTR page, void *args)
#define VPID_ISNULL(vpid_ptr)
Definition: dbtype_def.h:925
const char * data
Definition: recovery.h:203
int xehash_destroy(THREAD_ENTRY *thread_p, EHID *ehid_p)
void file_postpone_destroy(THREAD_ENTRY *thread_p, const VFID *vfid)
PGSLOTID spage_delete(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id)
EH_SEARCH
static void ehash_shrink_directory(THREAD_ENTRY *thread_p, EHID *ehid, int new_depth, bool is_temp)
#define ARG_FILE_LINE
Definition: error_manager.h:44
static const bool COPY
#define EHASH_UNDERFLOW_THRESHOLD
unsigned int DB_TIME
Definition: dbtype_def.h:754
int file_create_ehash(THREAD_ENTRY *thread_p, int npages, bool is_tmp, FILE_EHASH_DES *des_ehash, VFID *vfid)
INT16 PGLENGTH
unsigned short ntohs(unsigned short from)
unsigned int DB_DATE
Definition: dbtype_def.h:771
static void ehash_merge(THREAD_ENTRY *thread_p, EHID *ehid, void *key, bool is_temp)
INT16 PGSLOTID
static int ehash_initialize_bucket_new_page(THREAD_ENTRY *thread_p, PAGE_PTR page_p, void *alignment_depth)
#define free_and_init(ptr)
Definition: memory_alloc.h:147
static int ehash_compose_record(DB_TYPE key_type, void *key_ptr, OID *value_ptr, RECDES *recdes)
#define strlen(s1)
Definition: intl_support.c:43
static char * ehash_read_ehid_from_record(char *rec_p, EHID *ehid_p)
int oid_compare(const void *a, const void *b)
Definition: oid.c:243
int ehash_rv_delete_undo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
struct vpid VPID
Definition: dbtype_def.h:876
#define DB_PAGESIZE
#define OR_MOVE_DOUBLE(src, dst)
Definition: byte_order.h:89
unsigned int date
Definition: dbtype_def.h:776
void pgbuf_set_page_ptype(THREAD_ENTRY *thread_p, PAGE_PTR pgptr, PAGE_TYPE ptype)
Definition: page_buffer.c:4847
#define EHASH_OVERFLOW_THRESHOLD
static int ehash_initialize_dir_new_page(THREAD_ENTRY *thread_p, PAGE_PTR page_p, void *args)
void log_sysop_commit(THREAD_ENTRY *thread_p)
Definition: log_manager.c:3895
static char * ehash_write_ehid_to_record(char *rec_p, EHID *ehid_p)
SCAN_CODE spage_next_record(PAGE_PTR page_p, PGSLOTID *out_slot_id_p, RECDES *record_descriptor_p, int is_peeking)
int i
Definition: dynamic_load.c:954
int spage_get_free_space(THREAD_ENTRY *thread_p, PAGE_PTR page_p)
Definition: slotted_page.c:898
EH_SEARCH ehash_search(THREAD_ENTRY *thread_p, EHID *ehid_p, void *key_p, OID *value_p)
int spage_slot_size(void)
Definition: slotted_page.c:827
INT16 type
void db_date_decode(const DB_DATE *date, int *monthp, int *dayp, int *yearp)
Definition: db_date.c:338
#define NULL_VOLID
#define SP_ERROR
Definition: slotted_page.h:49
static char * ehash_write_oid_to_record(char *rec_p, OID *oid_p)
#define ER_EH_UNKNOWN_KEY
Definition: error_code.h:112
unsigned int mht_2strhash(const void *key, const unsigned int ht_size)
Definition: memory_hash.c:466
int ehash_rv_delete_redo(THREAD_ENTRY *thread_p, LOG_RCV *recv_p)
static int ehash_apply_each(THREAD_ENTRY *thread_p, EHID *ehid, RECDES *recdes, DB_TYPE key_type, char *bucrec_ptr, OID *assoc_value, int *out_apply_error, int(*apply_function)(THREAD_ENTRY *thread_p, void *key, void *data, void *args), void *args)
short volid
Definition: dbtype_def.h:887
static EHASH_RESULT ehash_insert_bucket_after_extend_if_need(THREAD_ENTRY *thread_p, EHID *ehid, PAGE_PTR dir_Rpgptr, EHASH_DIR_HEADER *dir_header, VPID *buc_vpid, void *key, EHASH_HASH_KEY hash_key, int lock_type, bool is_temp, OID *value_ptr, VPID *existing_ovf_vpid)
INT32 PAGEID
static PAGE_PTR ehash_fix_old_page(THREAD_ENTRY *thread_p, const VFID *vfid, const VPID *vpid, PGBUF_LATCH_MODE mode)
#define EHASH_NUM_FIRST_PAGES
#define DONT_FREE
Definition: page_buffer.h:41
DB_TIMESTAMP DB_UTIME
Definition: dbtype_def.h:761
static char * ehash_read_oid_from_record(char *rec_p, OID *oid_p)
#define SP_DOESNT_FIT
Definition: slotted_page.h:51
void db_time_decode(DB_TIME *timeval, int *hourp, int *minutep, int *secondp)
Definition: db_date.c:432
int overflow_get_length(THREAD_ENTRY *thread_p, const VPID *ovf_vpid)
#define PEEK
Definition: file_io.h:74
int overflow_insert(THREAD_ENTRY *thread_p, const VFID *ovf_vfid, VPID *ovf_vpid, RECDES *recdes, FILE_TYPE file_type)
Definition: overflow_file.c:95
#define VPID_SET_NULL(vpid_ptr)
Definition: dbtype_def.h:906
static int ehash_expand_directory(THREAD_ENTRY *thread_p, EHID *ehid, int new_depth, bool is_temp)
int file_is_temp(THREAD_ENTRY *thread_p, const VFID *vfid, bool *is_temp)
static int ehash_find_bucket_vpid(THREAD_ENTRY *thread_p, EHID *ehid, EHASH_DIR_HEADER *dir_header, int location, PGBUF_LATCH_MODE latch, VPID *out_vpid)
#define VFID_SET_NULL(vfid_ptr)
Definition: file_manager.h:65
const char ** p
Definition: dynamic_load.c:945
static EHASH_HASH_KEY ehash_hash_string_type(char *key, char *orig_key)
#define CLEARBIT(word, pos)
unsigned int time
Definition: dbtype_def.h:777
int spage_insert_at(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES *record_descriptor_p)
const VPID * overflow_delete(THREAD_ENTRY *thread_p, const VFID *ovf_vfid, const VPID *ovf_vpid)
int spage_insert_for_recovery(THREAD_ENTRY *thread_p, PAGE_PTR page_p, PGSLOTID slot_id, RECDES *record_descriptor_p)
void clear(cub_regex_object *&regex, char *&pattern)