~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to storage/innobase/page/page0cur.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/************************************************************************
 
2
The page cursor
 
3
 
 
4
(c) 1994-1996 Innobase Oy
 
5
 
 
6
Created 10/4/1994 Heikki Tuuri
 
7
*************************************************************************/
 
8
 
 
9
#include "page0cur.h"
 
10
#ifdef UNIV_NONINL
 
11
#include "page0cur.ic"
 
12
#endif
 
13
 
 
14
#include "rem0cmp.h"
 
15
#include "mtr0log.h"
 
16
#include "log0recv.h"
 
17
#include "rem0cmp.h"
 
18
#include "srv0srv.h"
 
19
#include "ut0ut.h"
 
20
 
 
21
static ulint    page_rnd        = 976722341;
 
22
 
 
23
#ifdef PAGE_CUR_ADAPT
 
24
# ifdef UNIV_SEARCH_PERF_STAT
 
25
ulint   page_cur_short_succ     = 0;
 
26
# endif /* UNIV_SEARCH_PERF_STAT */
 
27
 
 
28
/***********************************************************************
 
29
This is a linear congruential generator PRNG. Returns a pseudo random
 
30
number between 0 and 2^64-1 inclusive. The formula and the constants
 
31
being used are:
 
32
X[n+1] = (a * X[n] + c) mod m
 
33
where:
 
34
X[0] = ut_usectime()
 
35
a = 1103515245 (3^5 * 5 * 7 * 129749)
 
36
c = 12345 (3 * 5 * 823)
 
37
m = 18446744073709551616 (2^64)
 
38
*/
 
39
#define LCG_a   1103515245
 
40
#define LCG_c   12345
 
41
static
 
42
unsigned long long
 
43
page_cur_lcg_prng()
 
44
/*===============*/
 
45
                        /* out: number between 0 and 2^64-1 */
 
46
{
 
47
        static unsigned long long lcg_current = 0;
 
48
        static ibool            initialized = FALSE;
 
49
        ulint                   time_sec;
 
50
        ulint                   time_ms;
 
51
 
 
52
        if (!initialized) {
 
53
                ut_usectime(&time_sec, &time_ms);
 
54
                lcg_current = (unsigned long long) (time_sec * 1000000
 
55
                                                    + time_ms);
 
56
                initialized = TRUE;
 
57
        }
 
58
 
 
59
        /* no need to "% 2^64" explicitly because lcg_current is
 
60
        64 bit and this will be done anyway */
 
61
        lcg_current = LCG_a * lcg_current + LCG_c;
 
62
 
 
63
        return(lcg_current);
 
64
}
 
65
 
 
66
/********************************************************************
 
67
Tries a search shortcut based on the last insert. */
 
68
UNIV_INLINE
 
69
ibool
 
70
page_cur_try_search_shortcut(
 
71
/*=========================*/
 
72
                                /* out: TRUE on success */
 
73
        page_t*         page,   /* in: index page */
 
74
        dict_index_t*   index,  /* in: record descriptor */
 
75
        dtuple_t*       tuple,  /* in: data tuple */
 
76
        ulint*          iup_matched_fields,
 
77
                                /* in/out: already matched fields in upper
 
78
                                limit record */
 
79
        ulint*          iup_matched_bytes,
 
80
                                /* in/out: already matched bytes in a field
 
81
                                not yet completely matched */
 
82
        ulint*          ilow_matched_fields,
 
83
                                /* in/out: already matched fields in lower
 
84
                                limit record */
 
85
        ulint*          ilow_matched_bytes,
 
86
                                /* in/out: already matched bytes in a field
 
87
                                not yet completely matched */
 
88
        page_cur_t*     cursor) /* out: page cursor */
 
89
{
 
90
        rec_t*  rec;
 
91
        rec_t*  next_rec;
 
92
        ulint   low_match;
 
93
        ulint   low_bytes;
 
94
        ulint   up_match;
 
95
        ulint   up_bytes;
 
96
#ifdef UNIV_SEARCH_DEBUG
 
97
        page_cur_t cursor2;
 
98
#endif
 
99
        ibool           success         = FALSE;
 
100
        mem_heap_t*     heap            = NULL;
 
101
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
102
        ulint*          offsets         = offsets_;
 
103
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
104
 
 
105
        ut_ad(dtuple_check_typed(tuple));
 
106
 
 
107
        rec = page_header_get_ptr(page, PAGE_LAST_INSERT);
 
108
        offsets = rec_get_offsets(rec, index, offsets,
 
109
                                  dtuple_get_n_fields(tuple), &heap);
 
110
 
 
111
        ut_ad(rec);
 
112
        ut_ad(page_rec_is_user_rec(rec));
 
113
 
 
114
        ut_pair_min(&low_match, &low_bytes,
 
115
                    *ilow_matched_fields, *ilow_matched_bytes,
 
116
                    *iup_matched_fields, *iup_matched_bytes);
 
117
 
 
118
        up_match = low_match;
 
119
        up_bytes = low_bytes;
 
120
 
 
121
        if (page_cmp_dtuple_rec_with_match(tuple, rec, offsets,
 
122
                                           &low_match, &low_bytes) < 0) {
 
123
                goto exit_func;
 
124
        }
 
125
 
 
126
        next_rec = page_rec_get_next(rec);
 
127
        offsets = rec_get_offsets(next_rec, index, offsets,
 
128
                                  dtuple_get_n_fields(tuple), &heap);
 
129
 
 
130
        if (page_cmp_dtuple_rec_with_match(tuple, next_rec, offsets,
 
131
                                           &up_match, &up_bytes) >= 0) {
 
132
                goto exit_func;
 
133
        }
 
134
 
 
135
        cursor->rec = rec;
 
136
 
 
137
#ifdef UNIV_SEARCH_DEBUG
 
138
        page_cur_search_with_match(page, index, tuple, PAGE_CUR_DBG,
 
139
                                   iup_matched_fields,
 
140
                                   iup_matched_bytes,
 
141
                                   ilow_matched_fields,
 
142
                                   ilow_matched_bytes,
 
143
                                   &cursor2);
 
144
        ut_a(cursor2.rec == cursor->rec);
 
145
 
 
146
        if (next_rec != page_get_supremum_rec(page)) {
 
147
 
 
148
                ut_a(*iup_matched_fields == up_match);
 
149
                ut_a(*iup_matched_bytes == up_bytes);
 
150
        }
 
151
 
 
152
        ut_a(*ilow_matched_fields == low_match);
 
153
        ut_a(*ilow_matched_bytes == low_bytes);
 
154
#endif
 
155
        if (!page_rec_is_supremum(next_rec)) {
 
156
 
 
157
                *iup_matched_fields = up_match;
 
158
                *iup_matched_bytes = up_bytes;
 
159
        }
 
160
 
 
161
        *ilow_matched_fields = low_match;
 
162
        *ilow_matched_bytes = low_bytes;
 
163
 
 
164
#ifdef UNIV_SEARCH_PERF_STAT
 
165
        page_cur_short_succ++;
 
166
#endif
 
167
        success = TRUE;
 
168
exit_func:
 
169
        if (UNIV_LIKELY_NULL(heap)) {
 
170
                mem_heap_free(heap);
 
171
        }
 
172
        return(success);
 
173
}
 
174
 
 
175
#endif
 
176
 
 
177
#ifdef PAGE_CUR_LE_OR_EXTENDS
 
178
/********************************************************************
 
179
Checks if the nth field in a record is a character type field which extends
 
180
the nth field in tuple, i.e., the field is longer or equal in length and has
 
181
common first characters. */
 
182
static
 
183
ibool
 
184
page_cur_rec_field_extends(
 
185
/*=======================*/
 
186
                                /* out: TRUE if rec field
 
187
                                extends tuple field */
 
188
        dtuple_t*       tuple,  /* in: data tuple */
 
189
        rec_t*          rec,    /* in: record */
 
190
        const ulint*    offsets,/* in: array returned by rec_get_offsets() */
 
191
        ulint           n)      /* in: compare nth field */
 
