~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/row/row0upd.c

  • Committer: Monty Taylor
  • Date: 2008-11-16 06:29:53 UTC
  • mto: (584.1.9 devel)
  • mto: This revision was merged to the branch mainline in revision 589.
  • Revision ID: monty@inaugust.com-20081116062953-ivdltjmfe009b5fr
Moved stuff into item/

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Update of a row
 
3
 
 
4
(c) 1996 Innobase Oy
 
5
 
 
6
Created 12/27/1996 Heikki Tuuri
 
7
*******************************************************/
 
8
 
 
9
#include "row0upd.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "row0upd.ic"
 
13
#endif
 
14
 
 
15
#include "dict0dict.h"
 
16
#include "dict0boot.h"
 
17
#include "dict0crea.h"
 
18
#include "mach0data.h"
 
19
#include "trx0undo.h"
 
20
#include "btr0btr.h"
 
21
#include "btr0cur.h"
 
22
#include "que0que.h"
 
23
#include "row0ext.h"
 
24
#include "row0ins.h"
 
25
#include "row0sel.h"
 
26
#include "row0row.h"
 
27
#include "rem0cmp.h"
 
28
#include "lock0lock.h"
 
29
#include "log0log.h"
 
30
#include "pars0sym.h"
 
31
#include "eval0eval.h"
 
32
#include "buf0lru.h"
 
33
 
 
34
 
 
35
/* What kind of latch and lock can we assume when the control comes to
 
36
   -------------------------------------------------------------------
 
37
an update node?
 
38
--------------
 
39
Efficiency of massive updates would require keeping an x-latch on a
 
40
clustered index page through many updates, and not setting an explicit
 
41
x-lock on clustered index records, as they anyway will get an implicit
 
42
x-lock when they are updated. A problem is that the read nodes in the
 
43
graph should know that they must keep the latch when passing the control
 
44
up to the update node, and not set any record lock on the record which
 
45
will be updated. Another problem occurs if the execution is stopped,
 
46
as the kernel switches to another query thread, or the transaction must
 
47
wait for a lock. Then we should be able to release the latch and, maybe,
 
48
acquire an explicit x-lock on the record.
 
49
        Because this seems too complicated, we conclude that the less
 
50
efficient solution of releasing all the latches when the control is
 
51
transferred to another node, and acquiring explicit x-locks, is better. */
 
52
 
 
53
/* How is a delete performed? If there is a delete without an
 
54
explicit cursor, i.e., a searched delete, there are at least
 
55
two different situations:
 
56
the implicit select cursor may run on (1) the clustered index or
 
57
on (2) a secondary index. The delete is performed by setting
 
58
the delete bit in the record and substituting the id of the
 
59
deleting transaction for the original trx id, and substituting a
 
60
new roll ptr for previous roll ptr. The old trx id and roll ptr
 
61
are saved in the undo log record. Thus, no physical changes occur
 
62
in the index tree structure at the time of the delete. Only
 
63
when the undo log is purged, the index records will be physically
 
64
deleted from the index trees.
 
65
 
 
66
The query graph executing a searched delete would consist of
 
67
a delete node which has as a subtree a select subgraph.
 
68
The select subgraph should return a (persistent) cursor
 
69
in the clustered index, placed on page which is x-latched.
 
70
The delete node should look for all secondary index records for
 
71
this clustered index entry and mark them as deleted. When is
 
72
the x-latch freed? The most efficient way for performing a
 
73
searched delete is obviously to keep the x-latch for several
 
74
steps of query graph execution. */
 
75
 
 
76
/***************************************************************
 
77
Checks if an update vector changes some of the first ordering fields of an
 
78
index record. This is only used in foreign key checks and we can assume
 
79
that index does not contain column prefixes. */
 
80
static
 
81
ibool
 
82
row_upd_changes_first_fields_binary(
 
83
/*================================*/
 
84
                                /* out: TRUE if changes */
 
85
        dtuple_t*       entry,  /* in: old value of index entry */
 
86
        dict_index_t*   index,  /* in: index of entry */
 
87
        const upd_t*    update, /* in: update vector for the row */
 
88
        ulint           n);     /* in: how many first fields to check */
 
89
 
 
90
 
 
91
/*************************************************************************
 
92
Checks if index currently is mentioned as a referenced index in a foreign
 
93
key constraint. */
 
94
static
 
95
ibool
 
96
row_upd_index_is_referenced(
 
97
/*========================*/
 
98
                                /* out: TRUE if referenced; NOTE that since
 
99
                                we do not hold dict_operation_lock
 
100
                                when leaving the function, it may be that
 
101
                                the referencing table has been dropped when
 
102
                                we leave this function: this function is only
 
103
                                for heuristic use! */
 
104
        dict_index_t*   index,  /* in: index */
 
105
        trx_t*          trx)    /* in: transaction */
 
106
{
 
107
        dict_table_t*   table           = index->table;
 
108
        dict_foreign_t* foreign;
 
109
        ibool           froze_data_dict = FALSE;
 
110
        ibool           is_referenced   = FALSE;
 
111
 
 
112
        if (!UT_LIST_GET_FIRST(table->referenced_list)) {
 
113
 
 
114
                return(FALSE);
 
115
        }
 
116
 
 
117
        if (trx->dict_operation_lock_mode == 0) {
 
118
                row_mysql_freeze_data_dictionary(trx);
 
119
                froze_data_dict = TRUE;
 
120
        }
 
121
 
 
122
        foreign = UT_LIST_GET_FIRST(table->referenced_list);
 
123
 
 
124
        while (foreign) {
 
125
                if (foreign->referenced_index == index) {
 
126
 
 
127
                        is_referenced = TRUE;
 
128
                        goto func_exit;
 
129
                }
 
130
 
 
131
                foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
 
132
        }
 
133
 
 
134
func_exit:
 
135
        if (froze_data_dict) {
 
136
                row_mysql_unfreeze_data_dictionary(trx);
 
137
        }
 
138
 
 
139
        return(is_referenced);
 
140
}
 
141
 
 
142
/*************************************************************************
 
143
Checks if possible foreign key constraints hold after a delete of the record
 
144
under pcur. NOTE that this function will temporarily commit mtr and lose the
 
145
pcur position! */
 
146
static
 
147
ulint
 
148
row_upd_check_references_constraints(
 
149
/*=================================*/
 
150
                                /* out: DB_SUCCESS or an error code */
 
151
        upd_node_t*     node,   /* in: row update node */
 
152
        btr_pcur_t*     pcur,   /* in: cursor positioned on a record; NOTE: the
 
153
                                cursor position is lost in this function! */
 
154
        dict_table_t*   table,  /* in: table in question */
 
155
        dict_index_t*   index,  /* in: index of the cursor */
 
156
        ulint*          offsets,/* in/out: rec_get_offsets(pcur.rec, index) */
 
157
        que_thr_t*      thr,    /* in: query thread */
 
158
        mtr_t*          mtr)    /* in: mtr */
 
159
{
 
160
        dict_foreign_t* foreign;
 
161
        mem_heap_t*     heap;
 
162
        dtuple_t*       entry;
 
163
        trx_t*          trx;
 
164
        const rec_t*    rec;
 
165
        ulint           n_ext;
 
166
        ulint           err;
 
167
        ibool           got_s_lock      = FALSE;
 
168
 
 
169
        if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) {
 
170
 
 
171
                return(DB_SUCCESS);
 
172
        }
 
173
 
 
174
        trx = thr_get_trx(thr);
 
175
 
 
176
        rec = btr_pcur_get_rec(pcur);
 
177
        ut_ad(rec_offs_validate(rec, index, offsets));
 
178
 
 
179
        heap = mem_heap_create(500);
 
180
 
 
181
        entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets,
 
182
                                       &n_ext, heap);
 
183
 
 
184
        mtr_commit(mtr);
 
185
 
 
186
        mtr_start(mtr);
 
187
 
 
188
        if (trx->dict_operation_lock_mode == 0) {
 
189
                got_s_lock = TRUE;
 
190
 
 
191
                row_mysql_freeze_data_dictionary(trx);
 
192
        }
 
193
 
 
194
        foreign = UT_LIST_GET_FIRST(table->referenced_list);
 
195
 
 
196
        while (foreign) {
 
197
                /* Note that we may have an update which updates the index
 
198
                record, but does NOT update the first fields which are
 
199
                referenced in a foreign key constraint. Then the update does
 
200
                NOT break the constraint. */
 
201
 
 
202
                if (foreign->referenced_index == index
 
203
                    && (node->is_delete
 
204
                        || row_upd_changes_first_fields_binary(
 
205
                                entry, index, node->update,
 
206
                                foreign->n_fields))) {
 
207
 
 
208
                        if (foreign->foreign_table == NULL) {
 
209
                                dict_table_get(foreign->foreign_table_name,
 
210
                                               FALSE);
 
211
                        }
 
212
 
 
213
                        if (foreign->foreign_table) {
 
214
                                mutex_enter(&(dict_sys->mutex));
 
215
 
 
216
                                (foreign->foreign_table
 
217
                                 ->n_foreign_key_checks_running)++;
 
218
 
 
219
                                mutex_exit(&(dict_sys->mutex));
 
220
                        }
 
221
 
 
222
                        /* NOTE that if the thread ends up waiting for a lock
 
223
                        we will release dict_operation_lock temporarily!
 
224
                        But the counter on the table protects 'foreign' from
 
225
                        being dropped while the check is running. */
 
226
 
 
227
                        err = row_ins_check_foreign_constraint(
 
228
                                FALSE, foreign, table, entry, thr);
 
229
 
 
230
                        if (foreign->foreign_table) {
 
231
                                mutex_enter(&(dict_sys->mutex));
 
232
 
 
233
                                ut_a(foreign->foreign_table
 
234
                                     ->n_foreign_key_checks_running > 0);
 
235
 
 
236
                                (foreign->foreign_table
 
237
                                 ->n_foreign_key_checks_running)--;
 
238
 
 
239
                                mutex_exit(&(dict_sys->mutex));
 
240
                        }
 
241
 
 
242
                        if (err != DB_SUCCESS) {
 
243
 
 
244
                                goto func_exit;
 
245
                        }
 
246
                }
 
247
 
 
248
                foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
 
249
        }
 
250
 
 
251
        err = DB_SUCCESS;
 
252
 
 
253
func_exit:
 
254
        if (got_s_lock) {
 
255
                row_mysql_unfreeze_data_dictionary(trx);
 
256
        }
 
257
 
 
258
        mem_heap_free(heap);
 
259
 
 
260
        return(err);
 
261
}
 
262
 
 
263
/*************************************************************************
 
264
Creates an update node for a query graph. */
 
265
UNIV_INTERN
 
266
upd_node_t*
 
267
upd_node_create(
 
268
/*============*/
 
269
                                /* out, own: update node */
 
270
        mem_heap_t*     heap)   /* in: mem heap where created */
 