192
{
 
193
        dtype_t* type;
 
194
        dfield_t* dfield;
 
195
        byte*     rec_f;
 
196
        ulint     rec_f_len;
 
197
 
 
198
        ut_ad(rec_offs_validate(rec, NULL, offsets));
 
199
        dfield = dtuple_get_nth_field(tuple, n);
 
200
 
 
201
        type = dfield_get_type(dfield);
 
202
 
 
203
        rec_f = rec_get_nth_field(rec, offsets, n, &rec_f_len);
 
204
 
 
205
        if (type->mtype == DATA_VARCHAR
 
206
            || type->mtype == DATA_CHAR
 
207
            || type->mtype == DATA_FIXBINARY
 
208
            || type->mtype == DATA_BINARY
 
209
            || type->mtype == DATA_BLOB
 
210
            || type->mtype == DATA_VARMYSQL
 
211
            || type->mtype == DATA_MYSQL) {
 
212
 
 
213
                if (dfield_get_len(dfield) != UNIV_SQL_NULL
 
214
                    && rec_f_len != UNIV_SQL_NULL
 
215
                    && rec_f_len >= dfield_get_len(dfield)
 
216
                    && !cmp_data_data_slow(type,
 
217
                                           dfield_get_data(dfield),
 
218
                                           dfield_get_len(dfield),
 
219
                                           rec_f, dfield_get_len(dfield))) {
 
220
 
 
221
                        return(TRUE);
 
222
                }
 
223
        }
 
224
 
 
225
        return(FALSE);
 
226
}
 
227
#endif /* PAGE_CUR_LE_OR_EXTENDS */
 
228
 
 
229
/********************************************************************
 
230
Searches the right position for a page cursor. */
 
231
 
 
232
void
 
233
page_cur_search_with_match(
 
234
/*=======================*/
 
235
        page_t*         page,   /* in: index page */
 
236
        dict_index_t*   index,  /* in: record descriptor */
 
237
        dtuple_t*       tuple,  /* in: data tuple */
 
238
        ulint           mode,   /* in: PAGE_CUR_L, PAGE_CUR_LE, PAGE_CUR_G,
 
239
                                or PAGE_CUR_GE */
 
240
        ulint*          iup_matched_fields,
 
241
                                /* in/out: already matched fields in upper
 
242
                                limit record */
 
243
        ulint*          iup_matched_bytes,
 
244
                                /* in/out: already matched bytes in a field
 
245
                                not yet completely matched */
 
246
        ulint*          ilow_matched_fields,
 
247
                                /* in/out: already matched fields in lower
 
248
                                limit record */
 
249
        ulint*          ilow_matched_bytes,
 
250
                                /* in/out: already matched bytes in a field
 
251
                                not yet completely matched */
 
252
        page_cur_t*     cursor) /* out: page cursor */
 
253
{
 
254
        ulint   up;
 
255
        ulint   low;
 
256
        ulint   mid;
 
257
        page_dir_slot_t* slot;
 
258
        rec_t*  up_rec;
 
259
        rec_t*  low_rec;
 
260
        rec_t*  mid_rec;
 
261
        ulint   up_matched_fields;
 
262
        ulint   up_matched_bytes;
 
263
        ulint   low_matched_fields;
 
264
        ulint   low_matched_bytes;
 
265
        ulint   cur_matched_fields;
 
266
        ulint   cur_matched_bytes;
 
267
        int     cmp;
 
268
#ifdef UNIV_SEARCH_DEBUG
 
269
        int     dbg_cmp;
 
270
        ulint   dbg_matched_fields;
 
271
        ulint   dbg_matched_bytes;
 
272
#endif
 
273
        mem_heap_t*     heap            = NULL;
 
274
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
275
        ulint*          offsets         = offsets_;
 
276
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
277
 
 
278
        ut_ad(page && tuple && iup_matched_fields && iup_matched_bytes
 
279
              && ilow_matched_fields && ilow_matched_bytes && cursor);
 
280
        ut_ad(dtuple_validate(tuple));
 
281
        ut_ad(dtuple_check_typed(tuple));
 
282
#ifdef UNIV_DEBUG
 
283
# ifdef PAGE_CUR_DBG
 
284
        if (mode != PAGE_CUR_DBG)
 
285
# endif /* PAGE_CUR_DBG */
 
286
# ifdef PAGE_CUR_LE_OR_EXTENDS
 
287
                if (mode != PAGE_CUR_LE_OR_EXTENDS)
 
288
# endif /* PAGE_CUR_LE_OR_EXTENDS */
 
289
                        ut_ad(mode == PAGE_CUR_L || mode == PAGE_CUR_LE
 
290
                              || mode == PAGE_CUR_G || mode == PAGE_CUR_GE);
 
291
#endif /* UNIV_DEBUG */
 
292
 
 
293
        page_check_dir(page);
 
294
 
 
295
#ifdef PAGE_CUR_ADAPT
 
296
        if ((page_header_get_field(page, PAGE_LEVEL) == 0)
 
297
            && (mode == PAGE_CUR_LE)
 
298
            && (page_header_get_field(page, PAGE_N_DIRECTION) > 3)
 
299
            && (page_header_get_ptr(page, PAGE_LAST_INSERT))
 
300
            && (page_header_get_field(page, PAGE_DIRECTION) == PAGE_RIGHT)) {
 
301
 
 
302
                if (page_cur_try_search_shortcut(
 
303
                            page, index, tuple,
 
304
                            iup_matched_fields, iup_matched_bytes,
 
305
                            ilow_matched_fields, ilow_matched_bytes,
 
306
                            cursor)) {
 
307
                        return;
 
308
                }
 
309
        }
 
310
# ifdef PAGE_CUR_DBG
 
311
        if (mode == PAGE_CUR_DBG) {
 
312
                mode = PAGE_CUR_LE;
 
313
        }
 
314
# endif
 
315
#endif
 
316
 
 
317
        /* The following flag does not work for non-latin1 char sets because
 
318
        cmp_full_field does not tell how many bytes matched */
 
319
#ifdef PAGE_CUR_LE_OR_EXTENDS
 
320
        ut_a(mode != PAGE_CUR_LE_OR_EXTENDS);
 
321
#endif /* PAGE_CUR_LE_OR_EXTENDS */
 
322
 
 
323
        /* If mode PAGE_CUR_G is specified, we are trying to position the
 
324
        cursor to answer a query of the form "tuple < X", where tuple is
 
325
        the input parameter, and X denotes an arbitrary physical record on
 
326
        the page. We want to position the cursor on the first X which
 
327
        satisfies the condition. */
 
328
 
 
329
        up_matched_fields  = *iup_matched_fields;
 
330
        up_matched_bytes   = *iup_matched_bytes;
 
331
        low_matched_fields = *ilow_matched_fields;
 
332
        low_matched_bytes  = *ilow_matched_bytes;
 
333
 
 
334
        /* Perform binary search. First the search is done through the page
 
335
        directory, after that as a linear search in the list of records
 
336
        owned by the upper limit directory slot. */
 
337
 
 
338
        low = 0;
 
339
        up = page_dir_get_n_slots(page) - 1;
 
340
 
 
341
        /* Perform binary search until the lower and upper limit directory
 
342
        slots come to the distance 1 of each other */
 
343
 
 
344
        while (up - low > 1) {
 
345
                mid = (low + up) / 2;
 
346
                slot = page_dir_get_nth_slot(page, mid);
 
347
                mid_rec = page_dir_slot_get_rec(slot);
 
348
 
 
349
                ut_pair_min(&cur_matched_fields, &cur_matched_bytes,
 
350
                            low_matched_fields, low_matched_bytes,
 
351
                            up_matched_fields, up_matched_bytes);
 
352
 
 
353
                offsets = rec_get_offsets(mid_rec, index, offsets,
 
354
                                          dtuple_get_n_fields_cmp(tuple),
 
355
                                          &heap);
 
356
 
 
357
                cmp = cmp_dtuple_rec_with_match(tuple, mid_rec, offsets,
 
358
                                                &cur_matched_fields,
 
359
                                                &cur_matched_bytes);
 
360
                if (UNIV_LIKELY(cmp > 0)) {
 
361
low_slot_match:
 
362
                        low = mid;
 
363
                        low_matched_fields = cur_matched_fields;
 
364
                        low_matched_bytes = cur_matched_bytes;
 
365
 
 
366
                } else if (UNIV_EXPECT(cmp, -1)) {
 
367
#ifdef PAGE_CUR_LE_OR_EXTENDS
 
368
                        if (mode == PAGE_CUR_LE_OR_EXTENDS
 
369
                            && page_cur_rec_field_extends(
 
370
                                    tuple, mid_rec, offsets,
 
371
                                    cur_matched_fields)) {
 
372
 
 
373
                                goto low_slot_match;
 
374
                        }
 
375
#endif /* PAGE_CUR_LE_OR_EXTENDS */
 
376
up_slot_match:
 
377
                        up = mid;
 
378
                        up_matched_fields = cur_matched_fields;
 
379
                        up_matched_bytes = cur_matched_bytes;
 
380
 
 
381
                } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE
 
382
#ifdef PAGE_CUR_LE_OR_EXTENDS
 
383
                           || mode == PAGE_CUR_LE_OR_EXTENDS
 
384
#endif /* PAGE_CUR_LE_OR_EXTENDS */
 
385
                           ) {
 
386
 
 
387
                        goto low_slot_match;
 
388
                } else {
 
389
 
 
390
                        goto up_slot_match;
 
391
                }
 
392
        }
 
393
 
 
394
        slot = page_dir_get_nth_slot(page, low);
 
395
        low_rec = page_dir_slot_get_rec(slot);
 
396
        slot = page_dir_get_nth_slot(page, up);
 
397
        up_rec = page_dir_slot_get_rec(slot);
 
398
 
 
399
        /* Perform linear search until the upper and lower records come to
 
400
        distance 1 of each other. */
 
401
 
 
402
        while (page_rec_get_next(low_rec) != up_rec) {
 
403
 
 
404
                mid_rec = page_rec_get_next(low_rec);
 
405
 
 
406
                ut_pair_min(&cur_matched_fields, &cur_matched_bytes,
 
407
                            low_matched_fields, low_matched_bytes,
 
408
                            up_matched_fields, up_matched_bytes);
 
409
 
 
410
                offsets = rec_get_offsets(mid_rec, index, offsets,
 
411
                                          dtuple_get_n_fields_cmp(tuple),
 
412
                                          &heap);
 
413
 
 
414
                cmp = cmp_dtuple_rec_with_match(tuple, mid_rec, offsets,
 
415
                                                &cur_matched_fields,
 
416
                                                &cur_matched_bytes);
 
417
                if (UNIV_LIKELY(cmp > 0)) {
 
418
low_rec_match:
 
419
                        low_rec = mid_rec;
 
420
                        low_matched_fields = cur_matched_fields;
 
421
                        low_matched_bytes = cur_matched_bytes;
 
422
 
 
423
                } else if (UNIV_EXPECT(cmp, -1)) {
 
424
#ifdef PAGE_CUR_LE_OR_EXTENDS
 
425
                        if (mode == PAGE_CUR_LE_OR_EXTENDS
 
426
                            && page_cur_rec_field_extends(
 
427
                                    tuple, mid_rec, offsets,
 
428
                                    cur_matched_fields)) {
 
429
 
 
430
                                goto low_rec_match;
 
431
                        }
 
432
#endif /* PAGE_CUR_LE_OR_EXTENDS */
 
433
up_rec_match:
 
434
                        up_rec = mid_rec;
 
435
                        up_matched_fields = cur_matched_fields;
 
436
                        up_matched_bytes = cur_matched_bytes;
 
437
                } else if (mode == PAGE_CUR_G || mode == PAGE_CUR_LE
 
438
#ifdef PAGE_CUR_LE_OR_EXTENDS
 
439
                           || mode == PAGE_CUR_LE_OR_EXTENDS
 
440
#endif /* PAGE_CUR_LE_OR_EXTENDS */
 
441
                           ) {
 
442
 
 
443
                        goto low_rec_match;
 
444
                } else {
 
445
 
 
446
                        goto up_rec_match;
 
447
                }
 
448
        }
 
449
 
 
450
#ifdef UNIV_SEARCH_DEBUG
 
451
 
 
452
        /* Check that the lower and upper limit records have the
 
453
        right alphabetical order compared to tuple. */
 
454
        dbg_matched_fields = 0;
 
455
        dbg_matched_bytes = 0;
 
456
 
 
457
        offsets = rec_get_offsets(low_rec, index, offsets,
 
458
                                  ULINT_UNDEFINED, &heap);
 
459
        dbg_cmp = page_cmp_dtuple_rec_with_match(tuple, low_rec, offsets,
 
460
                                                 &dbg_matched_fields,
 
461
                                                 &dbg_matched_bytes);
 
462
        if (mode == PAGE_CUR_G) {
 
463
                ut_a(dbg_cmp >= 0);
 
464
        } else if (mode == PAGE_CUR_GE) {
 
465
                ut_a(dbg_cmp == 1);
 
466
        } else if (mode == PAGE_CUR_L) {
 
467
                ut_a(dbg_cmp == 1);
 
468
        } else if (mode == PAGE_CUR_LE) {
 
469
                ut_a(dbg_cmp >= 0);
 
470
        }
 
471
 
 
472
        if (low_rec != page_get_infimum_rec(page)) {
 
473
 
 
474
                ut_a(low_matched_fields == dbg_matched_fields);
 
475
                ut_a(low_matched_bytes == dbg_matched_bytes);
 
476
        }
 
477
 
 
478
        dbg_matched_fields = 0;
 
479
        dbg_matched_bytes = 0;
 
480
 
 
481
        offsets = rec_get_offsets(up_rec, index, offsets,
 
482
                                  ULINT_UNDEFINED, &heap);
 
483
        dbg_cmp = page_cmp_dtuple_rec_with_match(tuple, up_rec, offsets,
 
484
                                                 &dbg_matched_fields,
 
485
                                                 &dbg_matched_bytes);
 
486
        if (mode == PAGE_CUR_G) {
 
487
                ut_a(dbg_cmp == -1);
 
488
        } else if (mode == PAGE_CUR_GE) {
 
489
                ut_a(dbg_cmp <= 0);
 
490
        } else if (mode == PAGE_CUR_L) {
 
491
                ut_a(dbg_cmp <= 0);
 
492
        } else if (mode == PAGE_CUR_LE) {
 
493
                ut_a(dbg_cmp == -1);
 
494
        }
 
495
 
 
496
        if (up_rec != page_get_supremum_rec(page)) {
 
497
 
 
498
                ut_a(up_matched_fields == dbg_matched_fields);
 
499
                ut_a(up_matched_bytes == dbg_matched_bytes);
 
500
        }
 
501
#endif
 
502
        if (mode <= PAGE_CUR_GE) {
 
503
                cursor->rec = up_rec;
 
504
        } else {
 
505
                cursor->rec = low_rec;
 
506
        }
 
507
 
 
508
        *iup_matched_fields  = up_matched_fields;
 
509
        *iup_matched_bytes   = up_matched_bytes;
 
510
        *ilow_matched_fields = low_matched_fields;
 
511
        *ilow_matched_bytes  = low_matched_bytes;
 
512
        if (UNIV_LIKELY_NULL(heap)) {
 
513
                mem_heap_free(heap);
 
514
        }
 
515
}
 
516
 
 
517
/***************************************************************
 
518
Positions a page cursor on a randomly chosen user record on a page. If there
 
519
are no user records, sets the cursor on the infimum record. */
 
520
 
 
521
void
 
522
page_cur_open_on_rnd_user_rec(
 
523
/*==========================*/
 
524
        page_t*         page,   /* in: page */
 
525
        page_cur_t*     cursor) /* in/out: page cursor */
 
526
{
 
527
        ulint   rnd;
 
528
        rec_t*  rec;
 
529
 
 
530
        if (page_get_n_recs(page) == 0) {
 
531
                page_cur_position(page_get_infimum_rec(page), cursor);
 
532
 
 
533
                return;
 
534
        }
 
535
 
 
536
        if (srv_use_legacy_cardinality_algorithm) {
 
537
                page_rnd += 87584577;
 
538
 
 
539
                rnd = page_rnd % page_get_n_recs(page);
 
540
        } else {
 
541
                rnd = (ulint) (page_cur_lcg_prng() % page_get_n_recs(page));
 
542
        }
 
543
 
 
544
        rec = page_get_infimum_rec(page);
 
545
 
 
546
        rec = page_rec_get_next(rec);
 
547
 
 
548
        while (rnd > 0) {
 
549
                rec = page_rec_get_next(rec);
 
550
 
 
551
                rnd--;
 
552
        }
 
553
 
 
554
        page_cur_position(rec, cursor);
 
555
}
 
556
 
 
557
/***************************************************************
 
558
Writes the log record of a record insert on a page. */
 
559
static
 
560
void
 
561
page_cur_insert_rec_write_log(
 
562
/*==========================*/
 
563
        rec_t*          insert_rec,     /* in: inserted physical record */
 
564
        ulint           rec_size,       /* in: insert_rec size */
 
565
        rec_t*          cursor_rec,     /* in: record the
 
566
                                        cursor is pointing to */
 
567
        dict_index_t*   index,          /* in: record descriptor */
 
568
        mtr_t*          mtr)            /* in: mini-transaction handle */
 