271
{
 
272
        upd_node_t*     node;
 
273
 
 
274
        node = mem_heap_alloc(heap, sizeof(upd_node_t));
 
275
        node->common.type = QUE_NODE_UPDATE;
 
276
 
 
277
        node->state = UPD_NODE_UPDATE_CLUSTERED;
 
278
        node->select_will_do_update = FALSE;
 
279
        node->in_mysql_interface = FALSE;
 
280
 
 
281
        node->row = NULL;
 
282
        node->ext = NULL;
 
283
        node->upd_row = NULL;
 
284
        node->upd_ext = NULL;
 
285
        node->index = NULL;
 
286
        node->update = NULL;
 
287
 
 
288
        node->foreign = NULL;
 
289
        node->cascade_heap = NULL;
 
290
        node->cascade_node = NULL;
 
291
 
 
292
        node->select = NULL;
 
293
 
 
294
        node->heap = mem_heap_create(128);
 
295
        node->magic_n = UPD_NODE_MAGIC_N;
 
296
 
 
297
        node->cmpl_info = 0;
 
298
 
 
299
        return(node);
 
300
}
 
301
 
 
302
/*************************************************************************
 
303
Updates the trx id and roll ptr field in a clustered index record in database
 
304
recovery. */
 
305
UNIV_INTERN
 
306
void
 
307
row_upd_rec_sys_fields_in_recovery(
 
308
/*===============================*/
 
309
        rec_t*          rec,    /* in/out: record */
 
310
        page_zip_des_t* page_zip,/* in/out: compressed page, or NULL */
 
311
        const ulint*    offsets,/* in: array returned by rec_get_offsets() */
 
312
        ulint           pos,    /* in: TRX_ID position in rec */
 
313
        dulint          trx_id, /* in: transaction id */
 
314
        dulint          roll_ptr)/* in: roll ptr of the undo log record */
 
315
{
 
316
        ut_ad(rec_offs_validate(rec, NULL, offsets));
 
317
 
 
318
        if (UNIV_LIKELY_NULL(page_zip)) {
 
319
                page_zip_write_trx_id_and_roll_ptr(
 
320
                        page_zip, rec, offsets, pos, trx_id, roll_ptr);
 
321
        } else {
 
322
                byte*   field;
 
323
                ulint   len;
 
324
 
 
325
                field = rec_get_nth_field(rec, offsets, pos, &len);
 
326
                ut_ad(len == DATA_TRX_ID_LEN);
 
327
#if DATA_TRX_ID + 1 != DATA_ROLL_PTR
 
328
# error "DATA_TRX_ID + 1 != DATA_ROLL_PTR"
 
329
#endif
 
330
                trx_write_trx_id(field, trx_id);
 
331
                trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr);
 
332
        }
 
333
}
 
334
 
 
335
/*************************************************************************
 
336
Sets the trx id or roll ptr field of a clustered index entry. */
 
337
UNIV_INTERN
 
338
void
 
339
row_upd_index_entry_sys_field(
 
340
/*==========================*/
 
341
        const dtuple_t* entry,  /* in: index entry, where the memory buffers
 
342
                                for sys fields are already allocated:
 
343
                                the function just copies the new values to
 
344
                                them */
 
345
        dict_index_t*   index,  /* in: clustered index */
 
346
        ulint           type,   /* in: DATA_TRX_ID or DATA_ROLL_PTR */
 
347
        dulint          val)    /* in: value to write */
 
348
{
 
349
        dfield_t*       dfield;
 
350
        byte*           field;
 
351
        ulint           pos;
 
352
 
 
353
        ut_ad(dict_index_is_clust(index));
 
354
 
 
355
        pos = dict_index_get_sys_col_pos(index, type);
 
356
 
 
357
        dfield = dtuple_get_nth_field(entry, pos);
 
358
        field = dfield_get_data(dfield);
 
359
 
 
360
        if (type == DATA_TRX_ID) {
 
361
                trx_write_trx_id(field, val);
 
362
        } else {
 
363
                ut_ad(type == DATA_ROLL_PTR);
 
364
                trx_write_roll_ptr(field, val);
 
365
        }
 
366
}
 
367
 
 
368
/***************************************************************
 
369
Returns TRUE if row update changes size of some field in index or if some
 
370
field to be updated is stored externally in rec or update. */
 
371
UNIV_INTERN
 
372
ibool
 
373
row_upd_changes_field_size_or_external(
 
374
/*===================================*/
 
375
                                /* out: TRUE if the update changes the size of
 
376
                                some field in index or the field is external
 
377
                                in rec or update */
 
378
        dict_index_t*   index,  /* in: index */
 
379
        const ulint*    offsets,/* in: rec_get_offsets(rec, index) */
 
380
        const upd_t*    update) /* in: update vector */
 
381
{
 
382
        const upd_field_t*      upd_field;
 
383
        const dfield_t*         new_val;
 
384
        ulint                   old_len;
 
385
        ulint                   new_len;
 
386
        ulint                   n_fields;
 
387
        ulint                   i;
 
388
 
 
389
        ut_ad(rec_offs_validate(NULL, index, offsets));
 
390
        n_fields = upd_get_n_fields(update);
 
391
 
 
392
        for (i = 0; i < n_fields; i++) {
 
393
                upd_field = upd_get_nth_field(update, i);
 
394
 
 
395
                new_val = &(upd_field->new_val);
 
396
                new_len = dfield_get_len(new_val);
 
397
 
 
398
                if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) {
 
399
                        /* A bug fixed on Dec 31st, 2004: we looked at the
 
400
                        SQL NULL size from the wrong field! We may backport
 
401
                        this fix also to 4.0. The merge to 5.0 will be made
 
402
                        manually immediately after we commit this to 4.1. */
 
403
 
 
404
                        new_len = dict_col_get_sql_null_size(
 
405
                                dict_index_get_nth_col(index,
 
406
                                                       upd_field->field_no));
 
407
                }
 
408
 
 
409
                old_len = rec_offs_nth_size(offsets, upd_field->field_no);
 
410
 
 
411
                if (rec_offs_comp(offsets)
 
412
                    && rec_offs_nth_sql_null(offsets,
 
413
                                             upd_field->field_no)) {
 
414
                        /* Note that in the compact table format, for a
 
415
                        variable length field, an SQL NULL will use zero
 
416
                        bytes in the offset array at the start of the physical
 
417
                        record, but a zero-length value (empty string) will
 
418
                        use one byte! Thus, we cannot use update-in-place
 
419
                        if we update an SQL NULL varchar to an empty string! */
 
420
 
 
421
                        old_len = UNIV_SQL_NULL;
 
422
                }
 
423
 
 
424
                if (dfield_is_ext(new_val) || old_len != new_len
 
425
                    || rec_offs_nth_extern(offsets, upd_field->field_no)) {
 
426
 
 
427
                        return(TRUE);
 
428
                }
 
429
        }
 
430
 
 
431
        return(FALSE);
 
432
}
 
433
 
 
434
/***************************************************************
 
435
Replaces the new column values stored in the update vector to the record
 
436
given. No field size changes are allowed. */
 
437
UNIV_INTERN
 
438
void
 
439
row_upd_rec_in_place(
 
440
/*=================*/
 
441
        rec_t*          rec,    /* in/out: record where replaced */
 
442
        dict_index_t*   index,  /* in: the index the record belongs to */
 
443
        const ulint*    offsets,/* in: array returned by rec_get_offsets() */
 
444
        const upd_t*    update, /* in: update vector */
 
445
        page_zip_des_t* page_zip)/* in: compressed page with enough space
 
446
                                available, or NULL */
 
447
{
 
448
        const upd_field_t*      upd_field;
 
449
        const dfield_t*         new_val;
 
450
        ulint                   n_fields;
 
451
        ulint                   i;
 
452
 
 
453
        ut_ad(rec_offs_validate(rec, index, offsets));
 
454
 
 
455
        if (rec_offs_comp(offsets)) {
 
456
                rec_set_info_bits_new(rec, update->info_bits);
 
457
        } else {
 
458
                rec_set_info_bits_old(rec, update->info_bits);
 
459
        }
 
460
 
 
461
        n_fields = upd_get_n_fields(update);
 
462
 
 
463
        for (i = 0; i < n_fields; i++) {
 
464
                upd_field = upd_get_nth_field(update, i);
 
465
                new_val = &(upd_field->new_val);
 
466
                ut_ad(!dfield_is_ext(new_val) ==
 
467
                      !rec_offs_nth_extern(offsets, upd_field->field_no));
 
468
 
 
469
                rec_set_nth_field(rec, offsets, upd_field->field_no,
 
470
                                  dfield_get_data(new_val),
 
471
                                  dfield_get_len(new_val));
 
472
        }
 
473
 
 
474
        if (UNIV_LIKELY_NULL(page_zip)) {
 
475
                page_zip_write_rec(page_zip, rec, index, offsets, 0);
 
476
        }
 
477
}
 
478
 
 
479
/*************************************************************************
 
480
Writes into the redo log the values of trx id and roll ptr and enough info
 
481
to determine their positions within a clustered index record. */
 
482
UNIV_INTERN
 
483
byte*
 
484
row_upd_write_sys_vals_to_log(
 
485
/*==========================*/
 
486
                                /* out: new pointer to mlog */
 
487
        dict_index_t*   index,  /* in: clustered index */
 
488
        trx_t*          trx,    /* in: transaction */
 
489
        dulint          roll_ptr,/* in: roll ptr of the undo log record */
 
490
        byte*           log_ptr,/* pointer to a buffer of size > 20 opened
 
491
                                in mlog */
 
492
        mtr_t*          mtr __attribute__((unused))) /* in: mtr */
 
493
{
 
494
        ut_ad(dict_index_is_clust(index));
 
495
        ut_ad(mtr);
 
496
 
 
497
        log_ptr += mach_write_compressed(log_ptr,
 
498
                                         dict_index_get_sys_col_pos(
 
499
                                                 index, DATA_TRX_ID));
 
500
 
 
501
        trx_write_roll_ptr(log_ptr, roll_ptr);
 
502
        log_ptr += DATA_ROLL_PTR_LEN;
 
503
 
 
504
        log_ptr += mach_dulint_write_compressed(log_ptr, trx->id);
 
505
 
 
506
        return(log_ptr);
 
507
}
 
508
 
 
509
/*************************************************************************
 
510
Parses the log data of system field values. */
 
511
UNIV_INTERN
 
512
byte*
 
513
row_upd_parse_sys_vals(
 
514
/*===================*/
 
515
                        /* out: log data end or NULL */
 
516
        byte*   ptr,    /* in: buffer */
 
517
        byte*   end_ptr,/* in: buffer end */
 
518
        ulint*  pos,    /* out: TRX_ID position in record */
 
519
        dulint* trx_id, /* out: trx id */
 
520
        dulint* roll_ptr)/* out: roll ptr */
 
521
{
 
522
        ptr = mach_parse_compressed(ptr, end_ptr, pos);
 
523
 
 
524
        if (ptr == NULL) {
 
525
 
 
526
                return(NULL);
 
527
        }
 
528
 
 
529
        if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
 
530
 
 
531
                return(NULL);
 
532
        }
 
533
 
 
534
        *roll_ptr = trx_read_roll_ptr(ptr);
 
535
        ptr += DATA_ROLL_PTR_LEN;
 
536
 
 
537
        ptr = mach_dulint_parse_compressed(ptr, end_ptr, trx_id);
 
538
 
 
539
        return(ptr);
 
540
}
 
541
 
 
542
/***************************************************************
 
543
Writes to the redo log the new values of the fields occurring in the index. */
 
544
UNIV_INTERN
 
545
void
 
546
row_upd_index_write_log(
 
547
/*====================*/
 
548
        const upd_t*    update, /* in: update vector */
 
549
        byte*           log_ptr,/* in: pointer to mlog buffer: must
 
550
                                contain at least MLOG_BUF_MARGIN bytes
 
551
                                of free space; the buffer is closed
 
552
                                within this function */
 
553
        mtr_t*          mtr)    /* in: mtr into whose log to write */
 
554
{
 
555
        const upd_field_t*      upd_field;
 
556
        const dfield_t*         new_val;
 
557
        ulint                   len;
 
558
        ulint                   n_fields;
 
559
        byte*                   buf_end;
 
560
        ulint                   i;
 
561
 
 
562
        n_fields = upd_get_n_fields(update);
 
563
 
 
564
        buf_end = log_ptr + MLOG_BUF_MARGIN;
 
565
 
 
566
        mach_write_to_1(log_ptr, update->info_bits);
 
567
        log_ptr++;
 
568
        log_ptr += mach_write_compressed(log_ptr, n_fields);
 
569
 
 
570
        for (i = 0; i < n_fields; i++) {
 
571
 
 
572
#if MLOG_BUF_MARGIN <= 30
 
573
# error "MLOG_BUF_MARGIN <= 30"
 
574
#endif
 
575
 
 
576
                if (log_ptr + 30 > buf_end) {
 
577
                        mlog_close(mtr, log_ptr);
 
578
 
 
579
                        log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
 
580
                        buf_end = log_ptr + MLOG_BUF_MARGIN;
 
581
                }
 
582
 
 
583
                upd_field = upd_get_nth_field(update, i);
 
584
 
 
585
                new_val = &(upd_field->new_val);
 
586
 
 
587
                len = dfield_get_len(new_val);
 
588
 
 
589
                log_ptr += mach_write_compressed(log_ptr, upd_field->field_no);
 
590
                log_ptr += mach_write_compressed(log_ptr, len);
 
591
 
 
592
                if (len != UNIV_SQL_NULL) {
 
593
                        if (log_ptr + len < buf_end) {
 
594
                                memcpy(log_ptr, dfield_get_data(new_val), len);
 
595
 
 
596
                                log_ptr += len;
 
597
                        } else {
 
598
                                mlog_close(mtr, log_ptr);
 
599
 
 
600
                                mlog_catenate_string(mtr,
 
601
                                                     dfield_get_data(new_val),
 
602
                                                     len);
 
603
 
 
604
                                log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
 
605
                                buf_end = log_ptr + MLOG_BUF_MARGIN;
 
606
                        }
 
607
                }
 
608
        }
 
609
 
 
610
        mlog_close(mtr, log_ptr);
 
611
}
 
612
 
 
613
/*************************************************************************
 
614
Parses the log data written by row_upd_index_write_log. */
 
615
UNIV_INTERN
 
616
byte*
 
617
row_upd_index_parse(
 
618
/*================*/
 
619
                                /* out: log data end or NULL */
 
620
        byte*           ptr,    /* in: buffer */
 
621
        byte*           end_ptr,/* in: buffer end */
 
622
        mem_heap_t*     heap,   /* in: memory heap where update vector is
 
623
                                built */
 
624
        upd_t**         update_out)/* out: update vector */
 
625
{
 
626
        upd_t*          update;
 
627
        upd_field_t*    upd_field;
 
628
        dfield_t*       new_val;
 
629
        ulint           len;
 
630
        ulint           n_fields;
 
631
        ulint           info_bits;
 
632
        ulint           i;
 
633
 
 
634
        if (end_ptr < ptr + 1) {
 
635
 
 
636
                return(NULL);
 
637
        }
 
638
 
 
639
        info_bits = mach_read_from_1(ptr);
 
640
        ptr++;
 
641
        ptr = mach_parse_compressed(ptr, end_ptr, &n_fields);
 
642
 
 
643
        if (ptr == NULL) {
 
644
 
 
645
                return(NULL);
 
646
        }
 
647
 
 
648
        update = upd_create(n_fields, heap);
 
649
        update->info_bits = info_bits;
 
650
 
 
651
        for (i = 0; i < n_fields; i++) {
 
652
                ulint   field_no;
 
653
                upd_field = upd_get_nth_field(update, i);
 
654
                new_val = &(upd_field->new_val);
 
655
 
 
656
                ptr = mach_parse_compressed(ptr, end_ptr, &field_no);
 
657
 
 
658
                if (ptr == NULL) {
 
659
 
 
660
                        return(NULL);
 
661
                }
 
662
 
 
663
                upd_field->field_no = field_no;
 
664
 
 
665
                ptr = mach_parse_compressed(ptr, end_ptr, &len);
 
666
 
 
667
                if (ptr == NULL) {
 
668
 
 
669
                        return(NULL);
 
670
                }
 
671
 
 
672
                if (len != UNIV_SQL_NULL) {
 
673
 
 
674
                        if (end_ptr < ptr + len) {
 
675
 
 
676
                                return(NULL);
 
677
                        }
 
678
 
 
679
                        dfield_set_data(new_val,
 
680
                                        mem_heap_dup(heap, ptr, len), len);
 
681
                        ptr += len;
 
682
                } else {
 
683
                        dfield_set_null(new_val);
 
684
                }
 
685
        }
 
686
 
 
687
        *update_out = update;
 
688
 
 
689
        return(ptr);
 
690
}
 
691
 
 
692
/*******************************************************************
 
693
Builds an update vector from those fields which in a secondary index entry
 
694
differ from a record that has the equal ordering fields. NOTE: we compare
 
695
the fields as binary strings! */
 
696
UNIV_INTERN
 
697
upd_t*
 
698
row_upd_build_sec_rec_difference_binary(
 
699
/*====================================*/
 
700
                                /* out, own: update vector of differing
 
701
                                fields */
 
702
        dict_index_t*   index,  /* in: index */
 
703
        const dtuple_t* entry,  /* in: entry to insert */
 
704
        const rec_t*    rec,    /* in: secondary index record */
 
705
        trx_t*          trx,    /* in: transaction */
 
706
        mem_heap_t*     heap)   /* in: memory heap from which allocated */
 
707
{
 
708
        upd_field_t*    upd_field;
 
709
        const dfield_t* dfield;
 
710
        const byte*     data;
 
711
        ulint           len;
 
712
        upd_t*          update;
 
713
        ulint           n_diff;
 
714
        ulint           i;
 
715
        ulint           offsets_[REC_OFFS_SMALL_SIZE];
 
716
        const ulint*    offsets;
 
717
        rec_offs_init(offsets_);
 
718
 
 
719
        /* This function is used only for a secondary index */
 
720
        ut_a(!dict_index_is_clust(index));
 
721
 
 
722
        update = upd_create(dtuple_get_n_fields(entry), heap);
 
723
 
 
724
        n_diff = 0;
 
725
        offsets = rec_get_offsets(rec, index, offsets_,
 
726
                                  ULINT_UNDEFINED, &heap);
 
727
 
 
728
        for (i = 0; i < dtuple_get_n_fields(entry); i++) {
 
729
 
 
730
                data = rec_get_nth_field(rec, offsets, i, &len);
 
731
 
 
732
                dfield = dtuple_get_nth_field(entry, i);
 
733
 
 
734
                /* NOTE that it may be that len != dfield_get_len(dfield) if we
 
735
                are updating in a character set and collation where strings of
 
736
                different length can be equal in an alphabetical comparison,
 
737
                and also in the case where we have a column prefix index
 
738
                and the last characters in the index field are spaces; the
 
739
                latter case probably caused the assertion failures reported at
 
740
                row0upd.c line 713 in versions 4.0.14 - 4.0.16. */
 
741
 
 
742
                /* NOTE: we compare the fields as binary strings!
 
743
                (No collation) */
 
744
 
 
745
                if (!dfield_data_is_binary_equal(dfield, len, data)) {
 
746
 
 
747
                        upd_field = upd_get_nth_field(update, n_diff);
 
748
 
 
749
                        dfield_copy(&(upd_field->new_val), dfield);
 
750
 
 
751
                        upd_field_set_field_no(upd_field, i, index, trx);
 
752
 
 
753
                        n_diff++;
 
754
                }
 
755
        }
 
756
 
 
757
        update->n_fields = n_diff;
 
758
 
 
759
        return(update);
 
760
}
 
761
 
 
762
/*******************************************************************
 
763
Builds an update vector from those fields, excluding the roll ptr and
 
764
trx id fields, which in an index entry differ from a record that has
 
765
the equal ordering fields. NOTE: we compare the fields as binary strings! */
 
766
UNIV_INTERN
 
767
upd_t*
 
768
row_upd_build_difference_binary(
 
769
/*============================*/
 
770
                                /* out, own: update vector of differing
 
771
                                fields, excluding roll ptr and trx id */
 
772
        dict_index_t*   index,  /* in: clustered index */
 
773
        const dtuple_t* entry,  /* in: entry to insert */
 
774
        const rec_t*    rec,    /* in: clustered index record */
 
775
        trx_t*          trx,    /* in: transaction */
 
776
        mem_heap_t*     heap)   /* in: memory heap from which allocated */
 
777
{
 
778
        upd_field_t*    upd_field;
 
779
        const dfield_t* dfield;
 
780
        const byte*     data;
 
781
        ulint           len;
 
782
        upd_t*          update;
 
783
        ulint           n_diff;
 
784
        ulint           roll_ptr_pos;
 
785
        ulint           trx_id_pos;
 
786
        ulint           i;
 
787
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
788
        const ulint*    offsets;
 
789
        rec_offs_init(offsets_);
 
790
 
 
791
        /* This function is used only for a clustered index */
 
792
        ut_a(dict_index_is_clust(index));
 
793
 
 
794
        update = upd_create(dtuple_get_n_fields(entry), heap);
 
795
 
 
796
        n_diff = 0;
 
797
 
 
798
        roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR);
 
799
        trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
 
800
 
 
801
        offsets = rec_get_offsets(rec, index, offsets_,
 
802
                                  ULINT_UNDEFINED, &heap);
 
803
 
 
804
        for (i = 0; i < dtuple_get_n_fields(entry); i++) {
 
805
 
 
806
                data = rec_get_nth_field(rec, offsets, i, &len);
 
807
 
 
808
                dfield = dtuple_get_nth_field(entry, i);
 
809
 
 
810
                /* NOTE: we compare the fields as binary strings!
 
811
                (No collation) */
 
812
 
 
813
                if (i == trx_id_pos || i == roll_ptr_pos) {
 
814
 
 
815
                        goto skip_compare;
 
816
                }
 
817
 
 
818
                if (UNIV_UNLIKELY(!dfield_is_ext(dfield)
 
819
                                  != !rec_offs_nth_extern(offsets, i))
 
820
                    || !dfield_data_is_binary_equal(dfield, len, data)) {
 
821
 
 
822
                        upd_field = upd_get_nth_field(update, n_diff);
 
823
 
 
824
                        dfield_copy(&(upd_field->new_val), dfield);
 
825
 
 
826
                        upd_field_set_field_no(upd_field, i, index, trx);
 
827
 
 
828
                        n_diff++;
 
829
                }
 
830
skip_compare:
 
831
                ;
 
832
        }
 