569
{
 
570
        ulint   cur_rec_size;
 
571
        ulint   extra_size;
 
572
        ulint   cur_extra_size;
 
573
        ulint   min_rec_size;
 
574
        byte*   ins_ptr;
 
575
        byte*   cur_ptr;
 
576
        ulint   extra_info_yes;
 
577
        byte*   log_ptr;
 
578
        byte*   log_end;
 
579
        ulint   i;
 
580
        ulint   comp;
 
581
 
 
582
        ut_a(rec_size < UNIV_PAGE_SIZE);
 
583
        ut_ad(buf_frame_align(insert_rec) == buf_frame_align(cursor_rec));
 
584
        ut_ad(!page_rec_is_comp(insert_rec)
 
585
              == !dict_table_is_comp(index->table));
 
586
        comp = page_rec_is_comp(insert_rec);
 
587
 
 
588
        {
 
589
                mem_heap_t*     heap            = NULL;
 
590
                ulint           cur_offs_[REC_OFFS_NORMAL_SIZE];
 
591
                ulint           ins_offs_[REC_OFFS_NORMAL_SIZE];
 
592
 
 
593
                ulint*          cur_offs;
 
594
                ulint*          ins_offs;
 
595
 
 
596
                *cur_offs_ = (sizeof cur_offs_) / sizeof *cur_offs_;
 
597
                *ins_offs_ = (sizeof ins_offs_) / sizeof *ins_offs_;
 
598
 
 
599
                cur_offs = rec_get_offsets(cursor_rec, index, cur_offs_,
 
600
                                           ULINT_UNDEFINED, &heap);
 
601
                ins_offs = rec_get_offsets(insert_rec, index, ins_offs_,
 
602
                                           ULINT_UNDEFINED, &heap);
 
603
 
 
604
                extra_size = rec_offs_extra_size(ins_offs);
 
605
                cur_extra_size = rec_offs_extra_size(cur_offs);
 
606
                ut_ad(rec_size == rec_offs_size(ins_offs));
 
607
                cur_rec_size = rec_offs_size(cur_offs);
 
608
 
 
609
                if (UNIV_LIKELY_NULL(heap)) {
 
610
                        mem_heap_free(heap);
 
611
                }
 
612
        }
 
613
 
 
614
        ins_ptr = insert_rec - extra_size;
 
615
 
 
616
        i = 0;
 
617
 
 
618
        if (cur_extra_size == extra_size) {
 
619
                min_rec_size = ut_min(cur_rec_size, rec_size);
 
620
 
 
621
                cur_ptr = cursor_rec - cur_extra_size;
 
622
 
 
623
                /* Find out the first byte in insert_rec which differs from
 
624
                cursor_rec; skip the bytes in the record info */
 
625
 
 
626
                for (;;) {
 
627
                        if (i >= min_rec_size) {
 
628
 
 
629
                                break;
 
630
                        } else if (*ins_ptr == *cur_ptr) {
 
631
                                i++;
 
632
                                ins_ptr++;
 
633
                                cur_ptr++;
 
634
                        } else if ((i < extra_size)
 
635
                                   && (i >= extra_size
 
636
                                       - (comp
 
637
                                          ? REC_N_NEW_EXTRA_BYTES
 
638
                                          : REC_N_OLD_EXTRA_BYTES))) {
 
639
                                i = extra_size;
 
640
                                ins_ptr = insert_rec;
 
641
                                cur_ptr = cursor_rec;
 
642
                        } else {
 
643
                                break;
 
644
                        }
 
645
                }
 
646
        }
 
647
 
 
648
        if (mtr_get_log_mode(mtr) != MTR_LOG_SHORT_INSERTS) {
 
649
 
 
650
                log_ptr = mlog_open_and_write_index(mtr, insert_rec, index,
 
651
                                                    comp
 
652
                                                    ? MLOG_COMP_REC_INSERT
 
653
                                                    : MLOG_REC_INSERT,
 
654
                                                    2 + 5 + 1 + 5 + 5
 
655
                                                    + MLOG_BUF_MARGIN);
 
656
 
 
657
                if (!log_ptr) {
 
658
                        /* Logging in mtr is switched off during crash
 
659
                        recovery: in that case mlog_open returns NULL */
 
660
                        return;
 
661
                }
 
662
 
 
663
                log_end = &log_ptr[2 + 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN];
 
664
                /* Write the cursor rec offset as a 2-byte ulint */
 
665
                mach_write_to_2(log_ptr, cursor_rec
 
666
                                - buf_frame_align(cursor_rec));
 
667
                log_ptr += 2;
 
668
        } else {
 
669
                log_ptr = mlog_open(mtr, 5 + 1 + 5 + 5 + MLOG_BUF_MARGIN);
 
670
                if (!log_ptr) {
 
671
                        /* Logging in mtr is switched off during crash
 
672
                        recovery: in that case mlog_open returns NULL */
 
673
                        return;
 
674
                }
 
675
                log_end = &log_ptr[5 + 1 + 5 + 5 + MLOG_BUF_MARGIN];
 
676
        }
 
677
 
 
678
        if ((rec_get_info_and_status_bits(insert_rec, comp)
 
679
             != rec_get_info_and_status_bits(cursor_rec, comp))
 
680
            || (extra_size != cur_extra_size)
 
681
            || (rec_size != cur_rec_size)) {
 
682
 
 
683
                extra_info_yes = 1;
 
684
        } else {
 
685
                extra_info_yes = 0;
 
686
        }
 
687
 
 
688
        /* Write the record end segment length and the extra info storage
 
689
        flag */
 
690
        log_ptr += mach_write_compressed(log_ptr, 2 * (rec_size - i)
 
691
                                         + extra_info_yes);
 
692
        if (extra_info_yes) {
 
693
                /* Write the info bits */
 
694
                mach_write_to_1(log_ptr,
 
695
                                rec_get_info_and_status_bits(insert_rec,
 
696
                                                             comp));
 
697
                log_ptr++;
 
698
 
 
699
                /* Write the record origin offset */
 
700
                log_ptr += mach_write_compressed(log_ptr, extra_size);
 
701
 
 
702
                /* Write the mismatch index */
 
703
                log_ptr += mach_write_compressed(log_ptr, i);
 
704
 
 
705
                ut_a(i < UNIV_PAGE_SIZE);
 
706
                ut_a(extra_size < UNIV_PAGE_SIZE);
 
707
        }
 
708
 
 
709
        /* Write to the log the inserted index record end segment which
 
710
        differs from the cursor record */
 
711
 
 
712
        rec_size -= i;
 
713
 
 
714
        if (log_ptr + rec_size <= log_end) {
 
715
                memcpy(log_ptr, ins_ptr, rec_size);
 
716
                mlog_close(mtr, log_ptr + rec_size);
 
717
        } else {
 
718
                mlog_close(mtr, log_ptr);
 
719
                ut_a(rec_size < UNIV_PAGE_SIZE);
 
720
                mlog_catenate_string(mtr, ins_ptr, rec_size);
 
721
        }
 
722
}
 
723
 
 
724
/***************************************************************
 
725
Parses a log record of a record insert on a page. */
 
726
 
 
727
byte*
 
728
page_cur_parse_insert_rec(
 
729
/*======================*/
 
730
                                /* out: end of log record or NULL */
 
731
        ibool           is_short,/* in: TRUE if short inserts */
 
732
        byte*           ptr,    /* in: buffer */
 
733
        byte*           end_ptr,/* in: buffer end */
 
734
        dict_index_t*   index,  /* in: record descriptor */
 
735
        page_t*         page,   /* in: page or NULL */
 
736
        mtr_t*          mtr)    /* in: mtr or NULL */
 