833
 
 
834
        update->n_fields = n_diff;
 
835
 
 
836
        return(update);
 
837
}
 
838
 
 
839
/***************************************************************
 
840
Fetch a prefix of an externally stored column.  This is similar
 
841
to row_ext_lookup(), but the row_ext_t holds the old values
 
842
of the column and must not be poisoned with the new values. */
 
843
static
 
844
byte*
 
845
row_upd_ext_fetch(
 
846
/*==============*/
 
847
                                        /* out: BLOB prefix */
 
848
        const byte*     data,           /* in: 'internally' stored part of the
 
849
                                        field containing also the reference to
 
850
                                        the external part */
 
851
        ulint           local_len,      /* in: length of data, in bytes */
 
852
        ulint           zip_size,       /* in: nonzero=compressed BLOB
 
853
                                        page size, zero for uncompressed
 
854
                                        BLOBs */
 
855
        ulint*          len,            /* in: length of prefix to fetch;
 
856
                                        out: fetched length of the prefix */
 
857
        mem_heap_t*     heap)           /* in: heap where to allocate */
 
858
{
 
859
        byte*   buf = mem_heap_alloc(heap, *len);
 
860
 
 
861
        *len = btr_copy_externally_stored_field_prefix(buf, *len,
 
862
                                                       zip_size,
 
863
                                                       data, local_len);
 
864
 
 
865
        return(buf);
 
866
}
 
867
 
 
868
/***************************************************************
 
869
Replaces the new column value stored in the update vector in
 
870
the given index entry field. */
 
871
static
 
872
void
 
873
row_upd_index_replace_new_col_val(
 
874
/*==============================*/
 
875
        dfield_t*               dfield, /* in/out: data field
 
876
                                        of the index entry */
 
877
        const dict_field_t*     field,  /* in: index field */
 
878
        const dict_col_t*       col,    /* in: field->col */
 
879
        const upd_field_t*      uf,     /* in: update field */
 
880
        mem_heap_t*             heap,   /* in: memory heap for allocating
 
881
                                        and copying the new value */
 
882
        ulint                   zip_size)/* in: compressed page
 
883
                                         size of the table, or 0 */
 
884
{
 
885
        ulint           len;
 
886
        const byte*     data;
 
887
 
 
888
        dfield_copy_data(dfield, &uf->new_val);
 
889
 
 
890
        if (dfield_is_null(dfield)) {
 
891
                return;
 
892
        }
 
893
 
 
894
        len = dfield_get_len(dfield);
 
895
        data = dfield_get_data(dfield);
 
896
 
 
897
        if (field->prefix_len > 0) {
 
898
                ibool           fetch_ext = dfield_is_ext(dfield)
 
899
                        && len < (ulint) field->prefix_len
 
900
                        + BTR_EXTERN_FIELD_REF_SIZE;
 
901
 
 
902
                if (fetch_ext) {
 
903
                        ulint   l = len;
 
904
 
 
905
                        len = field->prefix_len;
 
906
 
 
907
                        data = row_upd_ext_fetch(data, l, zip_size,
 
908
                                                 &len, heap);
 
909
                }
 
910
 
 
911
                len = dtype_get_at_most_n_mbchars(col->prtype,
 
912
                                                  col->mbminlen, col->mbmaxlen,
 
913
                                                  field->prefix_len, len,
 
914
                                                  (const char*) data);
 
915
 
 
916
                dfield_set_data(dfield, data, len);
 
917
 
 
918
                if (!fetch_ext) {
 
919
                        dfield_dup(dfield, heap);
 
920
                }
 
921
 
 
922
                return;
 
923
        }
 
924
 
 
925
        switch (uf->orig_len) {
 
926
                byte*   buf;
 
927
        case BTR_EXTERN_FIELD_REF_SIZE:
 
928
                /* Restore the original locally stored
 
929
                part of the column.  In the undo log,
 
930
                InnoDB writes a longer prefix of externally
 
931
                stored columns, so that column prefixes
 
932
                in secondary indexes can be reconstructed. */
 
933
                dfield_set_data(dfield,
 
934
                                data + len - BTR_EXTERN_FIELD_REF_SIZE,
 
935
                                BTR_EXTERN_FIELD_REF_SIZE);
 
936
                dfield_set_ext(dfield);
 
937
                /* fall through */
 
938
        case 0:
 
939
                dfield_dup(dfield, heap);
 
940
                break;
 
941
        default:
 
942
                /* Reconstruct the original locally
 
943
                stored part of the column.  The data
 
944
                will have to be copied. */
 
945
                ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
 
946
                buf = mem_heap_alloc(heap, uf->orig_len);
 
947
                /* Copy the locally stored prefix. */
 
948
                memcpy(buf, data,
 
949
                       uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE);
 
950
                /* Copy the BLOB pointer. */
 
951
                memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
 
952
                       data + len - BTR_EXTERN_FIELD_REF_SIZE,
 
953
                       BTR_EXTERN_FIELD_REF_SIZE);
 
954
 
 
955
                dfield_set_data(dfield, buf, uf->orig_len);
 
956
                dfield_set_ext(dfield);
 
957
                break;
 
958
        }
 
959
}
 
960
 
 
961
/***************************************************************
 
962
Replaces the new column values stored in the update vector to the index entry
 
963
given. */
 
964
UNIV_INTERN
 
965
void
 
966
row_upd_index_replace_new_col_vals_index_pos(
 
967
/*=========================================*/
 
968
        dtuple_t*       entry,  /* in/out: index entry where replaced;
 
969
                                the clustered index record must be
 
970
                                covered by a lock or a page latch to
 
971
                                prevent deletion (rollback or purge) */
 
972
        dict_index_t*   index,  /* in: index; NOTE that this may also be a
 
973
                                non-clustered index */
 
974
        const upd_t*    update, /* in: an update vector built for the index so
 
975
                                that the field number in an upd_field is the
 
976
                                index position */
 
977
        ibool           order_only,
 
978
                                /* in: if TRUE, limit the replacement to
 
979
                                ordering fields of index; note that this
 
980
                                does not work for non-clustered indexes. */
 
981
        mem_heap_t*     heap)   /* in: memory heap for allocating and
 
982
                                copying the new values */
 
983
{
 
984
        ulint           i;
 
985
        ulint           n_fields;
 
986
        const ulint     zip_size        = dict_table_zip_size(index->table);
 
987
 
 
988
        ut_ad(index);
 
989
 
 
990
        dtuple_set_info_bits(entry, update->info_bits);
 
991
 
 
992
        if (order_only) {
 
993
                n_fields = dict_index_get_n_unique(index);
 
994
        } else {
 
995
                n_fields = dict_index_get_n_fields(index);
 
996
        }
 
997
 
 
998
        for (i = 0; i < n_fields; i++) {
 
999
                const dict_field_t*     field;
 
1000
                const dict_col_t*       col;
 
1001
                const upd_field_t*      uf;
 
1002
 
 
1003
                field = dict_index_get_nth_field(index, i);
 
1004
                col = dict_field_get_col(field);
 
1005
                uf = upd_get_field_by_field_no(update, i);
 
1006
 
 
1007
                if (uf) {
 
1008
                        row_upd_index_replace_new_col_val(
 
1009
                                dtuple_get_nth_field(entry, i),
 
1010
                                field, col, uf, heap, zip_size);
 
1011
                }
 
1012
        }
 
1013
}
 
1014
 
 
1015
/***************************************************************
 
1016
Replaces the new column values stored in the update vector to the index entry
 
1017
given. */
 
1018
UNIV_INTERN
 
1019
void
 
1020
row_upd_index_replace_new_col_vals(
 
1021
/*===============================*/
 
1022
        dtuple_t*       entry,  /* in/out: index entry where replaced;
 
1023
                                the clustered index record must be
 
1024
                                covered by a lock or a page latch to
 
1025
                                prevent deletion (rollback or purge) */
 
1026
        dict_index_t*   index,  /* in: index; NOTE that this may also be a
 
1027
                                non-clustered index */
 
1028
        const upd_t*    update, /* in: an update vector built for the
 
1029
                                CLUSTERED index so that the field number in
 
1030
                                an upd_field is the clustered index position */
 
1031
        mem_heap_t*     heap)   /* in: memory heap for allocating and
 
1032
                                copying the new values */
 
1033
{
 
1034
        ulint                   i;
 
1035
        const dict_index_t*     clust_index
 
1036
                = dict_table_get_first_index(index->table);
 
1037
        const ulint             zip_size
 
1038
                = dict_table_zip_size(index->table);
 
1039
 
 
1040
        dtuple_set_info_bits(entry, update->info_bits);
 
1041
 
 
1042
        for (i = 0; i < dict_index_get_n_fields(index); i++) {
 
1043
                const dict_field_t*     field;
 
1044
                const dict_col_t*       col;
 
1045
                const upd_field_t*      uf;
 
1046
 
 
1047
                field = dict_index_get_nth_field(index, i);
 
1048
                col = dict_field_get_col(field);
 
1049
                uf = upd_get_field_by_field_no(
 
1050
                        update, dict_col_get_clust_pos(col, clust_index));
 
1051
 
 
1052
                if (uf) {
 
1053
                        row_upd_index_replace_new_col_val(
 
1054
                                dtuple_get_nth_field(entry, i),
 
1055
                                field, col, uf, heap, zip_size);
 
1056
                }
 
1057
        }
 
1058
}
 
1059
 
 
1060
/***************************************************************
 
1061
Replaces the new column values stored in the update vector. */
 
1062
UNIV_INTERN
 
1063
void
 
1064
row_upd_replace(
 
1065
/*============*/
 
1066
        dtuple_t*               row,    /* in/out: row where replaced,
 
1067
                                        indexed by col_no;
 
1068
                                        the clustered index record must be
 
1069
                                        covered by a lock or a page latch to
 
1070
                                        prevent deletion (rollback or purge) */
 
1071
        row_ext_t**             ext,    /* out, own: NULL, or externally
 
1072
                                        stored column prefixes */
 
1073
        const dict_index_t*     index,  /* in: clustered index */
 
1074
        const upd_t*            update, /* in: an update vector built for the
 
1075
                                        clustered index */
 
1076
        mem_heap_t*             heap)   /* in: memory heap */
 