737
{
 
738
        ulint   extra_info_yes;
 
739
        ulint   offset = 0; /* remove warning */
 
740
        ulint   origin_offset;
 
741
        ulint   end_seg_len;
 
742
        ulint   mismatch_index;
 
743
        rec_t*  cursor_rec;
 
744
        byte    buf1[1024];
 
745
        byte*   buf;
 
746
        byte*   ptr2 = ptr;
 
747
        ulint   info_and_status_bits = 0; /* remove warning */
 
748
        page_cur_t cursor;
 
749
        mem_heap_t*     heap            = NULL;
 
750
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
751
        ulint*          offsets         = offsets_;
 
752
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
753
 
 
754
        if (!is_short) {
 
755
                /* Read the cursor rec offset as a 2-byte ulint */
 
756
 
 
757
                if (end_ptr < ptr + 2) {
 
758
 
 
759
                        return(NULL);
 
760
                }
 
761
 
 
762
                offset = mach_read_from_2(ptr);
 
763
 
 
764
                if (offset >= UNIV_PAGE_SIZE) {
 
765
 
 
766
                        recv_sys->found_corrupt_log = TRUE;
 
767
 
 
768
                        return(NULL);
 
769
                }
 
770
 
 
771
                ptr += 2;
 
772
        }
 
773
 
 
774
        ptr = mach_parse_compressed(ptr, end_ptr, &end_seg_len);
 
775
 
 
776
        if (ptr == NULL) {
 
777
 
 
778
                return(NULL);
 
779
        }
 
780
 
 
781
        extra_info_yes = end_seg_len & 0x1UL;
 
782
        end_seg_len >>= 1;
 
783
 
 
784
        if (end_seg_len >= UNIV_PAGE_SIZE) {
 
785
                recv_sys->found_corrupt_log = TRUE;
 
786
 
 
787
                return(NULL);
 
788
        }
 
789
 
 
790
        if (extra_info_yes) {
 
791
                /* Read the info bits */
 
792
 
 
793
                if (end_ptr < ptr + 1) {
 
794
 
 
795
                        return(NULL);
 
796
                }
 
797
 
 
798
                info_and_status_bits = mach_read_from_1(ptr);
 
799
                ptr++;
 
800
 
 
801
                ptr = mach_parse_compressed(ptr, end_ptr, &origin_offset);
 
802
 
 
803
                if (ptr == NULL) {
 
804
 
 
805
                        return(NULL);
 
806
                }
 
807
 
 
808
                ut_a(origin_offset < UNIV_PAGE_SIZE);
 
809
 
 
810
                ptr = mach_parse_compressed(ptr, end_ptr, &mismatch_index);
 
811
 
 
812
                if (ptr == NULL) {
 
813
 
 
814
                        return(NULL);
 
815
                }
 
816
 
 
817
                ut_a(mismatch_index < UNIV_PAGE_SIZE);
 
818
        }
 
819
 
 
820
        if (end_ptr < ptr + end_seg_len) {
 
821
 
 
822
                return(NULL);
 
823
        }
 
824
 
 
825
        if (page == NULL) {
 
826
 
 
827
                return(ptr + end_seg_len);
 
828
        }
 
829
 
 
830
        ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
 
831
 
 
832
        /* Read from the log the inserted index record end segment which
 
833
        differs from the cursor record */
 
834
 
 
835
        if (is_short) {
 
836
                cursor_rec = page_rec_get_prev(page_get_supremum_rec(page));
 
837
        } else {
 
838
                cursor_rec = page + offset;
 
839
        }
 
840
 
 
841
        offsets = rec_get_offsets(cursor_rec, index, offsets,
 
842
                                  ULINT_UNDEFINED, &heap);
 
843
 
 
844
        if (extra_info_yes == 0) {
 
845
                info_and_status_bits = rec_get_info_and_status_bits(
 
846
                        cursor_rec, page_is_comp(page));
 
847
                origin_offset = rec_offs_extra_size(offsets);
 
848
                mismatch_index = rec_offs_size(offsets) - end_seg_len;
 
849
        }
 
850
 
 
851
        if (mismatch_index + end_seg_len < sizeof buf1) {
 
852
                buf = buf1;
 
853
        } else {
 
854
                buf = mem_alloc(mismatch_index + end_seg_len);
 
855
        }
 
856
 
 
857
        /* Build the inserted record to buf */
 
858
 
 
859
        if (mismatch_index >= UNIV_PAGE_SIZE) {
 
860
                fprintf(stderr,
 
861
                        "Is short %lu, info_and_status_bits %lu, offset %lu, "
 
862
                        "o_offset %lu\n"
 
863
                        "mismatch index %lu, end_seg_len %lu\n"
 
864
                        "parsed len %lu\n",
 
865
                        (ulong) is_short, (ulong) info_and_status_bits,
 
866
                        (ulong) offset,
 
867
                        (ulong) origin_offset,
 
868
                        (ulong) mismatch_index, (ulong) end_seg_len,
 
869
                        (ulong) (ptr - ptr2));
 
870
 
 
871
                fputs("Dump of 300 bytes of log:\n", stderr);
 
872
                ut_print_buf(stderr, ptr2, 300);
 
873
 
 
874
                buf_page_print(page);
 
875
 
 
876
                ut_error;
 
877
        }
 
878
 
 
879
        ut_memcpy(buf, rec_get_start(cursor_rec, offsets), mismatch_index);
 
880
        ut_memcpy(buf + mismatch_index, ptr, end_seg_len);
 
881
 
 
882
        rec_set_info_and_status_bits(buf + origin_offset, page_is_comp(page),
 
883
                                     info_and_status_bits);
 
884
 
 
885
        page_cur_position(cursor_rec, &cursor);
 
886
 
 
887
        offsets = rec_get_offsets(buf + origin_offset, index, offsets,
 
888
                                  ULINT_UNDEFINED, &heap);
 
889
        page_cur_rec_insert(&cursor, buf + origin_offset, index, offsets, mtr);
 
890
 
 
891
        if (buf != buf1) {
 
892
 
 
893
                mem_free(buf);
 
894
        }
 
895
 
 
896
        if (UNIV_LIKELY_NULL(heap)) {
 
897
                mem_heap_free(heap);
 
898
        }
 
899
 
 
900
        return(ptr + end_seg_len);
 
901
}
 
902
 
 
903
/***************************************************************
 
904
Inserts a record next to page cursor. Returns pointer to inserted record if
 
905
succeed, i.e., enough space available, NULL otherwise. The record to be
 
906
inserted can be in a data tuple or as a physical record. The other parameter
 
907
must then be NULL. The cursor stays at the same position. */
 
908
 
 
909
rec_t*
 
910
page_cur_insert_rec_low(
 
911
/*====================*/
 
912
                                /* out: pointer to record if succeed, NULL
 
913
                                otherwise */
 
914
        page_cur_t*     cursor, /* in: a page cursor */
 
915
        dtuple_t*       tuple,  /* in: pointer to a data tuple or NULL */
 
916
        dict_index_t*   index,  /* in: record descriptor */
 
917
        rec_t*          rec,    /* in: pointer to a physical record or NULL */
 
918
        ulint*          offsets,/* in: rec_get_offsets(rec, index) or NULL */
 
919
        mtr_t*          mtr)    /* in: mini-transaction handle */
 
920
{
 
921
        byte*           insert_buf      = NULL;
 
922
        ulint           rec_size;
 
923
        byte*           page;           /* the relevant page */
 
924
        rec_t*          last_insert;    /* cursor position at previous
 
925
                                        insert */
 
926
        rec_t*          insert_rec;     /* inserted record */
 
927
        ulint           heap_no;        /* heap number of the inserted
 
928
                                        record */
 
929
        rec_t*          current_rec;    /* current record after which the
 
930
                                        new record is inserted */
 
931
        rec_t*          next_rec;       /* next record after current before
 
932
                                        the insertion */
 
933
        ulint           owner_slot;     /* the slot which owns the
 
934
                                        inserted record */
 
935
        rec_t*          owner_rec;
 
936
        ulint           n_owned;
 
937
        mem_heap_t*     heap            = NULL;
 
938
        ulint           comp;
 
939
 
 
940
        ut_ad(cursor && mtr);
 
941
        ut_ad(tuple || rec);
 
942
        ut_ad(!(tuple && rec));
 
943
        ut_ad(rec || dtuple_check_typed(tuple));
 
944
 
 
945
        page = page_cur_get_page(cursor);
 
946
        comp = page_is_comp(page);
 
947
        ut_ad(dict_table_is_comp(index->table) == !!comp);
 
948
 
 
949
        ut_ad(cursor->rec != page_get_supremum_rec(page));
 
950
 
 
951
        /* 1. Get the size of the physical record in the page */
 
952
        if (tuple != NULL) {
 
953
                rec_size = rec_get_converted_size(index, tuple);
 
954
        } else {
 
955
                if (!offsets) {
 
956
                        offsets = rec_get_offsets(rec, index, offsets,
 
957
                                                  ULINT_UNDEFINED, &heap);
 
958
                }
 
959
                ut_ad(rec_offs_validate(rec, index, offsets));
 
960
                rec_size = rec_offs_size(offsets);
 
961
        }
 
962
 
 
963
        /* 2. Try to find suitable space from page memory management */
 
964
        insert_buf = page_mem_alloc(page, rec_size, index, &heap_no);
 
965
 
 
966
        if (insert_buf == NULL) {
 
967
                if (UNIV_LIKELY_NULL(heap)) {
 
968
                        mem_heap_free(heap);
 
969
                }
 
970
                return(NULL);
 
971
        }
 
972
 
 
973
        /* 3. Create the record */
 
974
        if (tuple != NULL) {
 
975
                insert_rec = rec_convert_dtuple_to_rec(insert_buf,
 
976
                                                       index, tuple);
 
977
                offsets = rec_get_offsets(insert_rec, index, offsets,
 
978
                                          ULINT_UNDEFINED, &heap);
 
979
        } else {
 
980
                insert_rec = rec_copy(insert_buf, rec, offsets);
 
981
                ut_ad(rec_offs_validate(rec, index, offsets));
 
982
                rec_offs_make_valid(insert_rec, index, offsets);
 
983
        }
 
984
 
 
985
        ut_ad(insert_rec);
 
986
        ut_ad(rec_size == rec_offs_size(offsets));
 
987
 
 
988
        /* 4. Insert the record in the linked list of records */
 
989
        current_rec = cursor->rec;
 
990
 
 
991
        ut_ad(!comp || rec_get_status(current_rec) <= REC_STATUS_INFIMUM);
 
992
        ut_ad(!comp || rec_get_status(insert_rec) < REC_STATUS_INFIMUM);
 
993
 
 
994
        next_rec = page_rec_get_next(current_rec);
 
995
        ut_ad(!comp || rec_get_status(next_rec) != REC_STATUS_INFIMUM);
 
996
        page_rec_set_next(insert_rec, next_rec);
 
997
        page_rec_set_next(current_rec, insert_rec);
 
998
 
 
999
        page_header_set_field(page, PAGE_N_RECS, 1 + page_get_n_recs(page));
 
1000
 
 
1001
        /* 5. Set the n_owned field in the inserted record to zero,
 
1002
        and set the heap_no field */
 
1003
 
 
1004
        rec_set_n_owned(insert_rec, comp, 0);
 
1005
        rec_set_heap_no(insert_rec, comp, heap_no);
 
1006
 
 
1007
        /* 6. Update the last insertion info in page header */
 
1008
 
 
1009
        last_insert = page_header_get_ptr(page, PAGE_LAST_INSERT);
 
1010
        ut_ad(!last_insert || !comp
 
1011
              || rec_get_node_ptr_flag(last_insert)
 
1012
              == rec_get_node_ptr_flag(insert_rec));
 
1013
 
 
1014
        if (last_insert == NULL) {
 
1015
                page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
 
1016
                page_header_set_field(page, PAGE_N_DIRECTION, 0);
 
1017
 
 
1018
        } else if ((last_insert == current_rec)
 
1019
                   && (page_header_get_field(page, PAGE_DIRECTION)
 
1020
                       != PAGE_LEFT)) {
 
1021
 
 
1022
                page_header_set_field(page, PAGE_DIRECTION, PAGE_RIGHT);
 
1023
                page_header_set_field(page, PAGE_N_DIRECTION,
 
1024
                                      page_header_get_field(
 
1025
                                              page, PAGE_N_DIRECTION) + 1);
 
1026
 
 
1027
        } else if ((page_rec_get_next(insert_rec) == last_insert)
 
1028
                   && (page_header_get_field(page, PAGE_DIRECTION)
 
1029
                       != PAGE_RIGHT)) {
 
1030
 
 
1031
                page_header_set_field(page, PAGE_DIRECTION, PAGE_LEFT);
 
1032
                page_header_set_field(page, PAGE_N_DIRECTION,
 
1033
                                      page_header_get_field(
 
1034
                                              page, PAGE_N_DIRECTION) + 1);
 
1035
        } else {
 
1036
                page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
 
1037
                page_header_set_field(page, PAGE_N_DIRECTION, 0);
 
1038
        }
 
1039
 
 
1040
        page_header_set_ptr(page, PAGE_LAST_INSERT, insert_rec);
 
1041
 
 
1042
        /* 7. It remains to update the owner record. */
 
1043
 
 
1044
        owner_rec = page_rec_find_owner_rec(insert_rec);
 
1045
        n_owned = rec_get_n_owned(owner_rec, comp);
 
1046
        rec_set_n_owned(owner_rec, comp, n_owned + 1);
 
1047
 
 
1048
        /* 8. Now we have incremented the n_owned field of the owner
 
1049
        record. If the number exceeds PAGE_DIR_SLOT_MAX_N_OWNED,
 
1050
        we have to split the corresponding directory slot in two. */
 
1051
 
 
1052
        if (n_owned == PAGE_DIR_SLOT_MAX_N_OWNED) {
 
1053
                owner_slot = page_dir_find_owner_slot(owner_rec);
 
1054
                page_dir_split_slot(page, owner_slot);
 
1055
        }
 
1056
 
 
1057
        /* 9. Write log record of the insert */
 
1058
        page_cur_insert_rec_write_log(insert_rec, rec_size, current_rec,
 
1059
                                      index, mtr);
 
1060
 
 
1061
        if (UNIV_LIKELY_NULL(heap)) {
 
1062
                mem_heap_free(heap);
 
1063
        }
 
1064
        return(insert_rec);
 
1065
}
 
1066
 
 
1067
/**************************************************************
 
1068
Writes a log record of copying a record list end to a new created page. */
 
1069
UNIV_INLINE
 
1070
byte*
 
1071
page_copy_rec_list_to_created_page_write_log(
 
1072
/*=========================================*/
 
1073
                                /* out: 4-byte field where to
 
1074
                                write the log data length */
 
1075
        page_t*         page,   /* in: index page */
 
1076
        dict_index_t*   index,  /* in: record descriptor */
 
1077
        mtr_t*          mtr)    /* in: mtr */
 
1078
{
 
1079
        byte*   log_ptr;
 
1080
 
 
1081
        ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
 
1082
 
 
1083
        log_ptr = mlog_open_and_write_index(mtr, page, index,
 
1084
                                            page_is_comp(page)
 
1085
                                            ? MLOG_COMP_LIST_END_COPY_CREATED
 
1086
                                            : MLOG_LIST_END_COPY_CREATED, 4);
 
1087
        ut_a(log_ptr);
 
1088
        mlog_close(mtr, log_ptr + 4);
 
1089
 
 
1090
        return(log_ptr);
 
1091
}
 
1092
 
 
1093
/**************************************************************
 
1094
Parses a log record of copying a record list end to a new created page. */
 
1095
 
 
1096
byte*
 
1097
page_parse_copy_rec_list_to_created_page(
 
1098
/*=====================================*/
 
1099
                                /* out: end of log record or NULL */
 
1100
        byte*           ptr,    /* in: buffer */
 
1101
        byte*           end_ptr,/* in: buffer end */
 
1102
        dict_index_t*   index,  /* in: record descriptor */
 
1103
        page_t*         page,   /* in: page or NULL */
 
1104
        mtr_t*          mtr)    /* in: mtr or NULL */
 