1077
{
 
1078
        ulint                   col_no;
 
1079
        ulint                   i;
 
1080
        ulint                   n_cols;
 
1081
        ulint                   n_ext_cols;
 
1082
        ulint*                  ext_cols;
 
1083
        const dict_table_t*     table;
 
1084
 
 
1085
        ut_ad(row);
 
1086
        ut_ad(ext);
 
1087
        ut_ad(index);
 
1088
        ut_ad(dict_index_is_clust(index));
 
1089
        ut_ad(update);
 
1090
        ut_ad(heap);
 
1091
 
 
1092
        n_cols = dtuple_get_n_fields(row);
 
1093
        table = index->table;
 
1094
        ut_ad(n_cols == dict_table_get_n_cols(table));
 
1095
 
 
1096
        ext_cols = mem_heap_alloc(heap, n_cols * sizeof *ext_cols);
 
1097
        n_ext_cols = 0;
 
1098
 
 
1099
        dtuple_set_info_bits(row, update->info_bits);
 
1100
 
 
1101
        for (col_no = 0; col_no < n_cols; col_no++) {
 
1102
 
 
1103
                const dict_col_t*       col
 
1104
                        = dict_table_get_nth_col(table, col_no);
 
1105
                const ulint             clust_pos
 
1106
                        = dict_col_get_clust_pos(col, index);
 
1107
                dfield_t*               dfield;
 
1108
 
 
1109
                if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) {
 
1110
 
 
1111
                        continue;
 
1112
                }
 
1113
 
 
1114
                dfield = dtuple_get_nth_field(row, col_no);
 
1115
 
 
1116
                for (i = 0; i < upd_get_n_fields(update); i++) {
 
1117
 
 
1118
                        const upd_field_t*      upd_field
 
1119
                                = upd_get_nth_field(update, i);
 
1120
 
 
1121
                        if (upd_field->field_no != clust_pos) {
 
1122
 
 
1123
                                continue;
 
1124
                        }
 
1125
 
 
1126
                        dfield_copy_data(dfield, &upd_field->new_val);
 
1127
                        break;
 
1128
                }
 
1129
 
 
1130
                if (dfield_is_ext(dfield) && col->ord_part) {
 
1131
                        ext_cols[n_ext_cols++] = col_no;
 
1132
                }
 
1133
        }
 
1134
 
 
1135
        if (n_ext_cols) {
 
1136
                *ext = row_ext_create(n_ext_cols, ext_cols, row,
 
1137
                                      dict_table_zip_size(table), heap);
 
1138
        } else {
 
1139
                *ext = NULL;
 
1140
        }
 
1141
}
 
1142
 
 
1143
/***************************************************************
 
1144
Checks if an update vector changes an ordering field of an index record.
 
1145
This function is fast if the update vector is short or the number of ordering
 
1146
fields in the index is small. Otherwise, this can be quadratic.
 
1147
NOTE: we compare the fields as binary strings! */
 
1148
UNIV_INTERN
 
1149
ibool
 
1150
row_upd_changes_ord_field_binary(
 
1151
/*=============================*/
 
1152
                                /* out: TRUE if update vector changes
 
1153
                                an ordering field in the index record;
 
1154
                                NOTE: the fields are compared as binary
 
1155
                                strings */
 
1156
        const dtuple_t* row,    /* in: old value of row, or NULL if the
 
1157
                                row and the data values in update are not
 
1158
                                known when this function is called, e.g., at
 
1159
                                compile time */
 
1160
        dict_index_t*   index,  /* in: index of the record */
 
1161
        const upd_t*    update) /* in: update vector for the row; NOTE: the
 
1162
                                field numbers in this MUST be clustered index
 
1163
                                positions! */
 
1164
{
 
1165
        ulint           n_unique;
 
1166
        ulint           n_upd_fields;
 
1167
        ulint           i, j;
 
1168
        dict_index_t*   clust_index;
 
1169
 
 
1170
        ut_ad(update && index);
 
1171
 
 
1172
        n_unique = dict_index_get_n_unique(index);
 
1173
        n_upd_fields = upd_get_n_fields(update);
 
1174
 
 
1175
        clust_index = dict_table_get_first_index(index->table);
 
1176
 
 
1177
        for (i = 0; i < n_unique; i++) {
 
1178
 
 
1179
                const dict_field_t*     ind_field;
 
1180
                const dict_col_t*       col;
 
1181
                ulint                   col_pos;
 
1182
                ulint                   col_no;
 
1183
 
 
1184
                ind_field = dict_index_get_nth_field(index, i);
 
1185
                col = dict_field_get_col(ind_field);
 
1186
                col_pos = dict_col_get_clust_pos(col, clust_index);
 
1187
                col_no = dict_col_get_no(col);
 
1188
 
 
1189
                for (j = 0; j < n_upd_fields; j++) {
 
1190
 
 
1191
                        const upd_field_t*      upd_field
 
1192
                                = upd_get_nth_field(update, j);
 
1193
 
 
1194
                        /* Note that if the index field is a column prefix
 
1195
                        then it may be that row does not contain an externally
 
1196
                        stored part of the column value, and we cannot compare
 
1197
                        the datas */
 
1198
 
 
1199
                        if (col_pos == upd_field->field_no
 
1200
                            && (row == NULL
 
1201
                                || ind_field->prefix_len > 0
 
1202
                                || !dfield_datas_are_binary_equal(
 
1203
                                        dtuple_get_nth_field(row, col_no),
 
1204
                                        &(upd_field->new_val)))) {
 
1205
 
 
1206
                                return(TRUE);
 
1207
                        }
 
1208
                }
 
1209
        }
 
1210
 
 
1211
        return(FALSE);
 
1212
}
 
1213
 
 
1214
/***************************************************************
 
1215
Checks if an update vector changes an ordering field of an index record.
 
1216
NOTE: we compare the fields as binary strings! */
 
1217
UNIV_INTERN
 
1218
ibool
 
1219
row_upd_changes_some_index_ord_field_binary(
 
1220
/*========================================*/
 
1221
                                        /* out: TRUE if update vector
 
1222
                                        may change an ordering field
 
1223
                                        in an index record */
 
1224
        const dict_table_t*     table,  /* in: table */
 
1225
        const upd_t*            update) /* in: update vector for the row */
 
1226
{
 
1227
        upd_field_t*    upd_field;
 
1228
        dict_index_t*   index;
 
1229
        ulint           i;
 
1230
 
 
1231
        index = dict_table_get_first_index(table);
 
1232
 
 
1233
        for (i = 0; i < upd_get_n_fields(update); i++) {
 
1234
 
 
1235
                upd_field = upd_get_nth_field(update, i);
 
1236
 
 
1237
                if (dict_field_get_col(dict_index_get_nth_field(
 
1238
                                               index, upd_field->field_no))
 
1239
                    ->ord_part) {
 
1240
 
 
1241
                        return(TRUE);
 
1242
                }
 
1243
        }
 
1244
 
 
1245
        return(FALSE);
 
1246
}
 
1247
 
 
1248
/***************************************************************
 
1249
Checks if an update vector changes some of the first ordering fields of an
 
1250
index record. This is only used in foreign key checks and we can assume
 
1251
that index does not contain column prefixes. */
 
1252
static
 
1253
ibool
 
1254
row_upd_changes_first_fields_binary(
 
1255
/*================================*/
 
1256
                                /* out: TRUE if changes */
 
1257
        dtuple_t*       entry,  /* in: index entry */
 
1258
        dict_index_t*   index,  /* in: index of entry */
 
1259
        const upd_t*    update, /* in: update vector for the row */
 
1260
        ulint           n)      /* in: how many first fields to check */
 
1261
{
 
1262
        ulint           n_upd_fields;
 
1263
        ulint           i, j;
 
1264
        dict_index_t*   clust_index;
 
1265
 
 
1266
        ut_ad(update && index);
 
1267
        ut_ad(n <= dict_index_get_n_fields(index));
 
1268
 
 
1269
        n_upd_fields = upd_get_n_fields(update);
 
1270
        clust_index = dict_table_get_first_index(index->table);
 
1271
 
 
1272
        for (i = 0; i < n; i++) {
 
1273
 
 
1274
                const dict_field_t*     ind_field;
 
1275
                const dict_col_t*       col;
 
1276
                ulint                   col_pos;
 
1277
 
 
1278
                ind_field = dict_index_get_nth_field(index, i);
 
1279
                col = dict_field_get_col(ind_field);
 
1280
                col_pos = dict_col_get_clust_pos(col, clust_index);
 
1281
 
 
1282
                ut_a(ind_field->prefix_len == 0);
 
1283
 
 
1284
                for (j = 0; j < n_upd_fields; j++) {
 
1285
 
 
1286
                        upd_field_t*    upd_field
 
1287
                                = upd_get_nth_field(update, j);
 
1288
 
 
1289
                        if (col_pos == upd_field->field_no
 
1290
                            && !dfield_datas_are_binary_equal(
 
1291
                                    dtuple_get_nth_field(entry, i),
 
1292
                                    &(upd_field->new_val))) {
 
1293
 
 
1294
                                return(TRUE);
 
1295
                        }
 
1296
                }
 
1297
        }
 
1298
 
 
1299
        return(FALSE);
 
1300
}
 
1301
 
 
1302
/*************************************************************************
 
1303
Copies the column values from a record. */
 
1304
UNIV_INLINE
 
1305
void
 
1306
row_upd_copy_columns(
 
1307
/*=================*/
 
1308
        rec_t*          rec,    /* in: record in a clustered index */
 
1309
        const ulint*    offsets,/* in: array returned by rec_get_offsets() */
 
1310
        sym_node_t*     column) /* in: first column in a column list, or
 
1311
                                NULL */
 
1312
{
 
1313
        byte*   data;
 
1314
        ulint   len;
 
1315
 
 
1316
        while (column) {
 
1317
                data = rec_get_nth_field(rec, offsets,
 
1318
                                         column->field_nos[SYM_CLUST_FIELD_NO],
 
1319
                                         &len);
 
1320
                if (len == UNIV_SQL_NULL) {
 
1321
                        len = UNIV_SQL_NULL;
 
1322
                }
 
1323
                eval_node_copy_and_alloc_val(column, data, len);
 
1324
 
 
1325
                column = UT_LIST_GET_NEXT(col_var_list, column);
 
1326
        }
 
1327
}
 
1328
 
 
1329
/*************************************************************************
 
1330
Calculates the new values for fields to update. Note that row_upd_copy_columns
 
1331
must have been called first. */
 
1332
UNIV_INLINE
 
1333
void
 
1334
row_upd_eval_new_vals(
 
1335
/*==================*/
 
1336
        upd_t*  update) /* in/out: update vector */
 
1337
{
 
1338
        que_node_t*     exp;
 
1339
        upd_field_t*    upd_field;
 
1340
        ulint           n_fields;
 
1341
        ulint           i;
 
1342
 
 
1343
        n_fields = upd_get_n_fields(update);
 
1344
 
 
1345
        for (i = 0; i < n_fields; i++) {
 
1346
                upd_field = upd_get_nth_field(update, i);
 
1347
 
 
1348
                exp = upd_field->exp;
 
1349
 
 
1350
                eval_exp(exp);
 
1351
 
 
1352
                dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
 
1353
        }
 
1354
}
 
1355
 
 
1356
/***************************************************************
 
1357
Stores to the heap the row on which the node->pcur is positioned. */
 
1358
static
 
1359
void
 
1360
row_upd_store_row(
 
1361
/*==============*/
 
1362
        upd_node_t*     node)   /* in: row update node */
 
1363
{
 
1364
        dict_index_t*   clust_index;
 
1365
        rec_t*          rec;
 
1366
        mem_heap_t*     heap            = NULL;
 
1367
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1368
        const ulint*    offsets;
 
1369
        rec_offs_init(offsets_);
 
1370
 
 
1371
        ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
 
1372
 
 
1373
        if (node->row != NULL) {
 
1374
                mem_heap_empty(node->heap);
 
1375
        }
 
1376
 
 
1377
        clust_index = dict_table_get_first_index(node->table);
 
1378
 
 
1379
        rec = btr_pcur_get_rec(node->pcur);
 
1380
 
 
1381
        offsets = rec_get_offsets(rec, clust_index, offsets_,
 
1382
                                  ULINT_UNDEFINED, &heap);
 
1383
        node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
 
1384
                              NULL, &node->ext, node->heap);
 
1385
        if (node->is_delete) {
 
1386
                node->upd_row = NULL;
 
1387
                node->upd_ext = NULL;
 
1388
        } else {
 
1389
                node->upd_row = dtuple_copy(node->row, node->heap);
 
1390
                row_upd_replace(node->upd_row, &node->upd_ext,
 
1391
                                clust_index, node->update, node->heap);
 
1392
        }
 
1393
 
 
1394
        if (UNIV_LIKELY_NULL(heap)) {
 
1395
                mem_heap_free(heap);
 
1396
        }
 
1397
}
 
1398
 
 
1399
/***************************************************************
 
1400
Updates a secondary index entry of a row. */
 
1401
static
 
1402
ulint
 
1403
row_upd_sec_index_entry(
 
1404
/*====================*/
 
1405
                                /* out: DB_SUCCESS if operation successfully
 
1406
                                completed, else error code or DB_LOCK_WAIT */
 
1407
        upd_node_t*     node,   /* in: row update node */
 
1408
        que_thr_t*      thr)    /* in: query thread */
 
1409
{
 
1410
        ibool           check_ref;
 
1411
        ibool           found;
 
1412
        dict_index_t*   index;
 
1413
        dtuple_t*       entry;
 
1414
        btr_pcur_t      pcur;
 
1415
        btr_cur_t*      btr_cur;
 
1416
        mem_heap_t*     heap;
 
1417
        rec_t*          rec;
 
1418
        ulint           err     = DB_SUCCESS;
 
1419
        mtr_t           mtr;
 
1420
        trx_t*          trx     = thr_get_trx(thr);
 
1421
 
 
1422
        index = node->index;
 
1423
 
 
1424
        check_ref = row_upd_index_is_referenced(index, trx);
 
1425
 
 
1426
        heap = mem_heap_create(1024);
 
1427
 
 
1428
        /* Build old index entry */
 
1429
        entry = row_build_index_entry(node->row, node->ext, index, heap);
 
1430
        ut_a(entry);
 
1431
 
 
1432
        log_free_check();
 
1433
        mtr_start(&mtr);
 
1434
 
 
1435
        found = row_search_index_entry(index, entry, BTR_MODIFY_LEAF, &pcur,
 
1436
                                       &mtr);
 
1437
        btr_cur = btr_pcur_get_btr_cur(&pcur);
 
1438
 
 
1439
        rec = btr_cur_get_rec(btr_cur);
 
1440
 
 
1441
        if (UNIV_UNLIKELY(!found)) {
 
1442
                fputs("InnoDB: error in sec index entry update in\n"
 
1443
                      "InnoDB: ", stderr);
 
1444
                dict_index_name_print(stderr, trx, index);
 
1445
                fputs("\n"
 
1446
                      "InnoDB: tuple ", stderr);
 
1447
                dtuple_print(stderr, entry);
 
1448
                fputs("\n"
 
1449
                      "InnoDB: record ", stderr);
 
1450
                rec_print(stderr, rec, index);
 
1451
                putc('\n', stderr);
 
1452
 
 
1453
                trx_print(stderr, trx, 0);
 
1454
 
 
1455
                fputs("\n"
 
1456
                      "InnoDB: Submit a detailed bug report"
 
1457
                      " to http://bugs.mysql.com\n", stderr);
 
1458
        } else {
 
1459
                /* Delete mark the old index record; it can already be
 
1460
                delete marked if we return after a lock wait in
 
1461
                row_ins_index_entry below */
 
1462
 
 
1463
                if (!rec_get_deleted_flag(rec,
 
1464
                                          dict_table_is_comp(index->table))) {
 
1465
                        err = btr_cur_del_mark_set_sec_rec(0, btr_cur, TRUE,
 
1466
                                                           thr, &mtr);
 
1467
                        if (err == DB_SUCCESS && check_ref) {
 
1468
 
 
1469
                                ulint*  offsets = rec_get_offsets(
 
1470
                                        rec, index, NULL,
 
1471
                                        ULINT_UNDEFINED, &heap);
 
1472
                                /* NOTE that the following call loses
 
1473
                                the position of pcur ! */
 
1474
                                err = row_upd_check_references_constraints(
 
1475
                                        node, &pcur, index->table,
 
1476
                                        index, offsets, thr, &mtr);
 
1477
                        }
 
1478
                }
 
1479
        }
 
1480
 
 
1481
        btr_pcur_close(&pcur);
 
1482
        mtr_commit(&mtr);
 
1483
 
 
1484
        if (node->is_delete || err != DB_SUCCESS) {
 
1485
 
 
1486
                goto func_exit;
 
1487
        }
 
1488
 
 
1489
        /* Build a new index entry */
 
1490
        entry = row_build_index_entry(node->upd_row, node->upd_ext,
 
1491
                                      index, heap);
 
1492
        ut_a(entry);
 
1493
 
 
1494
        /* Insert new index entry */
 
1495
        err = row_ins_index_entry(index, entry, 0, TRUE, thr);
 
1496
 
 
1497
func_exit:
 
1498
        mem_heap_free(heap);
 
1499
 
 
1500
        return(err);
 
1501
}
 
1502
 
 
1503
/***************************************************************
 
1504
Updates the secondary index record if it is changed in the row update or
 
1505
deletes it if this is a delete. */
 
1506
UNIV_INLINE
 
1507
ulint
 
1508
row_upd_sec_step(
 
1509
/*=============*/
 
1510
                                /* out: DB_SUCCESS if operation successfully
 
1511
                                completed, else error code or DB_LOCK_WAIT */
 
1512
        upd_node_t*     node,   /* in: row update node */
 
1513
        que_thr_t*      thr)    /* in: query thread */
 
1514
{
 
1515
        ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
 
1516
              || (node->state == UPD_NODE_UPDATE_SOME_SEC));
 
1517
        ut_ad(!dict_index_is_clust(node->index));
 
1518
 
 
1519
        if (node->state == UPD_NODE_UPDATE_ALL_SEC
 
1520
            || row_upd_changes_ord_field_binary(node->row, node->index,
 
1521
                                                node->update)) {
 
1522
                return(row_upd_sec_index_entry(node, thr));
 
1523
        }
 
1524
 
 
1525
        return(DB_SUCCESS);
 
1526
}
 
1527
 
 
1528
/***************************************************************
 
1529
Marks the clustered index record deleted and inserts the updated version
 
1530
of the record to the index. This function should be used when the ordering
 
1531
fields of the clustered index record change. This should be quite rare in
 
1532
database applications. */
 
1533
static
 
1534
ulint
 
1535
row_upd_clust_rec_by_insert(
 
1536
/*========================*/
 
1537
                                /* out: DB_SUCCESS if operation successfully
 
1538
                                completed, else error code or DB_LOCK_WAIT */
 
1539
        upd_node_t*     node,   /* in: row update node */
 
1540
        dict_index_t*   index,  /* in: clustered index of the record */
 
1541
        que_thr_t*      thr,    /* in: query thread */
 
1542
        ibool           check_ref,/* in: TRUE if index may be referenced in
 
1543
                                a foreign key constraint */
 
1544
        mtr_t*          mtr)    /* in: mtr; gets committed here */
 
1545
{
 
1546
        mem_heap_t*     heap    = NULL;
 
1547
        btr_pcur_t*     pcur;
 
1548
        btr_cur_t*      btr_cur;
 
1549
        trx_t*          trx;
 
1550
        dict_table_t*   table;
 
1551
        dtuple_t*       entry;
 
1552
        ulint           err;
 
1553
 
 
1554
        ut_ad(node);
 
1555
        ut_ad(dict_index_is_clust(index));
 
1556
 
 
1557
        trx = thr_get_trx(thr);
 
1558
        table = node->table;
 
1559
        pcur = node->pcur;
 
1560
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
1561
 
 
1562
        if (node->state != UPD_NODE_INSERT_CLUSTERED) {
 
1563
                rec_t*          rec;
 
1564
                dict_index_t*   index;
 
1565
                ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1566
                ulint*          offsets;
 
1567
                rec_offs_init(offsets_);
 
1568
 
 
1569
                err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
 
1570
                                                     btr_cur, TRUE, thr, mtr);
 
1571
                if (err != DB_SUCCESS) {
 
1572
                        mtr_commit(mtr);
 
1573
                        return(err);
 
1574
                }
 
1575
 
 
1576
                /* Mark as not-owned the externally stored fields which the new
 
1577
                row inherits from the delete marked record: purge should not
 
1578
                free those externally stored fields even if the delete marked
 
1579
                record is removed from the index tree, or updated. */
 
1580
 
 
1581
                rec = btr_cur_get_rec(btr_cur);
 
1582
                index = dict_table_get_first_index(table);
 
1583
                offsets = rec_get_offsets(rec, index, offsets_,
 
1584
                                          ULINT_UNDEFINED, &heap);
 
1585
                btr_cur_mark_extern_inherited_fields(
 
1586
                        btr_cur_get_page_zip(btr_cur),
 
1587
                        rec, index, offsets, node->update, mtr);
 
1588
                if (check_ref) {
 
1589
                        /* NOTE that the following call loses
 
1590
                        the position of pcur ! */
 
1591
                        err = row_upd_check_references_constraints(
 
1592
                                node, pcur, table, index, offsets, thr, mtr);
 
1593
                        if (err != DB_SUCCESS) {
 
1594
                                mtr_commit(mtr);
 
1595
                                if (UNIV_LIKELY_NULL(heap)) {
 
1596
                                        mem_heap_free(heap);
 
1597
                                }
 
1598
                                return(err);
 
1599
                        }
 
1600
                }
 
1601
        }
 
1602
 
 
1603
        mtr_commit(mtr);
 
1604
 
 
1605
        if (!heap) {
 
1606
                heap = mem_heap_create(500);
 
1607
        }
 
1608
        node->state = UPD_NODE_INSERT_CLUSTERED;
 
1609
 
 
1610
        entry = row_build_index_entry(node->upd_row, node->upd_ext,
 
1611
                                      index, heap);
 
1612
        ut_a(entry);
 
1613
 
 
1614
        row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
 
1615
 
 
1616
        if (node->upd_ext) {
 
1617
                /* If we return from a lock wait, for example, we may have
 
1618
                extern fields marked as not-owned in entry (marked in the
 
1619
                if-branch above). We must unmark them. */
 
1620
 
 
1621
                btr_cur_unmark_dtuple_extern_fields(entry);
 
1622
 
 
1623
                /* We must mark non-updated extern fields in entry as
 
1624
                inherited, so that a possible rollback will not free them. */
 
1625
 
 
1626
                btr_cur_mark_dtuple_inherited_extern(entry, node->update);
 
1627
        }
 
1628
 
 
1629
        err = row_ins_index_entry(index, entry,
 
1630
                                  node->upd_ext ? node->upd_ext->n_ext : 0,
 
1631
                                  TRUE, thr);
 
1632
        mem_heap_free(heap);
 
1633
 
 
1634
        return(err);
 
1635
}
 
1636
 
 
1637
/***************************************************************
 
1638
Updates a clustered index record of a row when the ordering fields do
 
1639
not change. */
 
1640
static
 
1641
ulint
 