1105
{
 
1106
        byte*   rec_end;
 
1107
        ulint   log_data_len;
 
1108
 
 
1109
        if (ptr + 4 > end_ptr) {
 
1110
 
 
1111
                return(NULL);
 
1112
        }
 
1113
 
 
1114
        log_data_len = mach_read_from_4(ptr);
 
1115
        ptr += 4;
 
1116
 
 
1117
        rec_end = ptr + log_data_len;
 
1118
 
 
1119
        if (rec_end > end_ptr) {
 
1120
 
 
1121
                return(NULL);
 
1122
        }
 
1123
 
 
1124
        if (!page) {
 
1125
 
 
1126
                return(rec_end);
 
1127
        }
 
1128
 
 
1129
        while (ptr < rec_end) {
 
1130
                ptr = page_cur_parse_insert_rec(TRUE, ptr, end_ptr,
 
1131
                                                index, page, mtr);
 
1132
        }
 
1133
 
 
1134
        ut_a(ptr == rec_end);
 
1135
 
 
1136
        page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
 
1137
        page_header_set_field(page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
 
1138
        page_header_set_field(page, PAGE_N_DIRECTION, 0);
 
1139
 
 
1140
        return(rec_end);
 
1141
}
 
1142
 
 
1143
/*****************************************************************
 
1144
Copies records from page to a newly created page, from a given record onward,
 
1145
including that record. Infimum and supremum records are not copied. */
 
1146
 
 
1147
void
 
1148
page_copy_rec_list_end_to_created_page(
 
1149
/*===================================*/
 
1150
        page_t*         new_page,       /* in: index page to copy to */
 
1151
        page_t*         page,           /* in: index page */
 
1152
        rec_t*          rec,            /* in: first record to copy */
 
1153
        dict_index_t*   index,          /* in: record descriptor */
 
1154
        mtr_t*          mtr)            /* in: mtr */
 
1155
{
 
1156
        page_dir_slot_t* slot = 0; /* remove warning */
 
1157
        byte*   heap_top;
 
1158
        rec_t*  insert_rec = 0; /* remove warning */
 
1159
        rec_t*  prev_rec;
 
1160
        ulint   count;
 
1161
        ulint   n_recs;
 
1162
        ulint   slot_index;
 
1163
        ulint   rec_size;
 
1164
        ulint   log_mode;
 
1165
        byte*   log_ptr;
 
1166
        ulint   log_data_len;
 
1167
        ulint           comp            = page_is_comp(page);
 
1168
        mem_heap_t*     heap            = NULL;
 
1169
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1170
        ulint*          offsets         = offsets_;
 
1171
        *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
1172
 
 
1173
        ut_ad(page_dir_get_n_heap(new_page) == 2);
 
1174
        ut_ad(page != new_page);
 
1175
        ut_ad(comp == page_is_comp(new_page));
 
1176
 
 
1177
        if (rec == page_get_infimum_rec(page)) {
 
1178
 
 
1179
                rec = page_rec_get_next(rec);
 
1180
        }
 
1181
 
 
1182
        if (rec == page_get_supremum_rec(page)) {
 
1183
 
 
1184
                return;
 
1185
        }
 
1186
 
 
1187
#ifdef UNIV_DEBUG
 
1188
        /* To pass the debug tests we have to set these dummy values
 
1189
        in the debug version */
 
1190
        page_dir_set_n_slots(new_page, UNIV_PAGE_SIZE / 2);
 
1191
        page_header_set_ptr(new_page, PAGE_HEAP_TOP,
 
1192
                            new_page + UNIV_PAGE_SIZE - 1);
 
1193
#endif
 
1194
 
 
1195
        log_ptr = page_copy_rec_list_to_created_page_write_log(new_page,
 
1196
                                                               index, mtr);
 
1197
 
 
1198
        log_data_len = dyn_array_get_data_size(&(mtr->log));
 
1199
 
 
1200
        /* Individual inserts are logged in a shorter form */
 
1201
 
 
1202
        log_mode = mtr_set_log_mode(mtr, MTR_LOG_SHORT_INSERTS);
 
1203
 
 
1204
        prev_rec = page_get_infimum_rec(new_page);
 
1205
        if (comp) {
 
1206
                heap_top = new_page + PAGE_NEW_SUPREMUM_END;
 
1207
        } else {
 
1208
                heap_top = new_page + PAGE_OLD_SUPREMUM_END;
 
1209
        }
 
1210
        count = 0;
 
1211
        slot_index = 0;
 
1212
        n_recs = 0;
 
1213
 
 
1214
        /* should be do ... until, comment by Jani */
 
1215
        while (rec != page_get_supremum_rec(page)) {
 
1216
                offsets = rec_get_offsets(rec, index, offsets,
 
1217
                                          ULINT_UNDEFINED, &heap);
 
1218
                insert_rec = rec_copy(heap_top, rec, offsets);
 
1219
 
 
1220
                rec_set_next_offs(prev_rec, comp, insert_rec - new_page);
 
1221
 
 
1222
                rec_set_n_owned(insert_rec, comp, 0);
 
1223
                rec_set_heap_no(insert_rec, comp, 2 + n_recs);
 
1224
 
 
1225
                rec_size = rec_offs_size(offsets);
 
1226
 
 
1227
                heap_top = heap_top + rec_size;
 
1228
 
 
1229
                ut_ad(heap_top < new_page + UNIV_PAGE_SIZE);
 
1230
 
 
1231
                count++;
 
1232
                n_recs++;
 
1233
 
 
1234
                if (count == (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2) {
 
1235
 
 
1236
                        slot_index++;
 
1237
 
 
1238
                        slot = page_dir_get_nth_slot(new_page, slot_index);
 
1239
 
 
1240
                        page_dir_slot_set_rec(slot, insert_rec);
 
1241
                        page_dir_slot_set_n_owned(slot, count);
 
1242
 
 
1243
                        count = 0;
 
1244
                }
 
1245
 
 
1246
                page_cur_insert_rec_write_log(insert_rec, rec_size, prev_rec,
 
1247
                                              index, mtr);
 
1248
                prev_rec = insert_rec;
 
1249
                rec = page_rec_get_next(rec);
 
1250
        }
 
1251
 
 
1252
        if ((slot_index > 0) && (count + 1
 
1253
                                 + (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2
 
1254
                                 <= PAGE_DIR_SLOT_MAX_N_OWNED)) {
 
1255
                /* We can merge the two last dir slots. This operation is
 
1256
                here to make this function imitate exactly the equivalent
 
1257
                task made using page_cur_insert_rec, which we use in database
 
1258
                recovery to reproduce the task performed by this function.
 
1259
                To be able to check the correctness of recovery, it is good
 
1260
                that it imitates exactly. */
 
1261
 
 
1262
                count += (PAGE_DIR_SLOT_MAX_N_OWNED + 1) / 2;
 
1263
 
 
1264
                page_dir_slot_set_n_owned(slot, 0);
 
1265
 
 
1266
                slot_index--;
 
1267
        }
 
1268
 
 
1269
        if (UNIV_LIKELY_NULL(heap)) {
 
1270
                mem_heap_free(heap);
 
1271
        }
 
1272
 
 
1273
        log_data_len = dyn_array_get_data_size(&(mtr->log)) - log_data_len;
 
1274
 
 
1275
        ut_a(log_data_len < 100 * UNIV_PAGE_SIZE);
 
1276
 
 
1277
        mach_write_to_4(log_ptr, log_data_len);
 
1278
 
 
1279
        rec_set_next_offs(insert_rec, comp,
 
1280
                          comp ? PAGE_NEW_SUPREMUM : PAGE_OLD_SUPREMUM);
 
1281
 
 
1282
        slot = page_dir_get_nth_slot(new_page, 1 + slot_index);
 
1283
 
 
1284
        page_dir_slot_set_rec(slot, page_get_supremum_rec(new_page));
 
1285
        page_dir_slot_set_n_owned(slot, count + 1);
 
1286
 
 
1287
        page_dir_set_n_slots(new_page, 2 + slot_index);
 
1288
        page_header_set_ptr(new_page, PAGE_HEAP_TOP, heap_top);
 
1289
        page_dir_set_n_heap(new_page, 2 + n_recs);
 
1290
        page_header_set_field(new_page, PAGE_N_RECS, n_recs);
 
1291
 
 
1292
        page_header_set_ptr(new_page, PAGE_LAST_INSERT, NULL);
 
1293
        page_header_set_field(new_page, PAGE_DIRECTION, PAGE_NO_DIRECTION);
 
1294
        page_header_set_field(new_page, PAGE_N_DIRECTION, 0);
 
1295
 
 
1296
        /* Restore the log mode */
 
1297
 
 
1298
        mtr_set_log_mode(mtr, log_mode);
 
1299
}
 
1300
 
 
1301
/***************************************************************
 
1302
Writes log record of a record delete on a page. */
 
1303
UNIV_INLINE
 
1304
void
 
1305
page_cur_delete_rec_write_log(
 
1306
/*==========================*/
 
1307
        rec_t*          rec,    /* in: record to be deleted */
 
1308
        dict_index_t*   index,  /* in: record descriptor */
 
1309
        mtr_t*          mtr)    /* in: mini-transaction handle */
 
1310
{
 
1311
        byte*   log_ptr;
 
1312
 
 
1313
        ut_ad(!!page_rec_is_comp(rec) == dict_table_is_comp(index->table));
 
1314
 
 
1315
        log_ptr = mlog_open_and_write_index(mtr, rec, index,
 
1316
                                            page_rec_is_comp(rec)
 
1317
                                            ? MLOG_COMP_REC_DELETE
 
1318
                                            : MLOG_REC_DELETE, 2);
 
1319
 
 
1320
        if (!log_ptr) {
 
1321
                /* Logging in mtr is switched off during crash recovery:
 
1322
                in that case mlog_open returns NULL */
 
1323
                return;
 
1324
        }
 
1325
 
 
1326
        /* Write the cursor rec offset as a 2-byte ulint */
 
1327
        mach_write_to_2(log_ptr, page_offset(rec));
 
1328
 
 
1329
        mlog_close(mtr, log_ptr + 2);
 
1330
}
 
1331
 
 
1332
/***************************************************************
 
1333
Parses log record of a record delete on a page. */
 
1334
 
 
1335
byte*
 
1336
page_cur_parse_delete_rec(
 
1337
/*======================*/
 
1338
                                /* out: pointer to record end or NULL */
 
1339
        byte*           ptr,    /* in: buffer */
 
1340
        byte*           end_ptr,/* in: buffer end */
 
1341
        dict_index_t*   index,  /* in: record descriptor */
 
1342
        page_t*         page,   /* in: page or NULL */
 
1343
        mtr_t*          mtr)    /* in: mtr or NULL */
 
1344
{
 
1345
        ulint           offset;
 
1346
        page_cur_t      cursor;
 
1347
 
 
1348
        if (end_ptr < ptr + 2) {
 
1349
 
 
1350
                return(NULL);
 
1351
        }
 
1352
 
 
1353
        /* Read the cursor rec offset as a 2-byte ulint */
 
1354
        offset = mach_read_from_2(ptr);
 
1355
        ptr += 2;
 
1356
 
 
1357
        ut_a(offset <= UNIV_PAGE_SIZE);
 
1358
 
 
1359
        if (page) {
 
1360
                mem_heap_t*     heap            = NULL;
 
1361
                ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1362
                rec_t*          rec             = page + offset;
 
1363
                *offsets_ = (sizeof offsets_) / sizeof *offsets_;
 
1364
 
 
1365
                page_cur_position(rec, &cursor);
 
1366
 
 
1367
                page_cur_delete_rec(&cursor, index,
 
1368
                                    rec_get_offsets(rec, index, offsets_,
 
1369
                                                    ULINT_UNDEFINED, &heap),
 
1370
                                    mtr);
 
1371
                if (UNIV_LIKELY_NULL(heap)) {
 
1372
                        mem_heap_free(heap);
 
1373
                }
 
1374
        }
 
1375
 
 
1376
        return(ptr);
 
1377
}
 
1378
 
 
1379
/***************************************************************
 
1380
Deletes a record at the page cursor. The cursor is moved to the next
 
1381
record after the deleted one. */
 
1382
 
 
1383
void
 
1384
page_cur_delete_rec(
 
1385
/*================*/
 
1386
        page_cur_t*     cursor, /* in: a page cursor */
 
1387
        dict_index_t*   index,  /* in: record descriptor */
 
1388
        const ulint*    offsets,/* in: rec_get_offsets(cursor->rec, index) */
 
1389
        mtr_t*          mtr)    /* in: mini-transaction handle */
 
1390
{
 
1391
        page_dir_slot_t* cur_dir_slot;
 
1392
        page_dir_slot_t* prev_slot;
 
1393
        page_t*         page;
 
1394
        rec_t*          current_rec;
 
1395
        rec_t*          prev_rec        = NULL;
 
1396
        rec_t*          next_rec;
 
1397
        ulint           cur_slot_no;
 
1398
        ulint           cur_n_owned;
 
1399
        rec_t*          rec;
 
1400
 
 
1401
        ut_ad(cursor && mtr);
 
1402
 
 
1403
        page = page_cur_get_page(cursor);
 
1404
        current_rec = cursor->rec;
 
1405
        ut_ad(rec_offs_validate(current_rec, index, offsets));
 
1406
        ut_ad(!!page_is_comp(page) == dict_table_is_comp(index->table));
 
1407
 
 
1408
        /* The record must not be the supremum or infimum record. */
 
1409
        ut_ad(current_rec != page_get_supremum_rec(page));
 
1410
        ut_ad(current_rec != page_get_infimum_rec(page));
 
1411
 
 
1412
        /* Save to local variables some data associated with current_rec */
 
1413
        cur_slot_no = page_dir_find_owner_slot(current_rec);
 
1414
        cur_dir_slot = page_dir_get_nth_slot(page, cur_slot_no);
 
1415
        cur_n_owned = page_dir_slot_get_n_owned(cur_dir_slot);
 
1416
 
 
1417
        /* 0. Write the log record */
 
1418
        page_cur_delete_rec_write_log(current_rec, index, mtr);
 
1419
 
 
1420
        /* 1. Reset the last insert info in the page header and increment
 
1421
        the modify clock for the frame */
 
1422
 
 
1423
        page_header_set_ptr(page, PAGE_LAST_INSERT, NULL);
 
1424
 
 
1425
        /* The page gets invalid for optimistic searches: increment the
 
1426
        frame modify clock */
 
1427
 
 
1428
        buf_frame_modify_clock_inc(page);
 
1429
 
 
1430
        /* 2. Find the next and the previous record. Note that the cursor is
 
1431
        left at the next record. */
 
1432
 
 
1433
        ut_ad(cur_slot_no > 0);
 
1434
        prev_slot = page_dir_get_nth_slot(page, cur_slot_no - 1);
 
1435
 
 
1436
        rec = page_dir_slot_get_rec(prev_slot);
 
1437
 
 
1438
        /* rec now points to the record of the previous directory slot. Look
 
1439
        for the immediate predecessor of current_rec in a loop. */
 
1440
 
 
1441
        while(current_rec != rec) {
 
1442
                prev_rec = rec;
 
1443
                rec = page_rec_get_next(rec);
 
1444
        }
 
1445
 
 
1446
        page_cur_move_to_next(cursor);
 
1447
        next_rec = cursor->rec;
 
1448
 
 
1449
        /* 3. Remove the record from the linked list of records */
 
1450
 
 
1451
        page_rec_set_next(prev_rec, next_rec);
 
1452
        page_header_set_field(page, PAGE_N_RECS,
 
1453
                              (ulint)(page_get_n_recs(page) - 1));
 
1454
 
 
1455
        /* 4. If the deleted record is pointed to by a dir slot, update the
 
1456
        record pointer in slot. In the following if-clause we assume that
 
1457
        prev_rec is owned by the same slot, i.e., PAGE_DIR_SLOT_MIN_N_OWNED
 
1458
        >= 2. */
 
1459
 
 
1460
#if PAGE_DIR_SLOT_MIN_N_OWNED < 2
 
1461
# error "PAGE_DIR_SLOT_MIN_N_OWNED < 2"
 
1462
#endif
 
1463
        ut_ad(cur_n_owned > 1);
 
1464
 
 
1465
        if (current_rec == page_dir_slot_get_rec(cur_dir_slot)) {
 
1466
                page_dir_slot_set_rec(cur_dir_slot, prev_rec);
 
1467
        }
 
1468
 
 
1469
        /* 5. Update the number of owned records of the slot */
 
1470
 
 
1471
        page_dir_slot_set_n_owned(cur_dir_slot, cur_n_owned - 1);
 
1472
 
 
1473
        /* 6. Free the memory occupied by the record */
 
1474
        page_mem_free(page, current_rec, offsets);
 
1475
 
 
1476
        /* 7. Now we have decremented the number of owned records of the slot.
 
1477
        If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the
 
1478
        slots. */
 
1479
 
 
1480
        if (cur_n_owned <= PAGE_DIR_SLOT_MIN_N_OWNED) {
 
1481
                page_dir_balance_slot(page, cur_slot_no);
 
1482
        }
 
1483
}
 
1484
 
 
1485
#ifdef UNIV_COMPILE_TEST_FUNCS
 
1486
 
 
1487
/***********************************************************************
 
1488
Print the first n numbers, generated by page_cur_lcg_prng() to make sure
 
1489
(visually) that it works properly. */
 
1490
void
 
1491
test_page_cur_lcg_prng(
 
1492
/*===================*/
 
1493
        int     n)      /* in: print first n numbers */
 
1494
{
 
1495
        int                     i;
 
1496
        unsigned long long      rnd;
 
1497
 
 
1498
        for (i = 0; i < n; i++) {
 
1499
                rnd = page_cur_lcg_prng();
 
1500
                printf("%llu\t%%2=%llu %%3=%llu %%5=%llu %%7=%llu %%11=%llu\n",
 
1501
                       rnd,
 
1502
                       rnd % 2,
 
1503
                       rnd % 3,
 
1504
                       rnd % 5,
 
1505
                       rnd % 7,
 
1506
                       rnd % 11);
 
1507
        }
 
1508
}
 
1509
 
 
1510
#endif /* UNIV_COMPILE_TEST_FUNCS */