1642
row_upd_clust_rec(
 
1643
/*==============*/
 
1644
                                /* out: DB_SUCCESS if operation successfully
 
1645
                                completed, else error code or DB_LOCK_WAIT */
 
1646
        upd_node_t*     node,   /* in: row update node */
 
1647
        dict_index_t*   index,  /* in: clustered index */
 
1648
        que_thr_t*      thr,    /* in: query thread */
 
1649
        mtr_t*          mtr)    /* in: mtr; gets committed here */
 
1650
{
 
1651
        mem_heap_t*     heap    = NULL;
 
1652
        big_rec_t*      big_rec = NULL;
 
1653
        btr_pcur_t*     pcur;
 
1654
        btr_cur_t*      btr_cur;
 
1655
        ulint           err;
 
1656
 
 
1657
        ut_ad(node);
 
1658
        ut_ad(dict_index_is_clust(index));
 
1659
 
 
1660
        pcur = node->pcur;
 
1661
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
1662
 
 
1663
        ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
 
1664
                                    dict_table_is_comp(index->table)));
 
1665
 
 
1666
        /* Try optimistic updating of the record, keeping changes within
 
1667
        the page; we do not check locks because we assume the x-lock on the
 
1668
        record to update */
 
1669
 
 
1670
        if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
 
1671
                err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG,
 
1672
                                              btr_cur, node->update,
 
1673
                                              node->cmpl_info, thr, mtr);
 
1674
        } else {
 
1675
                err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG,
 
1676
                                                btr_cur, node->update,
 
1677
                                                node->cmpl_info, thr, mtr);
 
1678
        }
 
1679
 
 
1680
        mtr_commit(mtr);
 
1681
 
 
1682
        if (UNIV_LIKELY(err == DB_SUCCESS)) {
 
1683
 
 
1684
                return(DB_SUCCESS);
 
1685
        }
 
1686
 
 
1687
        if (buf_LRU_buf_pool_running_out()) {
 
1688
 
 
1689
                return(DB_LOCK_TABLE_FULL);
 
1690
        }
 
1691
        /* We may have to modify the tree structure: do a pessimistic descent
 
1692
        down the index tree */
 
1693
 
 
1694
        mtr_start(mtr);
 
1695
 
 
1696
        /* NOTE: this transaction has an s-lock or x-lock on the record and
 
1697
        therefore other transactions cannot modify the record when we have no
 
1698
        latch on the page. In addition, we assume that other query threads of
 
1699
        the same transaction do not modify the record in the meantime.
 
1700
        Therefore we can assert that the restoration of the cursor succeeds. */
 
1701
 
 
1702
        ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
 
1703
 
 
1704
        ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
 
1705
                                    dict_table_is_comp(index->table)));
 
1706
 
 
1707
        err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur,
 
1708
                                         &heap, &big_rec, node->update,
 
1709
                                         node->cmpl_info, thr, mtr);
 
1710
        mtr_commit(mtr);
 
1711
 
 
1712
        if (err == DB_SUCCESS && big_rec) {
 
1713
                ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1714
                rec_t*          rec;
 
1715
                rec_offs_init(offsets_);
 
1716
 
 
1717
                mtr_start(mtr);
 
1718
 
 
1719
                ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
 
1720
                rec = btr_cur_get_rec(btr_cur);
 
1721
                err = btr_store_big_rec_extern_fields(
 
1722
                        index, btr_cur_get_block(btr_cur), rec,
 
1723
                        rec_get_offsets(rec, index, offsets_,
 
1724
                                        ULINT_UNDEFINED, &heap),
 
1725
                        big_rec, mtr);
 
1726
                mtr_commit(mtr);
 
1727
        }
 
1728
 
 
1729
        if (UNIV_LIKELY_NULL(heap)) {
 
1730
                mem_heap_free(heap);
 
1731
        }
 
1732
 
 
1733
        if (big_rec) {
 
1734
                dtuple_big_rec_free(big_rec);
 
1735
        }
 
1736
 
 
1737
        return(err);
 
1738
}
 
1739
 
 
1740
/***************************************************************
 
1741
Delete marks a clustered index record. */
 
1742
static
 
1743
ulint
 
1744
row_upd_del_mark_clust_rec(
 
1745
/*=======================*/
 
1746
                                /* out: DB_SUCCESS if operation successfully
 
1747
                                completed, else error code */
 
1748
        upd_node_t*     node,   /* in: row update node */
 
1749
        dict_index_t*   index,  /* in: clustered index */
 
1750
        ulint*          offsets,/* in/out: rec_get_offsets() for the
 
1751
                                record under the cursor */
 
1752
        que_thr_t*      thr,    /* in: query thread */
 
1753
        ibool           check_ref,/* in: TRUE if index may be referenced in
 
1754
                                a foreign key constraint */
 
1755
        mtr_t*          mtr)    /* in: mtr; gets committed here */
 
1756
{
 
1757
        btr_pcur_t*     pcur;
 
1758
        btr_cur_t*      btr_cur;
 
1759
        ulint           err;
 
1760
 
 
1761
        ut_ad(node);
 
1762
        ut_ad(dict_index_is_clust(index));
 
1763
        ut_ad(node->is_delete);
 
1764
 
 
1765
        pcur = node->pcur;
 
1766
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
1767
 
 
1768
        /* Store row because we have to build also the secondary index
 
1769
        entries */
 
1770
 
 
1771
        row_upd_store_row(node);
 
1772
 
 
1773
        /* Mark the clustered index record deleted; we do not have to check
 
1774
        locks, because we assume that we have an x-lock on the record */
 
1775
 
 
1776
        err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
 
1777
                                             btr_cur, TRUE, thr, mtr);
 
1778
        if (err == DB_SUCCESS && check_ref) {
 
1779
                /* NOTE that the following call loses the position of pcur ! */
 
1780
 
 
1781
                err = row_upd_check_references_constraints(node,
 
1782
                                                           pcur, index->table,
 
1783
                                                           index, offsets,
 
1784
                                                           thr, mtr);
 
1785
        }
 
1786
 
 
1787
        mtr_commit(mtr);
 
1788
 
 
1789
        return(err);
 
1790
}
 
1791
 
 
1792
/***************************************************************
 
1793
Updates the clustered index record. */
 
1794
static
 
1795
ulint
 
1796
row_upd_clust_step(
 
1797
/*===============*/
 
1798
                                /* out: DB_SUCCESS if operation successfully
 
1799
                                completed, DB_LOCK_WAIT in case of a lock wait,
 
1800
                                else error code */
 
1801
        upd_node_t*     node,   /* in: row update node */
 
1802
        que_thr_t*      thr)    /* in: query thread */
 
1803
{
 
1804
        dict_index_t*   index;
 
1805
        btr_pcur_t*     pcur;
 
1806
        ibool           success;
 
1807
        ibool           check_ref;
 
1808
        ulint           err;
 
1809
        mtr_t*          mtr;
 
1810
        mtr_t           mtr_buf;
 
1811
        rec_t*          rec;
 
1812
        mem_heap_t*     heap            = NULL;
 
1813
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
1814
        ulint*          offsets;
 
1815
        rec_offs_init(offsets_);
 
1816
 
 
1817
        index = dict_table_get_first_index(node->table);
 
1818
 
 
1819
        check_ref = row_upd_index_is_referenced(index, thr_get_trx(thr));
 
1820
 
 
1821
        pcur = node->pcur;
 
1822
 
 
1823
        /* We have to restore the cursor to its position */
 
1824
        mtr = &mtr_buf;
 
1825
 
 
1826
        mtr_start(mtr);
 
1827
 
 
1828
        /* If the restoration does not succeed, then the same
 
1829
        transaction has deleted the record on which the cursor was,
 
1830
        and that is an SQL error. If the restoration succeeds, it may
 
1831
        still be that the same transaction has successively deleted
 
1832
        and inserted a record with the same ordering fields, but in
 
1833
        that case we know that the transaction has at least an
 
1834
        implicit x-lock on the record. */
 
1835
 
 
1836
        ut_a(pcur->rel_pos == BTR_PCUR_ON);
 
1837
 
 
1838
        success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr);
 
1839
 
 
1840
        if (!success) {
 
1841
                err = DB_RECORD_NOT_FOUND;
 
1842
 
 
1843
                mtr_commit(mtr);
 
1844
 
 
1845
                return(err);
 
1846
        }
 
1847
 
 
1848
        /* If this is a row in SYS_INDEXES table of the data dictionary,
 
1849
        then we have to free the file segments of the index tree associated
 
1850
        with the index */
 
1851
 
 
1852
        if (node->is_delete
 
1853
            && ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) {
 
1854
 
 
1855
                dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr);
 
1856
 
 
1857
                mtr_commit(mtr);
 
1858
 
 
1859
                mtr_start(mtr);
 
1860
 
 
1861
                success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
 
1862
                                                    mtr);
 
1863
                if (!success) {
 
1864
                        err = DB_ERROR;
 
1865
 
 
1866
                        mtr_commit(mtr);
 
1867
 
 
1868
                        return(err);
 
1869
                }
 
1870
        }
 
1871
 
 
1872
        rec = btr_pcur_get_rec(pcur);
 
1873
        offsets = rec_get_offsets(rec, index, offsets_,
 
1874
                                  ULINT_UNDEFINED, &heap);
 
1875
 
 
1876
        if (!node->has_clust_rec_x_lock) {
 
1877
                err = lock_clust_rec_modify_check_and_lock(
 
1878
                        0, btr_pcur_get_block(pcur),
 
1879
                        rec, index, offsets, thr);
 
1880
                if (err != DB_SUCCESS) {
 
1881
                        mtr_commit(mtr);
 
1882
                        goto exit_func;
 
1883
                }
 
1884
        }
 
1885
 
 
1886
        /* NOTE: the following function calls will also commit mtr */
 
1887
 
 
1888
        if (node->is_delete) {
 
1889
                err = row_upd_del_mark_clust_rec(node, index, offsets,
 
1890
                                                 thr, check_ref, mtr);
 
1891
                if (err == DB_SUCCESS) {
 
1892
                        node->state = UPD_NODE_UPDATE_ALL_SEC;
 
1893
                        node->index = dict_table_get_next_index(index);
 
1894
                }
 
1895
exit_func:
 
1896
                if (UNIV_LIKELY_NULL(heap)) {
 
1897
                        mem_heap_free(heap);
 
1898
                }
 
1899
                return(err);
 
1900
        }
 
1901
 
 
1902
        /* If the update is made for MySQL, we already have the update vector
 
1903
        ready, else we have to do some evaluation: */
 
1904
 
 
1905
        if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
 
1906
                /* Copy the necessary columns from clust_rec and calculate the
 
1907
                new values to set */
 
1908
                row_upd_copy_columns(rec, offsets,
 
1909
                                     UT_LIST_GET_FIRST(node->columns));
 
1910
                row_upd_eval_new_vals(node->update);
 
1911
        }
 
1912
 
 
1913
        if (UNIV_LIKELY_NULL(heap)) {
 
1914
                mem_heap_free(heap);
 
1915
        }
 
1916
 
 
1917
        if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
 
1918
 
 
1919
                err = row_upd_clust_rec(node, index, thr, mtr);
 
1920
                return(err);
 
1921
        }
 
1922
 
 
1923
        row_upd_store_row(node);
 
1924
 
 
1925
        if (row_upd_changes_ord_field_binary(node->row, index, node->update)) {
 
1926
 
 
1927
                /* Update causes an ordering field (ordering fields within
 
1928
                the B-tree) of the clustered index record to change: perform
 
1929
                the update by delete marking and inserting.
 
1930
 
 
1931
                TODO! What to do to the 'Halloween problem', where an update
 
1932
                moves the record forward in index so that it is again
 
1933
                updated when the cursor arrives there? Solution: the
 
1934
                read operation must check the undo record undo number when
 
1935
                choosing records to update. MySQL solves now the problem
 
1936
                externally! */
 
1937
 
 
1938
                err = row_upd_clust_rec_by_insert(node, index, thr, check_ref,
 
1939
                                                  mtr);
 
1940
                if (err != DB_SUCCESS) {
 
1941
 
 
1942
                        return(err);
 
1943
                }
 
1944
 
 
1945
                node->state = UPD_NODE_UPDATE_ALL_SEC;
 
1946
        } else {
 
1947
                err = row_upd_clust_rec(node, index, thr, mtr);
 
1948
 
 
1949
                if (err != DB_SUCCESS) {
 
1950
 
 
1951
                        return(err);
 
1952
                }
 
1953
 
 
1954
                node->state = UPD_NODE_UPDATE_SOME_SEC;
 
1955
        }
 
1956
 
 
1957
        node->index = dict_table_get_next_index(index);
 
1958
 
 
1959
        return(err);
 
1960
}
 
1961
 
 
1962
/***************************************************************
 
1963
Updates the affected index records of a row. When the control is transferred
 
1964
to this node, we assume that we have a persistent cursor which was on a
 
1965
record, and the position of the cursor is stored in the cursor. */
 
1966
static
 
1967
ulint
 
1968
row_upd(
 
1969
/*====*/
 
1970
                                /* out: DB_SUCCESS if operation successfully
 
1971
                                completed, else error code or DB_LOCK_WAIT */
 
1972
        upd_node_t*     node,   /* in: row update node */
 
1973
        que_thr_t*      thr)    /* in: query thread */
 
1974
{
 
1975
        ulint   err     = DB_SUCCESS;
 
1976
 
 
1977
        ut_ad(node && thr);
 
1978
 
 
1979
        if (UNIV_LIKELY(node->in_mysql_interface)) {
 
1980
 
 
1981
                /* We do not get the cmpl_info value from the MySQL
 
1982
                interpreter: we must calculate it on the fly: */
 
1983
 
 
1984
                if (node->is_delete
 
1985
                    || row_upd_changes_some_index_ord_field_binary(
 
1986
                            node->table, node->update)) {
 
1987
                        node->cmpl_info = 0;
 
1988
                } else {
 
1989
                        node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
 
1990
                }
 
1991
        }
 
1992
 
 
1993
        if (node->state == UPD_NODE_UPDATE_CLUSTERED
 
1994
            || node->state == UPD_NODE_INSERT_CLUSTERED) {
 
1995
 
 
1996
                err = row_upd_clust_step(node, thr);
 
1997
 
 
1998
                if (err != DB_SUCCESS) {
 
1999
 
 
2000
                        goto function_exit;
 
2001
                }
 
2002
        }
 
2003
 
 
2004
        if (!node->is_delete && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
 
2005
 
 
2006
                goto function_exit;
 
2007
        }
 
2008
 
 
2009
        while (node->index != NULL) {
 
2010
                err = row_upd_sec_step(node, thr);
 
2011
 
 
2012
                if (err != DB_SUCCESS) {
 
2013
 
 
2014
                        goto function_exit;
 
2015
                }
 
2016
 
 
2017
                node->index = dict_table_get_next_index(node->index);
 
2018
        }
 
2019
 
 
2020
function_exit:
 
2021
        if (err == DB_SUCCESS) {
 
2022
                /* Do some cleanup */
 
2023
 
 
2024
                if (node->row != NULL) {
 
2025
                        node->row = NULL;
 
2026
                        node->ext = NULL;
 
2027
                        node->upd_row = NULL;
 
2028
                        node->upd_ext = NULL;
 
2029
                        mem_heap_empty(node->heap);
 
2030
                }
 
2031
 
 
2032
                node->state = UPD_NODE_UPDATE_CLUSTERED;
 
2033
        }
 
2034
 
 
2035
        return(err);
 
2036
}
 
2037
 
 
2038
/***************************************************************
 
2039
Updates a row in a table. This is a high-level function used in SQL execution
 
2040
graphs. */
 
2041
UNIV_INTERN
 
2042
que_thr_t*
 
2043
row_upd_step(
 
2044
/*=========*/
 
2045
                                /* out: query thread to run next or NULL */
 
2046
        que_thr_t*      thr)    /* in: query thread */
 
2047
{
 
2048
        upd_node_t*     node;
 
2049
        sel_node_t*     sel_node;
 
2050
        que_node_t*     parent;
 
2051
        ulint           err             = DB_SUCCESS;
 
2052
        trx_t*          trx;
 
2053
 
 
2054
        ut_ad(thr);
 
2055
 
 
2056
        trx = thr_get_trx(thr);
 
2057
 
 
2058
        trx_start_if_not_started(trx);
 
2059
 
 
2060
        node = thr->run_node;
 
2061
 
 
2062
        sel_node = node->select;
 
2063
 
 
2064
        parent = que_node_get_parent(node);
 
2065
 
 
2066
        ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
 
2067
 
 
2068
        if (thr->prev_node == parent) {
 
2069
                node->state = UPD_NODE_SET_IX_LOCK;
 
2070
        }
 
2071
 
 
2072
        if (node->state == UPD_NODE_SET_IX_LOCK) {
 
2073
 
 
2074
                if (!node->has_clust_rec_x_lock) {
 
2075
                        /* It may be that the current session has not yet
 
2076
                        started its transaction, or it has been committed: */
 
2077
 
 
2078
                        err = lock_table(0, node->table, LOCK_IX, thr);
 
2079
 
 
2080
                        if (err != DB_SUCCESS) {
 
2081
 
 
2082
                                goto error_handling;
 
2083
                        }
 
2084
                }
 
2085
 
 
2086
                node->state = UPD_NODE_UPDATE_CLUSTERED;
 
2087
 
 
2088
                if (node->searched_update) {
 
2089
                        /* Reset the cursor */
 
2090
                        sel_node->state = SEL_NODE_OPEN;
 
2091
 
 
2092
                        /* Fetch a row to update */
 
2093
 
 
2094
                        thr->run_node = sel_node;
 
2095
 
 
2096
                        return(thr);
 
2097
                }
 
2098
        }
 
2099
 
 
2100
        /* sel_node is NULL if we are in the MySQL interface */
 
2101
 
 
2102
        if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
 
2103
 
 
2104
                if (!node->searched_update) {
 
2105
                        /* An explicit cursor should be positioned on a row
 
2106
                        to update */
 
2107
 
 
2108
                        ut_error;
 
2109
 
 
2110
                        err = DB_ERROR;
 
2111
 
 
2112
                        goto error_handling;
 
2113
                }
 
2114
 
 
2115
                ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
 
2116
 
 
2117
                /* No more rows to update, or the select node performed the
 
2118
                updates directly in-place */
 
2119
 
 
2120
                thr->run_node = parent;
 
2121
 
 
2122
                return(thr);
 
2123
        }
 
2124
 
 
2125
        /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
 
2126
 
 
2127
        err = row_upd(node, thr);
 
2128
 
 
2129
error_handling:
 
2130
        trx->error_state = err;
 
2131
 
 
2132
        if (err != DB_SUCCESS) {
 
2133
                return(NULL);
 
2134
        }
 
2135
 
 
2136
        /* DO THE TRIGGER ACTIONS HERE */
 
2137
 
 
2138
        if (node->searched_update) {
 
2139
                /* Fetch next row to update */
 
2140
 
 
2141
                thr->run_node = sel_node;
 
2142
        } else {
 
2143
                /* It was an explicit cursor update */
 
2144
 
 
2145
                thr->run_node = parent;
 
2146
        }
 
2147
 
 
2148
        node->state = UPD_NODE_UPDATE_CLUSTERED;
 
2149
 
 
2150
        return(thr);
 
2151
}
 
2152
 
 
2153
/*************************************************************************
 
2154
Performs an in-place update for the current clustered index record in
 
2155
select. */
 
2156
UNIV_INTERN
 
2157
void
 
2158
row_upd_in_place_in_select(
 
2159
/*=======================*/
 
2160
        sel_node_t*     sel_node,       /* in: select node */
 
2161
        que_thr_t*      thr,            /* in: query thread */
 
2162
        mtr_t*          mtr)            /* in: mtr */
 
2163
{
 
2164
        upd_node_t*     node;
 
2165
        btr_pcur_t*     pcur;
 
2166
        btr_cur_t*      btr_cur;
 
2167
        ulint           err;
 
2168
        mem_heap_t*     heap            = NULL;
 
2169
        ulint           offsets_[REC_OFFS_NORMAL_SIZE];
 
2170
        rec_offs_init(offsets_);
 
2171
 
 
2172
        ut_ad(sel_node->select_will_do_update);
 
2173
        ut_ad(sel_node->latch_mode == BTR_MODIFY_LEAF);
 
2174
        ut_ad(sel_node->asc);
 
2175
 
 
2176
        node = que_node_get_parent(sel_node);
 
2177
 
 
2178
        ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
 
2179
 
 
2180
        pcur = node->pcur;
 
2181
        btr_cur = btr_pcur_get_btr_cur(pcur);
 
2182
 
 
2183
        /* Copy the necessary columns from clust_rec and calculate the new
 
2184
        values to set */
 
2185
 
 
2186
        row_upd_copy_columns(btr_pcur_get_rec(pcur),
 
2187
                             rec_get_offsets(btr_pcur_get_rec(pcur),
 
2188
                                             btr_cur->index, offsets_,
 
2189
                                             ULINT_UNDEFINED, &heap),
 
2190
                             UT_LIST_GET_FIRST(node->columns));
 
2191
        if (UNIV_LIKELY_NULL(heap)) {
 
2192
                mem_heap_free(heap);
 
2193
        }
 
2194
        row_upd_eval_new_vals(node->update);
 
2195
 
 
2196
        ut_ad(!rec_get_deleted_flag(
 
2197
                      btr_pcur_get_rec(pcur),
 
2198
                      dict_table_is_comp(btr_cur->index->table)));
 
2199
 
 
2200
        ut_ad(node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE);
 
2201
        ut_ad(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE);
 
2202
        ut_ad(node->select_will_do_update);
 
2203
 
 
2204
        err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG, btr_cur,
 
2205
                                      node->update, node->cmpl_info,
 
2206
                                      thr, mtr);
 
2207
        /* TODO: the above can fail with DB_ZIP_OVERFLOW if page_zip != NULL.
 
2208
        However, this function row_upd_in_place_in_select() is only invoked
 
2209
        when executing UPDATE statements of the built-in InnoDB SQL parser.
 
2210
        The built-in SQL is only used for InnoDB system tables, which
 
2211
        always are in the old, uncompressed format (ROW_FORMAT=REDUNDANT,
 
2212
        comp == FALSE, page_zip == NULL). */
 
2213
        ut_ad(err == DB_SUCCESS);
 
2214
}