Drizzled Public API Documentation

row0upd.cc

00001 /*****************************************************************************
00002 
00003 Copyright (C) 1996, 2010, Innobase Oy. All Rights Reserved.
00004 
00005 This program is free software; you can redistribute it and/or modify it under
00006 the terms of the GNU General Public License as published by the Free Software
00007 Foundation; version 2 of the License.
00008 
00009 This program is distributed in the hope that it will be useful, but WITHOUT
00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00011 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00012 
00013 You should have received a copy of the GNU General Public License along with
00014 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00015 St, Fifth Floor, Boston, MA 02110-1301 USA
00016 
00017 *****************************************************************************/
00018 
00019 /**************************************************/
00026 #include "row0upd.h"
00027 
00028 #ifdef UNIV_NONINL
00029 #include "row0upd.ic"
00030 #endif
00031 
00032 #include "dict0dict.h"
00033 #include "trx0undo.h"
00034 #include "rem0rec.h"
00035 #ifndef UNIV_HOTBACKUP
00036 #include "dict0boot.h"
00037 #include "dict0crea.h"
00038 #include "mach0data.h"
00039 #include "btr0btr.h"
00040 #include "btr0cur.h"
00041 #include "que0que.h"
00042 #include "row0ext.h"
00043 #include "row0ins.h"
00044 #include "row0sel.h"
00045 #include "row0row.h"
00046 #include "rem0cmp.h"
00047 #include "lock0lock.h"
00048 #include "log0log.h"
00049 #include "pars0sym.h"
00050 #include "eval0eval.h"
00051 #include "buf0lru.h"
00052 
00053 
00054 /* What kind of latch and lock can we assume when the control comes to
00055    -------------------------------------------------------------------
00056 an update node?
00057 --------------
00058 Efficiency of massive updates would require keeping an x-latch on a
00059 clustered index page through many updates, and not setting an explicit
00060 x-lock on clustered index records, as they anyway will get an implicit
00061 x-lock when they are updated. A problem is that the read nodes in the
00062 graph should know that they must keep the latch when passing the control
00063 up to the update node, and not set any record lock on the record which
00064 will be updated. Another problem occurs if the execution is stopped,
00065 as the kernel switches to another query thread, or the transaction must
00066 wait for a lock. Then we should be able to release the latch and, maybe,
00067 acquire an explicit x-lock on the record.
00068   Because this seems too complicated, we conclude that the less
00069 efficient solution of releasing all the latches when the control is
00070 transferred to another node, and acquiring explicit x-locks, is better. */
00071 
00072 /* How is a delete performed? If there is a delete without an
00073 explicit cursor, i.e., a searched delete, there are at least
00074 two different situations:
00075 the implicit select cursor may run on (1) the clustered index or
00076 on (2) a secondary index. The delete is performed by setting
00077 the delete bit in the record and substituting the id of the
00078 deleting transaction for the original trx id, and substituting a
00079 new roll ptr for previous roll ptr. The old trx id and roll ptr
00080 are saved in the undo log record. Thus, no physical changes occur
00081 in the index tree structure at the time of the delete. Only
00082 when the undo log is purged, the index records will be physically
00083 deleted from the index trees.
00084 
00085 The query graph executing a searched delete would consist of
00086 a delete node which has as a subtree a select subgraph.
00087 The select subgraph should return a (persistent) cursor
00088 in the clustered index, placed on page which is x-latched.
00089 The delete node should look for all secondary index records for
00090 this clustered index entry and mark them as deleted. When is
00091 the x-latch freed? The most efficient way for performing a
00092 searched delete is obviously to keep the x-latch for several
00093 steps of query graph execution. */
00094 
00095 /*************************************************************************
00096 IMPORTANT NOTE: Any operation that generates redo MUST check that there
00097 is enough space in the redo log before for that operation. This is
00098 done by calling log_free_check(). The reason for checking the
00099 availability of the redo log space before the start of the operation is
00100 that we MUST not hold any synchonization objects when performing the
00101 check.
00102 If you make a change in this module make sure that no codepath is
00103 introduced where a call to log_free_check() is bypassed. */
00104 
00105 /*************************************************************************
00106 IMPORTANT NOTE: Any operation that generates redo MUST check that there
00107 is enough space in the redo log before for that operation. This is
00108 done by calling log_free_check(). The reason for checking the
00109 availability of the redo log space before the start of the operation is
00110 that we MUST not hold any synchonization objects when performing the
00111 check.
00112 If you make a change in this module make sure that no codepath is
00113 introduced where a call to log_free_check() is bypassed. */
00114 
00115 /***********************************************************/
00120 static
00121 ibool
00122 row_upd_changes_first_fields_binary(
00123 /*================================*/
00124   dtuple_t* entry,  
00125   dict_index_t* index,  
00126   const upd_t*  update, 
00127   ulint   n); 
00130 /*********************************************************************/
00139 static
00140 ibool
00141 row_upd_index_is_referenced(
00142 /*========================*/
00143   dict_index_t* index,  
00144   trx_t*    trx)  
00145 {
00146   dict_table_t* table   = index->table;
00147   dict_foreign_t* foreign;
00148   ibool   froze_data_dict = FALSE;
00149   ibool   is_referenced = FALSE;
00150 
00151   if (!UT_LIST_GET_FIRST(table->referenced_list)) {
00152 
00153     return(FALSE);
00154   }
00155 
00156   if (trx->dict_operation_lock_mode == 0) {
00157     row_mysql_freeze_data_dictionary(trx);
00158     froze_data_dict = TRUE;
00159   }
00160 
00161   foreign = UT_LIST_GET_FIRST(table->referenced_list);
00162 
00163   while (foreign) {
00164     if (foreign->referenced_index == index) {
00165 
00166       is_referenced = TRUE;
00167       goto func_exit;
00168     }
00169 
00170     foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
00171   }
00172 
00173 func_exit:
00174   if (froze_data_dict) {
00175     row_mysql_unfreeze_data_dictionary(trx);
00176   }
00177 
00178   return(is_referenced);
00179 }
00180 
00181 /*********************************************************************/
00189 static
00190 ulint
00191 row_upd_check_references_constraints(
00192 /*=================================*/
00193   upd_node_t* node, 
00194   btr_pcur_t* pcur, 
00196   dict_table_t* table,  
00197   dict_index_t* index,  
00198   ulint*    offsets,
00199   que_thr_t*  thr,  
00200   mtr_t*    mtr)  
00201 {
00202   dict_foreign_t* foreign;
00203   mem_heap_t* heap;
00204   dtuple_t* entry;
00205   trx_t*    trx;
00206   const rec_t*  rec;
00207   ulint   n_ext;
00208   ulint   err;
00209   ibool   got_s_lock  = FALSE;
00210 
00211   if (UT_LIST_GET_FIRST(table->referenced_list) == NULL) {
00212 
00213     return(DB_SUCCESS);
00214   }
00215 
00216   trx = thr_get_trx(thr);
00217 
00218   rec = btr_pcur_get_rec(pcur);
00219   ut_ad(rec_offs_validate(rec, index, offsets));
00220 
00221   heap = mem_heap_create(500);
00222 
00223   entry = row_rec_to_index_entry(ROW_COPY_DATA, rec, index, offsets,
00224                &n_ext, heap);
00225 
00226   mtr_commit(mtr);
00227 
00228   mtr_start(mtr);
00229 
00230   if (trx->dict_operation_lock_mode == 0) {
00231     got_s_lock = TRUE;
00232 
00233     row_mysql_freeze_data_dictionary(trx);
00234   }
00235 
00236   foreign = UT_LIST_GET_FIRST(table->referenced_list);
00237 
00238   while (foreign) {
00239     /* Note that we may have an update which updates the index
00240     record, but does NOT update the first fields which are
00241     referenced in a foreign key constraint. Then the update does
00242     NOT break the constraint. */
00243 
00244     if (foreign->referenced_index == index
00245         && (node->is_delete
00246       || row_upd_changes_first_fields_binary(
00247         entry, index, node->update,
00248         foreign->n_fields))) {
00249 
00250       if (foreign->foreign_table == NULL) {
00251         dict_table_get(foreign->foreign_table_name,
00252                  FALSE);
00253       }
00254 
00255       if (foreign->foreign_table) {
00256         mutex_enter(&(dict_sys->mutex));
00257 
00258         (foreign->foreign_table
00259          ->n_foreign_key_checks_running)++;
00260 
00261         mutex_exit(&(dict_sys->mutex));
00262       }
00263 
00264       /* NOTE that if the thread ends up waiting for a lock
00265       we will release dict_operation_lock temporarily!
00266       But the counter on the table protects 'foreign' from
00267       being dropped while the check is running. */
00268 
00269       err = row_ins_check_foreign_constraint(
00270         FALSE, foreign, table, entry, thr);
00271 
00272       if (foreign->foreign_table) {
00273         mutex_enter(&(dict_sys->mutex));
00274 
00275         ut_a(foreign->foreign_table
00276              ->n_foreign_key_checks_running > 0);
00277 
00278         (foreign->foreign_table
00279          ->n_foreign_key_checks_running)--;
00280 
00281         mutex_exit(&(dict_sys->mutex));
00282       }
00283 
00284       if (err != DB_SUCCESS) {
00285 
00286         goto func_exit;
00287       }
00288     }
00289 
00290     foreign = UT_LIST_GET_NEXT(referenced_list, foreign);
00291   }
00292 
00293   err = DB_SUCCESS;
00294 
00295 func_exit:
00296   if (got_s_lock) {
00297     row_mysql_unfreeze_data_dictionary(trx);
00298   }
00299 
00300   mem_heap_free(heap);
00301 
00302   return(err);
00303 }
00304 
00305 /*********************************************************************/
00308 UNIV_INTERN
00309 upd_node_t*
00310 upd_node_create(
00311 /*============*/
00312   mem_heap_t* heap) 
00313 {
00314   upd_node_t* node;
00315 
00316         node = static_cast<upd_node_t *>(mem_heap_alloc(heap, sizeof(upd_node_t)));
00317   node->common.type = QUE_NODE_UPDATE;
00318 
00319   node->state = UPD_NODE_UPDATE_CLUSTERED;
00320   node->in_mysql_interface = FALSE;
00321 
00322   node->row = NULL;
00323   node->ext = NULL;
00324   node->upd_row = NULL;
00325   node->upd_ext = NULL;
00326   node->index = NULL;
00327   node->update = NULL;
00328 
00329   node->foreign = NULL;
00330   node->cascade_heap = NULL;
00331   node->cascade_node = NULL;
00332 
00333   node->select = NULL;
00334 
00335   node->heap = mem_heap_create(128);
00336   node->magic_n = UPD_NODE_MAGIC_N;
00337 
00338   node->cmpl_info = 0;
00339 
00340   return(node);
00341 }
00342 #endif /* !UNIV_HOTBACKUP */
00343 
00344 /*********************************************************************/
00347 UNIV_INTERN
00348 void
00349 row_upd_rec_sys_fields_in_recovery(
00350 /*===============================*/
00351   rec_t*    rec,  
00352   page_zip_des_t* page_zip,
00353   const ulint*  offsets,
00354   ulint   pos,  
00355   trx_id_t  trx_id, 
00356   roll_ptr_t  roll_ptr)
00357 {
00358   ut_ad(rec_offs_validate(rec, NULL, offsets));
00359 
00360   if (UNIV_LIKELY_NULL(page_zip)) {
00361     page_zip_write_trx_id_and_roll_ptr(
00362       page_zip, rec, offsets, pos, trx_id, roll_ptr);
00363   } else {
00364     byte* field;
00365     ulint len;
00366 
00367     field = rec_get_nth_field(rec, offsets, pos, &len);
00368     ut_ad(len == DATA_TRX_ID_LEN);
00369 #if DATA_TRX_ID + 1 != DATA_ROLL_PTR
00370 # error "DATA_TRX_ID + 1 != DATA_ROLL_PTR"
00371 #endif
00372     trx_write_trx_id(field, trx_id);
00373     trx_write_roll_ptr(field + DATA_TRX_ID_LEN, roll_ptr);
00374   }
00375 }
00376 
00377 #ifndef UNIV_HOTBACKUP
00378 /*********************************************************************/
00380 UNIV_INTERN
00381 void
00382 row_upd_index_entry_sys_field(
00383 /*==========================*/
00384   dtuple_t* entry,  
00388   dict_index_t* index,  
00389   ulint   type, 
00390   ib_uint64_t val)  
00391 {
00392   dfield_t* dfield;
00393   byte*   field;
00394   ulint   pos;
00395 
00396   ut_ad(dict_index_is_clust(index));
00397 
00398   pos = dict_index_get_sys_col_pos(index, type);
00399 
00400   dfield = dtuple_get_nth_field(entry, pos);
00401         field = static_cast<byte *>(dfield_get_data(dfield));
00402 
00403   if (type == DATA_TRX_ID) {
00404     trx_write_trx_id(field, val);
00405   } else {
00406     ut_ad(type == DATA_ROLL_PTR);
00407     trx_write_roll_ptr(field, val);
00408   }
00409 }
00410 
00411 /***********************************************************/
00416 UNIV_INTERN
00417 ibool
00418 row_upd_changes_field_size_or_external(
00419 /*===================================*/
00420   dict_index_t* index,  
00421   const ulint*  offsets,
00422   const upd_t*  update) 
00423 {
00424   const upd_field_t*  upd_field;
00425   const dfield_t*   new_val;
00426   ulint     old_len;
00427   ulint     new_len;
00428   ulint     n_fields;
00429   ulint     i;
00430 
00431   ut_ad(rec_offs_validate(NULL, index, offsets));
00432   n_fields = upd_get_n_fields(update);
00433 
00434   for (i = 0; i < n_fields; i++) {
00435     upd_field = upd_get_nth_field(update, i);
00436 
00437     new_val = &(upd_field->new_val);
00438     new_len = dfield_get_len(new_val);
00439 
00440     if (dfield_is_null(new_val) && !rec_offs_comp(offsets)) {
00441       /* A bug fixed on Dec 31st, 2004: we looked at the
00442       SQL NULL size from the wrong field! We may backport
00443       this fix also to 4.0. The merge to 5.0 will be made
00444       manually immediately after we commit this to 4.1. */
00445 
00446       new_len = dict_col_get_sql_null_size(
00447         dict_index_get_nth_col(index,
00448                    upd_field->field_no),
00449         0);
00450     }
00451 
00452     old_len = rec_offs_nth_size(offsets, upd_field->field_no);
00453 
00454     if (rec_offs_comp(offsets)
00455         && rec_offs_nth_sql_null(offsets,
00456                upd_field->field_no)) {
00457       /* Note that in the compact table format, for a
00458       variable length field, an SQL NULL will use zero
00459       bytes in the offset array at the start of the physical
00460       record, but a zero-length value (empty string) will
00461       use one byte! Thus, we cannot use update-in-place
00462       if we update an SQL NULL varchar to an empty string! */
00463 
00464       old_len = UNIV_SQL_NULL;
00465     }
00466 
00467     if (dfield_is_ext(new_val) || old_len != new_len
00468         || rec_offs_nth_extern(offsets, upd_field->field_no)) {
00469 
00470       return(TRUE);
00471     }
00472   }
00473 
00474   return(FALSE);
00475 }
00476 #endif /* !UNIV_HOTBACKUP */
00477 
00478 /***********************************************************/
00484 UNIV_INTERN
00485 void
00486 row_upd_rec_in_place(
00487 /*=================*/
00488   rec_t*    rec,  
00489   dict_index_t* index,  
00490   const ulint*  offsets,
00491   const upd_t*  update, 
00492   page_zip_des_t* page_zip)
00494 {
00495   const upd_field_t*  upd_field;
00496   const dfield_t*   new_val;
00497   ulint     n_fields;
00498   ulint     i;
00499 
00500   ut_ad(rec_offs_validate(rec, index, offsets));
00501 
00502   if (rec_offs_comp(offsets)) {
00503     rec_set_info_bits_new(rec, update->info_bits);
00504   } else {
00505     rec_set_info_bits_old(rec, update->info_bits);
00506   }
00507 
00508   n_fields = upd_get_n_fields(update);
00509 
00510   for (i = 0; i < n_fields; i++) {
00511     upd_field = upd_get_nth_field(update, i);
00512     new_val = &(upd_field->new_val);
00513     ut_ad(!dfield_is_ext(new_val) ==
00514           !rec_offs_nth_extern(offsets, upd_field->field_no));
00515 
00516     rec_set_nth_field(rec, offsets, upd_field->field_no,
00517           dfield_get_data(new_val),
00518           dfield_get_len(new_val));
00519   }
00520 
00521   if (UNIV_LIKELY_NULL(page_zip)) {
00522     page_zip_write_rec(page_zip, rec, index, offsets, 0);
00523   }
00524 }
00525 
00526 #ifndef UNIV_HOTBACKUP
00527 /*********************************************************************/
00531 UNIV_INTERN
00532 byte*
00533 row_upd_write_sys_vals_to_log(
00534 /*==========================*/
00535   dict_index_t* index,  
00536   trx_t*    trx,  
00537   roll_ptr_t  roll_ptr,
00538   byte*   log_ptr,
00540   mtr_t*    /*mtr __attribute__((unused))*/) 
00541 {
00542   ut_ad(dict_index_is_clust(index));
00543   ut_ad(mtr);
00544 
00545   log_ptr += mach_write_compressed(log_ptr,
00546            dict_index_get_sys_col_pos(
00547              index, DATA_TRX_ID));
00548 
00549   trx_write_roll_ptr(log_ptr, roll_ptr);
00550   log_ptr += DATA_ROLL_PTR_LEN;
00551 
00552   log_ptr += mach_ull_write_compressed(log_ptr, trx->id);
00553 
00554   return(log_ptr);
00555 }
00556 #endif /* !UNIV_HOTBACKUP */
00557 
00558 /*********************************************************************/
00561 UNIV_INTERN
00562 byte*
00563 row_upd_parse_sys_vals(
00564 /*===================*/
00565   byte*   ptr,  
00566   byte*   end_ptr,
00567   ulint*    pos,  
00568   trx_id_t* trx_id, 
00569   roll_ptr_t* roll_ptr)
00570 {
00571   ptr = mach_parse_compressed(ptr, end_ptr, pos);
00572 
00573   if (ptr == NULL) {
00574 
00575     return(NULL);
00576   }
00577 
00578   if (end_ptr < ptr + DATA_ROLL_PTR_LEN) {
00579 
00580     return(NULL);
00581   }
00582 
00583   *roll_ptr = trx_read_roll_ptr(ptr);
00584   ptr += DATA_ROLL_PTR_LEN;
00585 
00586   ptr = mach_ull_parse_compressed(ptr, end_ptr, trx_id);
00587 
00588   return(ptr);
00589 }
00590 
00591 #ifndef UNIV_HOTBACKUP
00592 /***********************************************************/
00594 UNIV_INTERN
00595 void
00596 row_upd_index_write_log(
00597 /*====================*/
00598   const upd_t*  update, 
00599   byte*   log_ptr,
00603   mtr_t*    mtr)  
00604 {
00605   const upd_field_t*  upd_field;
00606   const dfield_t*   new_val;
00607   ulint     len;
00608   ulint     n_fields;
00609   byte*     buf_end;
00610   ulint     i;
00611 
00612   n_fields = upd_get_n_fields(update);
00613 
00614   buf_end = log_ptr + MLOG_BUF_MARGIN;
00615 
00616   mach_write_to_1(log_ptr, update->info_bits);
00617   log_ptr++;
00618   log_ptr += mach_write_compressed(log_ptr, n_fields);
00619 
00620   for (i = 0; i < n_fields; i++) {
00621 
00622 #if MLOG_BUF_MARGIN <= 30
00623 # error "MLOG_BUF_MARGIN <= 30"
00624 #endif
00625 
00626     if (log_ptr + 30 > buf_end) {
00627       mlog_close(mtr, log_ptr);
00628 
00629       log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
00630       buf_end = log_ptr + MLOG_BUF_MARGIN;
00631     }
00632 
00633     upd_field = upd_get_nth_field(update, i);
00634 
00635     new_val = &(upd_field->new_val);
00636 
00637     len = dfield_get_len(new_val);
00638 
00639     log_ptr += mach_write_compressed(log_ptr, upd_field->field_no);
00640     log_ptr += mach_write_compressed(log_ptr, len);
00641 
00642     if (len != UNIV_SQL_NULL) {
00643       if (log_ptr + len < buf_end) {
00644         memcpy(log_ptr, dfield_get_data(new_val), len);
00645 
00646         log_ptr += len;
00647       } else {
00648         mlog_close(mtr, log_ptr);
00649 
00650         mlog_catenate_string(mtr,
00651                                                      static_cast<byte *>(dfield_get_data(new_val)),
00652                  len);
00653 
00654         log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
00655         buf_end = log_ptr + MLOG_BUF_MARGIN;
00656       }
00657     }
00658   }
00659 
00660   mlog_close(mtr, log_ptr);
00661 }
00662 #endif /* !UNIV_HOTBACKUP */
00663 
00664 /*********************************************************************/
00667 UNIV_INTERN
00668 byte*
00669 row_upd_index_parse(
00670 /*================*/
00671   byte*   ptr,  
00672   byte*   end_ptr,
00673   mem_heap_t* heap, 
00675   upd_t**   update_out)
00676 {
00677   upd_t*    update;
00678   upd_field_t*  upd_field;
00679   dfield_t* new_val;
00680   ulint   len;
00681   ulint   n_fields;
00682   ulint   info_bits;
00683   ulint   i;
00684 
00685   if (end_ptr < ptr + 1) {
00686 
00687     return(NULL);
00688   }
00689 
00690   info_bits = mach_read_from_1(ptr);
00691   ptr++;
00692   ptr = mach_parse_compressed(ptr, end_ptr, &n_fields);
00693 
00694   if (ptr == NULL) {
00695 
00696     return(NULL);
00697   }
00698 
00699   update = upd_create(n_fields, heap);
00700   update->info_bits = info_bits;
00701 
00702   for (i = 0; i < n_fields; i++) {
00703     ulint field_no;
00704     upd_field = upd_get_nth_field(update, i);
00705     new_val = &(upd_field->new_val);
00706 
00707     ptr = mach_parse_compressed(ptr, end_ptr, &field_no);
00708 
00709     if (ptr == NULL) {
00710 
00711       return(NULL);
00712     }
00713 
00714     upd_field->field_no = field_no;
00715 
00716     ptr = mach_parse_compressed(ptr, end_ptr, &len);
00717 
00718     if (ptr == NULL) {
00719 
00720       return(NULL);
00721     }
00722 
00723     if (len != UNIV_SQL_NULL) {
00724 
00725       if (end_ptr < ptr + len) {
00726 
00727         return(NULL);
00728       }
00729 
00730       dfield_set_data(new_val,
00731           mem_heap_dup(heap, ptr, len), len);
00732       ptr += len;
00733     } else {
00734       dfield_set_null(new_val);
00735     }
00736   }
00737 
00738   *update_out = update;
00739 
00740   return(ptr);
00741 }
00742 
00743 #ifndef UNIV_HOTBACKUP
00744 /***************************************************************/
00749 UNIV_INTERN
00750 upd_t*
00751 row_upd_build_sec_rec_difference_binary(
00752 /*====================================*/
00753   dict_index_t* index,  
00754   const dtuple_t* entry,  
00755   const rec_t*  rec,  
00756   trx_t*    trx,  
00757   mem_heap_t* heap) 
00758 {
00759   upd_field_t*  upd_field;
00760   const dfield_t* dfield;
00761   const byte* data;
00762   ulint   len;
00763   upd_t*    update;
00764   ulint   n_diff;
00765   ulint   i;
00766   ulint   offsets_[REC_OFFS_SMALL_SIZE];
00767   const ulint*  offsets;
00768   rec_offs_init(offsets_);
00769 
00770   /* This function is used only for a secondary index */
00771   ut_a(!dict_index_is_clust(index));
00772 
00773   update = upd_create(dtuple_get_n_fields(entry), heap);
00774 
00775   n_diff = 0;
00776   offsets = rec_get_offsets(rec, index, offsets_,
00777           ULINT_UNDEFINED, &heap);
00778 
00779   for (i = 0; i < dtuple_get_n_fields(entry); i++) {
00780 
00781     data = rec_get_nth_field(rec, offsets, i, &len);
00782 
00783     dfield = dtuple_get_nth_field(entry, i);
00784 
00785     /* NOTE that it may be that len != dfield_get_len(dfield) if we
00786     are updating in a character set and collation where strings of
00787     different length can be equal in an alphabetical comparison,
00788     and also in the case where we have a column prefix index
00789     and the last characters in the index field are spaces; the
00790     latter case probably caused the assertion failures reported at
00791     row0upd.c line 713 in versions 4.0.14 - 4.0.16. */
00792 
00793     /* NOTE: we compare the fields as binary strings!
00794     (No collation) */
00795 
00796     if (!dfield_data_is_binary_equal(dfield, len, data)) {
00797 
00798       upd_field = upd_get_nth_field(update, n_diff);
00799 
00800       dfield_copy(&(upd_field->new_val), dfield);
00801 
00802       upd_field_set_field_no(upd_field, i, index, trx);
00803 
00804       n_diff++;
00805     }
00806   }
00807 
00808   update->n_fields = n_diff;
00809 
00810   return(update);
00811 }
00812 
00813 /***************************************************************/
00819 UNIV_INTERN
00820 upd_t*
00821 row_upd_build_difference_binary(
00822 /*============================*/
00823   dict_index_t* index,  
00824   const dtuple_t* entry,  
00825   const rec_t*  rec,  
00826   trx_t*    trx,  
00827   mem_heap_t* heap) 
00828 {
00829   upd_field_t*  upd_field;
00830   const dfield_t* dfield;
00831   const byte* data;
00832   ulint   len;
00833   upd_t*    update;
00834   ulint   n_diff;
00835   ulint   roll_ptr_pos;
00836   ulint   trx_id_pos;
00837   ulint   i;
00838   ulint   offsets_[REC_OFFS_NORMAL_SIZE];
00839   const ulint*  offsets;
00840   rec_offs_init(offsets_);
00841 
00842   /* This function is used only for a clustered index */
00843   ut_a(dict_index_is_clust(index));
00844 
00845   update = upd_create(dtuple_get_n_fields(entry), heap);
00846 
00847   n_diff = 0;
00848 
00849   roll_ptr_pos = dict_index_get_sys_col_pos(index, DATA_ROLL_PTR);
00850   trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID);
00851 
00852   offsets = rec_get_offsets(rec, index, offsets_,
00853           ULINT_UNDEFINED, &heap);
00854 
00855   for (i = 0; i < dtuple_get_n_fields(entry); i++) {
00856 
00857     data = rec_get_nth_field(rec, offsets, i, &len);
00858 
00859     dfield = dtuple_get_nth_field(entry, i);
00860 
00861     /* NOTE: we compare the fields as binary strings!
00862     (No collation) */
00863 
00864     if (i == trx_id_pos || i == roll_ptr_pos) {
00865 
00866       goto skip_compare;
00867     }
00868 
00869     if (UNIV_UNLIKELY(!dfield_is_ext(dfield)
00870           != !rec_offs_nth_extern(offsets, i))
00871         || !dfield_data_is_binary_equal(dfield, len, data)) {
00872 
00873       upd_field = upd_get_nth_field(update, n_diff);
00874 
00875       dfield_copy(&(upd_field->new_val), dfield);
00876 
00877       upd_field_set_field_no(upd_field, i, index, trx);
00878 
00879       n_diff++;
00880     }
00881 skip_compare:
00882     ;
00883   }
00884 
00885   update->n_fields = n_diff;
00886 
00887   return(update);
00888 }
00889 
00890 /***********************************************************/
00895 static
00896 byte*
00897 row_upd_ext_fetch(
00898 /*==============*/
00899   const byte* data,   
00902   ulint   local_len,  
00903   ulint   zip_size, 
00906   ulint*    len,    
00908   mem_heap_t* heap)   
00909 {
00910         byte* buf = static_cast<byte *>(mem_heap_alloc(heap, *len));
00911 
00912   *len = btr_copy_externally_stored_field_prefix(buf, *len,
00913                    zip_size,
00914                    data, local_len);
00915   /* We should never update records containing a half-deleted BLOB. */
00916   ut_a(*len);
00917 
00918   return(buf);
00919 }
00920 
00921 /***********************************************************/
00924 static
00925 void
00926 row_upd_index_replace_new_col_val(
00927 /*==============================*/
00928   dfield_t*   dfield, 
00930   const dict_field_t* field,  
00931   const dict_col_t* col,  
00932   const upd_field_t*  uf, 
00933   mem_heap_t*   heap, 
00935   ulint     zip_size)
00937 {
00938   ulint   len;
00939   const byte* data;
00940 
00941   dfield_copy_data(dfield, &uf->new_val);
00942 
00943   if (dfield_is_null(dfield)) {
00944     return;
00945   }
00946 
00947   len = dfield_get_len(dfield);
00948         data = static_cast<const byte *>(dfield_get_data(dfield));
00949 
00950   if (field->prefix_len > 0) {
00951     ibool   fetch_ext = dfield_is_ext(dfield)
00952       && len < (ulint) field->prefix_len
00953       + BTR_EXTERN_FIELD_REF_SIZE;
00954 
00955     if (fetch_ext) {
00956       ulint l = len;
00957 
00958       len = field->prefix_len;
00959 
00960       data = row_upd_ext_fetch(data, l, zip_size,
00961              &len, heap);
00962     }
00963 
00964     len = dtype_get_at_most_n_mbchars(col->prtype,
00965               col->mbminmaxlen,
00966               field->prefix_len, len,
00967               (const char*) data);
00968 
00969     dfield_set_data(dfield, data, len);
00970 
00971     if (!fetch_ext) {
00972       dfield_dup(dfield, heap);
00973     }
00974 
00975     return;
00976   }
00977 
00978   switch (uf->orig_len) {
00979     byte* buf;
00980   case BTR_EXTERN_FIELD_REF_SIZE:
00981     /* Restore the original locally stored
00982     part of the column.  In the undo log,
00983     InnoDB writes a longer prefix of externally
00984     stored columns, so that column prefixes
00985     in secondary indexes can be reconstructed. */
00986     dfield_set_data(dfield,
00987         data + len - BTR_EXTERN_FIELD_REF_SIZE,
00988         BTR_EXTERN_FIELD_REF_SIZE);
00989     dfield_set_ext(dfield);
00990     /* fall through */
00991   case 0:
00992     dfield_dup(dfield, heap);
00993     break;
00994   default:
00995     /* Reconstruct the original locally
00996     stored part of the column.  The data
00997     will have to be copied. */
00998     ut_a(uf->orig_len > BTR_EXTERN_FIELD_REF_SIZE);
00999                 buf = static_cast<byte *>(mem_heap_alloc(heap, uf->orig_len));
01000     /* Copy the locally stored prefix. */
01001     memcpy(buf, data,
01002            uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE);
01003     /* Copy the BLOB pointer. */
01004     memcpy(buf + uf->orig_len - BTR_EXTERN_FIELD_REF_SIZE,
01005            data + len - BTR_EXTERN_FIELD_REF_SIZE,
01006            BTR_EXTERN_FIELD_REF_SIZE);
01007 
01008     dfield_set_data(dfield, buf, uf->orig_len);
01009     dfield_set_ext(dfield);
01010     break;
01011   }
01012 }
01013 
01014 /***********************************************************/
01017 UNIV_INTERN
01018 void
01019 row_upd_index_replace_new_col_vals_index_pos(
01020 /*=========================================*/
01021   dtuple_t* entry,  
01025   dict_index_t* index,  
01027   const upd_t*  update, 
01030   ibool   order_only,
01034   mem_heap_t* heap) 
01036 {
01037   ulint   i;
01038   ulint   n_fields;
01039   const ulint zip_size  = dict_table_zip_size(index->table);
01040 
01041   ut_ad(index);
01042 
01043   dtuple_set_info_bits(entry, update->info_bits);
01044 
01045   if (order_only) {
01046     n_fields = dict_index_get_n_unique(index);
01047   } else {
01048     n_fields = dict_index_get_n_fields(index);
01049   }
01050 
01051   for (i = 0; i < n_fields; i++) {
01052     const dict_field_t* field;
01053     const dict_col_t* col;
01054     const upd_field_t*  uf;
01055 
01056     field = dict_index_get_nth_field(index, i);
01057     col = dict_field_get_col(field);
01058     uf = upd_get_field_by_field_no(update, i);
01059 
01060     if (uf) {
01061       row_upd_index_replace_new_col_val(
01062         dtuple_get_nth_field(entry, i),
01063         field, col, uf, heap, zip_size);
01064     }
01065   }
01066 }
01067 
01068 /***********************************************************/
01071 UNIV_INTERN
01072 void
01073 row_upd_index_replace_new_col_vals(
01074 /*===============================*/
01075   dtuple_t* entry,  
01079   dict_index_t* index,  
01081   const upd_t*  update, 
01084   mem_heap_t* heap) 
01086 {
01087   ulint     i;
01088   const dict_index_t* clust_index
01089     = dict_table_get_first_index(index->table);
01090   const ulint   zip_size
01091     = dict_table_zip_size(index->table);
01092 
01093   dtuple_set_info_bits(entry, update->info_bits);
01094 
01095   for (i = 0; i < dict_index_get_n_fields(index); i++) {
01096     const dict_field_t* field;
01097     const dict_col_t* col;
01098     const upd_field_t*  uf;
01099 
01100     field = dict_index_get_nth_field(index, i);
01101     col = dict_field_get_col(field);
01102     uf = upd_get_field_by_field_no(
01103       update, dict_col_get_clust_pos(col, clust_index));
01104 
01105     if (uf) {
01106       row_upd_index_replace_new_col_val(
01107         dtuple_get_nth_field(entry, i),
01108         field, col, uf, heap, zip_size);
01109     }
01110   }
01111 }
01112 
01113 /***********************************************************/
01115 UNIV_INTERN
01116 void
01117 row_upd_replace(
01118 /*============*/
01119   dtuple_t*   row,  
01124   row_ext_t**   ext,  
01126   const dict_index_t* index,  
01127   const upd_t*    update, 
01129   mem_heap_t*   heap) 
01130 {
01131   ulint     col_no;
01132   ulint     i;
01133   ulint     n_cols;
01134   ulint     n_ext_cols;
01135   ulint*      ext_cols;
01136   const dict_table_t* table;
01137 
01138   ut_ad(row);
01139   ut_ad(ext);
01140   ut_ad(index);
01141   ut_ad(dict_index_is_clust(index));
01142   ut_ad(update);
01143   ut_ad(heap);
01144 
01145   n_cols = dtuple_get_n_fields(row);
01146   table = index->table;
01147   ut_ad(n_cols == dict_table_get_n_cols(table));
01148 
01149         ext_cols = static_cast<ulint *>(mem_heap_alloc(heap, n_cols * sizeof *ext_cols));
01150   n_ext_cols = 0;
01151 
01152   dtuple_set_info_bits(row, update->info_bits);
01153 
01154   for (col_no = 0; col_no < n_cols; col_no++) {
01155 
01156     const dict_col_t* col
01157       = dict_table_get_nth_col(table, col_no);
01158     const ulint   clust_pos
01159       = dict_col_get_clust_pos(col, index);
01160     dfield_t*   dfield;
01161 
01162     if (UNIV_UNLIKELY(clust_pos == ULINT_UNDEFINED)) {
01163 
01164       continue;
01165     }
01166 
01167     dfield = dtuple_get_nth_field(row, col_no);
01168 
01169     for (i = 0; i < upd_get_n_fields(update); i++) {
01170 
01171       const upd_field_t*  upd_field
01172         = upd_get_nth_field(update, i);
01173 
01174       if (upd_field->field_no != clust_pos) {
01175 
01176         continue;
01177       }
01178 
01179       dfield_copy_data(dfield, &upd_field->new_val);
01180       break;
01181     }
01182 
01183     if (dfield_is_ext(dfield) && col->ord_part) {
01184       ext_cols[n_ext_cols++] = col_no;
01185     }
01186   }
01187 
01188   if (n_ext_cols) {
01189     *ext = row_ext_create(n_ext_cols, ext_cols, row,
01190               dict_table_zip_size(table), heap);
01191   } else {
01192     *ext = NULL;
01193   }
01194 }
01195 
01196 /***********************************************************/
01203 UNIV_INTERN
01204 ibool
01205 row_upd_changes_ord_field_binary(
01206 /*=============================*/
01207   const dtuple_t* row,  
01211   dict_index_t* index,  
01212   const upd_t*  update) 
01215 {
01216   ulint   n_unique;
01217   ulint   n_upd_fields;
01218   ulint   i, j;
01219   dict_index_t* clust_index;
01220 
01221   ut_ad(update && index);
01222 
01223   n_unique = dict_index_get_n_unique(index);
01224   n_upd_fields = upd_get_n_fields(update);
01225 
01226   clust_index = dict_table_get_first_index(index->table);
01227 
01228   for (i = 0; i < n_unique; i++) {
01229 
01230     const dict_field_t* ind_field;
01231     const dict_col_t* col;
01232     ulint     col_pos;
01233     ulint     col_no;
01234 
01235     ind_field = dict_index_get_nth_field(index, i);
01236     col = dict_field_get_col(ind_field);
01237     col_pos = dict_col_get_clust_pos(col, clust_index);
01238     col_no = dict_col_get_no(col);
01239 
01240     for (j = 0; j < n_upd_fields; j++) {
01241 
01242       const upd_field_t*  upd_field
01243         = upd_get_nth_field(update, j);
01244 
01245       /* Note that if the index field is a column prefix
01246       then it may be that row does not contain an externally
01247       stored part of the column value, and we cannot compare
01248       the datas */
01249 
01250       if (col_pos == upd_field->field_no
01251           && (row == NULL
01252         || ind_field->prefix_len > 0
01253         || !dfield_datas_are_binary_equal(
01254           dtuple_get_nth_field(row, col_no),
01255           &(upd_field->new_val)))) {
01256 
01257         return(TRUE);
01258       }
01259     }
01260   }
01261 
01262   return(FALSE);
01263 }
01264 
01265 /***********************************************************/
01270 UNIV_INTERN
01271 ibool
01272 row_upd_changes_some_index_ord_field_binary(
01273 /*========================================*/
01274   const dict_table_t* table,  
01275   const upd_t*    update) 
01276 {
01277   upd_field_t*  upd_field;
01278   dict_index_t* index;
01279   ulint   i;
01280 
01281   index = dict_table_get_first_index(table);
01282 
01283   for (i = 0; i < upd_get_n_fields(update); i++) {
01284 
01285     upd_field = upd_get_nth_field(update, i);
01286 
01287     if (dict_field_get_col(dict_index_get_nth_field(
01288                  index, upd_field->field_no))
01289         ->ord_part) {
01290 
01291       return(TRUE);
01292     }
01293   }
01294 
01295   return(FALSE);
01296 }
01297 
01298 /***********************************************************/
01303 static
01304 ibool
01305 row_upd_changes_first_fields_binary(
01306 /*================================*/
01307   dtuple_t* entry,  
01308   dict_index_t* index,  
01309   const upd_t*  update, 
01310   ulint   n)  
01311 {
01312   ulint   n_upd_fields;
01313   ulint   i, j;
01314   dict_index_t* clust_index;
01315 
01316   ut_ad(update && index);
01317   ut_ad(n <= dict_index_get_n_fields(index));
01318 
01319   n_upd_fields = upd_get_n_fields(update);
01320   clust_index = dict_table_get_first_index(index->table);
01321 
01322   for (i = 0; i < n; i++) {
01323 
01324     const dict_field_t* ind_field;
01325     const dict_col_t* col;
01326     ulint     col_pos;
01327 
01328     ind_field = dict_index_get_nth_field(index, i);
01329     col = dict_field_get_col(ind_field);
01330     col_pos = dict_col_get_clust_pos(col, clust_index);
01331 
01332     ut_a(ind_field->prefix_len == 0);
01333 
01334     for (j = 0; j < n_upd_fields; j++) {
01335 
01336       upd_field_t*  upd_field
01337         = upd_get_nth_field(update, j);
01338 
01339       if (col_pos == upd_field->field_no
01340           && !dfield_datas_are_binary_equal(
01341             dtuple_get_nth_field(entry, i),
01342             &(upd_field->new_val))) {
01343 
01344         return(TRUE);
01345       }
01346     }
01347   }
01348 
01349   return(FALSE);
01350 }
01351 
01352 /*********************************************************************/
01354 UNIV_INLINE
01355 void
01356 row_upd_copy_columns(
01357 /*=================*/
01358   rec_t*    rec,  
01359   const ulint*  offsets,
01360   sym_node_t* column) 
01362 {
01363   byte* data;
01364   ulint len;
01365 
01366   while (column) {
01367     data = rec_get_nth_field(rec, offsets,
01368            column->field_nos[SYM_CLUST_FIELD_NO],
01369            &len);
01370     eval_node_copy_and_alloc_val(column, data, len);
01371 
01372     column = UT_LIST_GET_NEXT(col_var_list, column);
01373   }
01374 }
01375 
01376 /*********************************************************************/
01379 UNIV_INLINE
01380 void
01381 row_upd_eval_new_vals(
01382 /*==================*/
01383   upd_t*  update) 
01384 {
01385   que_node_t* exp;
01386   upd_field_t*  upd_field;
01387   ulint   n_fields;
01388   ulint   i;
01389 
01390   n_fields = upd_get_n_fields(update);
01391 
01392   for (i = 0; i < n_fields; i++) {
01393     upd_field = upd_get_nth_field(update, i);
01394 
01395     exp = upd_field->exp;
01396 
01397     eval_exp(exp);
01398 
01399     dfield_copy_data(&(upd_field->new_val), que_node_get_val(exp));
01400   }
01401 }
01402 
01403 /***********************************************************/
01405 static
01406 void
01407 row_upd_store_row(
01408 /*==============*/
01409   upd_node_t* node) 
01410 {
01411   dict_index_t* clust_index;
01412   rec_t*    rec;
01413   mem_heap_t* heap    = NULL;
01414   row_ext_t** ext;
01415   ulint   offsets_[REC_OFFS_NORMAL_SIZE];
01416   const ulint*  offsets;
01417   rec_offs_init(offsets_);
01418 
01419   ut_ad(node->pcur->latch_mode != BTR_NO_LATCHES);
01420 
01421   if (node->row != NULL) {
01422     mem_heap_empty(node->heap);
01423   }
01424 
01425   clust_index = dict_table_get_first_index(node->table);
01426 
01427   rec = btr_pcur_get_rec(node->pcur);
01428 
01429   offsets = rec_get_offsets(rec, clust_index, offsets_,
01430           ULINT_UNDEFINED, &heap);
01431 
01432   if (dict_table_get_format(node->table) >= DICT_TF_FORMAT_ZIP) {
01433     /* In DYNAMIC or COMPRESSED format, there is no prefix
01434     of externally stored columns in the clustered index
01435     record. Build a cache of column prefixes. */
01436     ext = &node->ext;
01437   } else {
01438     /* REDUNDANT and COMPACT formats store a local
01439     768-byte prefix of each externally stored column.
01440     No cache is needed. */
01441     ext = NULL;
01442     node->ext = NULL;
01443   }
01444 
01445   node->row = row_build(ROW_COPY_DATA, clust_index, rec, offsets,
01446             NULL, ext, node->heap);
01447   if (node->is_delete) {
01448     node->upd_row = NULL;
01449     node->upd_ext = NULL;
01450   } else {
01451     node->upd_row = dtuple_copy(node->row, node->heap);
01452     row_upd_replace(node->upd_row, &node->upd_ext,
01453         clust_index, node->update, node->heap);
01454   }
01455 
01456   if (UNIV_LIKELY_NULL(heap)) {
01457     mem_heap_free(heap);
01458   }
01459 }
01460 
01461 /***********************************************************/
01465 static
01466 ulint
01467 row_upd_sec_index_entry(
01468 /*====================*/
01469   upd_node_t* node, 
01470   que_thr_t*  thr)  
01471 {
01472   mtr_t     mtr;
01473   const rec_t*    rec;
01474   btr_pcur_t    pcur;
01475   mem_heap_t*   heap;
01476   dtuple_t*   entry;
01477   dict_index_t*   index;
01478   btr_cur_t*    btr_cur;
01479   ibool     referenced;
01480   ulint     err = DB_SUCCESS;
01481   trx_t*      trx = thr_get_trx(thr);
01482   ulint     mode  = BTR_MODIFY_LEAF;
01483   enum row_search_result  search_result;
01484 
01485   index = node->index;
01486 
01487   referenced = row_upd_index_is_referenced(index, trx);
01488 
01489   heap = mem_heap_create(1024);
01490 
01491   /* Build old index entry */
01492   entry = row_build_index_entry(node->row, node->ext, index, heap);
01493   ut_a(entry);
01494 
01495   mtr_start(&mtr);
01496 
01497   /* Set the query thread, so that ibuf_insert_low() will be
01498   able to invoke thd_get_trx(). */
01499   btr_pcur_get_btr_cur(&pcur)->thr = thr;
01500 
01501   /* We can only try to use the insert/delete buffer to buffer
01502   delete-mark operations if the index we're modifying has no foreign
01503   key constraints referring to it. */
01504   if (!referenced) {
01505     mode |= BTR_DELETE_MARK;
01506   }
01507 
01508   search_result = row_search_index_entry(index, entry, mode,
01509                  &pcur, &mtr);
01510 
01511   btr_cur = btr_pcur_get_btr_cur(&pcur);
01512 
01513   rec = btr_cur_get_rec(btr_cur);
01514 
01515   switch (search_result) {
01516   case ROW_NOT_DELETED_REF: /* should only occur for BTR_DELETE */
01517     ut_error;
01518     break;
01519   case ROW_BUFFERED:
01520     /* Entry was delete marked already. */
01521     break;
01522 
01523   case ROW_NOT_FOUND:
01524     fputs("InnoDB: error in sec index entry update in\n"
01525           "InnoDB: ", stderr);
01526     dict_index_name_print(stderr, trx, index);
01527     fputs("\n"
01528           "InnoDB: tuple ", stderr);
01529     dtuple_print(stderr, entry);
01530     fputs("\n"
01531           "InnoDB: record ", stderr);
01532     rec_print(stderr, rec, index);
01533     putc('\n', stderr);
01534 
01535     trx_print(stderr, trx, 0);
01536 
01537     fputs("\n"
01538           "InnoDB: Submit a detailed bug report"
01539           " to http://bugs.mysql.com\n", stderr);
01540     break;
01541   case ROW_FOUND:
01542     /* Delete mark the old index record; it can already be
01543     delete marked if we return after a lock wait in
01544     row_ins_index_entry below */
01545 
01546     if (!rec_get_deleted_flag(
01547       rec, dict_table_is_comp(index->table))) {
01548 
01549       err = btr_cur_del_mark_set_sec_rec(
01550         0, btr_cur, TRUE, thr, &mtr);
01551 
01552       if (err == DB_SUCCESS && referenced) {
01553 
01554         ulint*  offsets;
01555 
01556         offsets = rec_get_offsets(
01557           rec, index, NULL, ULINT_UNDEFINED,
01558           &heap);
01559 
01560         /* NOTE that the following call loses
01561         the position of pcur ! */
01562         err = row_upd_check_references_constraints(
01563           node, &pcur, index->table,
01564           index, offsets, thr, &mtr);
01565       }
01566     }
01567     break;
01568   }
01569 
01570   btr_pcur_close(&pcur);
01571   mtr_commit(&mtr);
01572 
01573   if (node->is_delete || err != DB_SUCCESS) {
01574 
01575     goto func_exit;
01576   }
01577 
01578   /* Build a new index entry */
01579   entry = row_build_index_entry(node->upd_row, node->upd_ext,
01580               index, heap);
01581   ut_a(entry);
01582 
01583   /* Insert new index entry */
01584   err = row_ins_index_entry(index, entry, 0, TRUE, thr);
01585 
01586 func_exit:
01587   mem_heap_free(heap);
01588 
01589   return(err);
01590 }
01591 
01592 /***********************************************************/
01597 static
01598 ulint
01599 row_upd_sec_step(
01600 /*=============*/
01601   upd_node_t* node, 
01602   que_thr_t*  thr)  
01603 {
01604   ut_ad((node->state == UPD_NODE_UPDATE_ALL_SEC)
01605         || (node->state == UPD_NODE_UPDATE_SOME_SEC));
01606   ut_ad(!dict_index_is_clust(node->index));
01607 
01608   if (node->state == UPD_NODE_UPDATE_ALL_SEC
01609       || row_upd_changes_ord_field_binary(node->row, node->index,
01610             node->update)) {
01611     return(row_upd_sec_index_entry(node, thr));
01612   }
01613 
01614   return(DB_SUCCESS);
01615 }
01616 
01617 /***********************************************************/
01624 static
01625 ulint
01626 row_upd_clust_rec_by_insert(
01627 /*========================*/
01628   upd_node_t* node, 
01629   dict_index_t* index,  
01630   que_thr_t*  thr,  
01631   ibool   referenced,
01633         mtr_t*    mtr)  
01634 {
01635   mem_heap_t* heap  = NULL;
01636   btr_pcur_t* pcur;
01637   btr_cur_t*  btr_cur;
01638   trx_t*    trx;
01639   dict_table_t* table;
01640   dtuple_t* entry;
01641   ulint   err;
01642   ibool   change_ownership = FALSE;
01643 
01644   ut_ad(node);
01645   ut_ad(dict_index_is_clust(index));
01646 
01647   trx = thr_get_trx(thr);
01648   table = node->table;
01649   pcur = node->pcur;
01650   btr_cur = btr_pcur_get_btr_cur(pcur);
01651 
01652   if (node->state != UPD_NODE_INSERT_CLUSTERED) {
01653     rec_t*    rec;
01654     dict_index_t* node_index;
01655     ulint   offsets_[REC_OFFS_NORMAL_SIZE];
01656     ulint*    offsets;
01657     rec_offs_init(offsets_);
01658 
01659     err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
01660                  btr_cur, TRUE, thr, mtr);
01661     if (err != DB_SUCCESS) {
01662       mtr_commit(mtr);
01663       return(err);
01664     }
01665 
01666     /* Mark as not-owned the externally stored fields which the new
01667     row inherits from the delete marked record: purge should not
01668     free those externally stored fields even if the delete marked
01669     record is removed from the index tree, or updated. */
01670 
01671     rec = btr_cur_get_rec(btr_cur);
01672     node_index = dict_table_get_first_index(table);
01673     offsets = rec_get_offsets(rec, node_index, offsets_,
01674             ULINT_UNDEFINED, &heap);
01675     change_ownership = btr_cur_mark_extern_inherited_fields(
01676       btr_cur_get_page_zip(btr_cur), rec, node_index, offsets,
01677       node->update, mtr);
01678     if (referenced) {
01679       /* NOTE that the following call loses
01680       the position of pcur ! */
01681 
01682       err = row_upd_check_references_constraints(
01683         node, pcur, table, node_index, offsets, thr, mtr);
01684 
01685       if (err != DB_SUCCESS) {
01686 
01687         mtr_commit(mtr);
01688 
01689         if (UNIV_LIKELY_NULL(heap)) {
01690           mem_heap_free(heap);
01691         }
01692 
01693         return(err);
01694       }
01695     }
01696   }
01697 
01698   mtr_commit(mtr);
01699 
01700   if (!heap) {
01701     heap = mem_heap_create(500);
01702   }
01703   node->state = UPD_NODE_INSERT_CLUSTERED;
01704 
01705   entry = row_build_index_entry(node->upd_row, node->upd_ext,
01706               index, heap);
01707   ut_a(entry);
01708 
01709   row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id);
01710 
01711   if (change_ownership) {
01712     /* If we return from a lock wait, for example, we may have
01713     extern fields marked as not-owned in entry (marked in the
01714     if-branch above). We must unmark them, take the ownership
01715     back. */
01716 
01717     btr_cur_unmark_dtuple_extern_fields(entry);
01718 
01719     /* We must mark non-updated extern fields in entry as
01720     inherited, so that a possible rollback will not free them. */
01721 
01722     btr_cur_mark_dtuple_inherited_extern(entry, node->update);
01723   }
01724 
01725   err = row_ins_index_entry(index, entry,
01726           node->upd_ext ? node->upd_ext->n_ext : 0,
01727           TRUE, thr);
01728   mem_heap_free(heap);
01729 
01730   return(err);
01731 }
01732 
01733 /***********************************************************/
01738 static
01739 ulint
01740 row_upd_clust_rec(
01741 /*==============*/
01742   upd_node_t* node, 
01743   dict_index_t* index,  
01744   que_thr_t*  thr,  
01745   mtr_t*    mtr)  
01746 {
01747   mem_heap_t* heap  = NULL;
01748   big_rec_t*  big_rec = NULL;
01749   btr_pcur_t* pcur;
01750   btr_cur_t*  btr_cur;
01751   ulint   err;
01752 
01753   ut_ad(node);
01754   ut_ad(dict_index_is_clust(index));
01755 
01756   pcur = node->pcur;
01757   btr_cur = btr_pcur_get_btr_cur(pcur);
01758 
01759   ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
01760             dict_table_is_comp(index->table)));
01761 
01762   /* Try optimistic updating of the record, keeping changes within
01763   the page; we do not check locks because we assume the x-lock on the
01764   record to update */
01765 
01766   if (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) {
01767     err = btr_cur_update_in_place(BTR_NO_LOCKING_FLAG,
01768                 btr_cur, node->update,
01769                 node->cmpl_info, thr, mtr);
01770   } else {
01771     err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG,
01772             btr_cur, node->update,
01773             node->cmpl_info, thr, mtr);
01774   }
01775 
01776   mtr_commit(mtr);
01777 
01778   if (UNIV_LIKELY(err == DB_SUCCESS)) {
01779 
01780     return(DB_SUCCESS);
01781   }
01782 
01783   if (buf_LRU_buf_pool_running_out()) {
01784 
01785     return(DB_LOCK_TABLE_FULL);
01786   }
01787   /* We may have to modify the tree structure: do a pessimistic descent
01788   down the index tree */
01789 
01790   mtr_start(mtr);
01791 
01792   /* NOTE: this transaction has an s-lock or x-lock on the record and
01793   therefore other transactions cannot modify the record when we have no
01794   latch on the page. In addition, we assume that other query threads of
01795   the same transaction do not modify the record in the meantime.
01796   Therefore we can assert that the restoration of the cursor succeeds. */
01797 
01798   ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
01799 
01800   ut_ad(!rec_get_deleted_flag(btr_pcur_get_rec(pcur),
01801             dict_table_is_comp(index->table)));
01802 
01803   err = btr_cur_pessimistic_update(BTR_NO_LOCKING_FLAG, btr_cur,
01804            &heap, &big_rec, node->update,
01805            node->cmpl_info, thr, mtr);
01806   mtr_commit(mtr);
01807 
01808   if (err == DB_SUCCESS && big_rec) {
01809     ulint   offsets_[REC_OFFS_NORMAL_SIZE];
01810     rec_t*    rec;
01811     rec_offs_init(offsets_);
01812 
01813     mtr_start(mtr);
01814 
01815     ut_a(btr_pcur_restore_position(BTR_MODIFY_TREE, pcur, mtr));
01816     rec = btr_cur_get_rec(btr_cur);
01817     err = btr_store_big_rec_extern_fields(
01818       index, btr_cur_get_block(btr_cur), rec,
01819       rec_get_offsets(rec, index, offsets_,
01820           ULINT_UNDEFINED, &heap),
01821       big_rec, mtr);
01822     mtr_commit(mtr);
01823   }
01824 
01825   if (UNIV_LIKELY_NULL(heap)) {
01826     mem_heap_free(heap);
01827   }
01828 
01829   if (big_rec) {
01830     dtuple_big_rec_free(big_rec);
01831   }
01832 
01833   return(err);
01834 }
01835 
01836 /***********************************************************/
01839 static
01840 ulint
01841 row_upd_del_mark_clust_rec(
01842 /*=======================*/
01843   upd_node_t* node, 
01844   dict_index_t* index,  
01845   ulint*    offsets,
01847   que_thr_t*  thr,  
01848   ibool   referenced,
01851   mtr_t*    mtr)  
01852 {
01853   btr_pcur_t* pcur;
01854   btr_cur_t*  btr_cur;
01855   ulint   err;
01856 
01857   ut_ad(node);
01858   ut_ad(dict_index_is_clust(index));
01859   ut_ad(node->is_delete);
01860 
01861   pcur = node->pcur;
01862   btr_cur = btr_pcur_get_btr_cur(pcur);
01863 
01864   /* Store row because we have to build also the secondary index
01865   entries */
01866 
01867   row_upd_store_row(node);
01868 
01869   /* Mark the clustered index record deleted; we do not have to check
01870   locks, because we assume that we have an x-lock on the record */
01871 
01872   err = btr_cur_del_mark_set_clust_rec(BTR_NO_LOCKING_FLAG,
01873                btr_cur, TRUE, thr, mtr);
01874   if (err == DB_SUCCESS && referenced) {
01875     /* NOTE that the following call loses the position of pcur ! */
01876 
01877     err = row_upd_check_references_constraints(
01878       node, pcur, index->table, index, offsets, thr, mtr);
01879   }
01880 
01881   mtr_commit(mtr);
01882 
01883   return(err);
01884 }
01885 
01886 /***********************************************************/
01890 static
01891 ulint
01892 row_upd_clust_step(
01893 /*===============*/
01894   upd_node_t* node, 
01895   que_thr_t*  thr)  
01896 {
01897   dict_index_t* index;
01898   btr_pcur_t* pcur;
01899   ibool   success;
01900   ulint   err;
01901   mtr_t*    mtr;
01902   mtr_t   mtr_buf;
01903   rec_t*    rec;
01904   mem_heap_t* heap    = NULL;
01905   ulint   offsets_[REC_OFFS_NORMAL_SIZE];
01906   ulint*    offsets;
01907   ibool   referenced;
01908   rec_offs_init(offsets_);
01909 
01910   index = dict_table_get_first_index(node->table);
01911 
01912   referenced = row_upd_index_is_referenced(index, thr_get_trx(thr));
01913 
01914   pcur = node->pcur;
01915 
01916   /* We have to restore the cursor to its position */
01917   mtr = &mtr_buf;
01918 
01919   mtr_start(mtr);
01920 
01921   /* If the restoration does not succeed, then the same
01922   transaction has deleted the record on which the cursor was,
01923   and that is an SQL error. If the restoration succeeds, it may
01924   still be that the same transaction has successively deleted
01925   and inserted a record with the same ordering fields, but in
01926   that case we know that the transaction has at least an
01927   implicit x-lock on the record. */
01928 
01929   ut_a(pcur->rel_pos == BTR_PCUR_ON);
01930 
01931   success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr);
01932 
01933   if (!success) {
01934     err = DB_RECORD_NOT_FOUND;
01935 
01936     mtr_commit(mtr);
01937 
01938     return(err);
01939   }
01940 
01941   /* If this is a row in SYS_INDEXES table of the data dictionary,
01942   then we have to free the file segments of the index tree associated
01943   with the index */
01944 
01945   if (node->is_delete && node->table->id == DICT_INDEXES_ID) {
01946 
01947     dict_drop_index_tree(btr_pcur_get_rec(pcur), mtr);
01948 
01949     mtr_commit(mtr);
01950 
01951     mtr_start(mtr);
01952 
01953     success = btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur,
01954                 mtr);
01955     if (!success) {
01956       err = DB_ERROR;
01957 
01958       mtr_commit(mtr);
01959 
01960       return(err);
01961     }
01962   }
01963 
01964   rec = btr_pcur_get_rec(pcur);
01965   offsets = rec_get_offsets(rec, index, offsets_,
01966           ULINT_UNDEFINED, &heap);
01967 
01968   if (!node->has_clust_rec_x_lock) {
01969     err = lock_clust_rec_modify_check_and_lock(
01970       0, btr_pcur_get_block(pcur),
01971       rec, index, offsets, thr);
01972     if (err != DB_SUCCESS) {
01973       mtr_commit(mtr);
01974       goto exit_func;
01975     }
01976   }
01977 
01978   /* NOTE: the following function calls will also commit mtr */
01979 
01980   if (node->is_delete) {
01981     err = row_upd_del_mark_clust_rec(
01982       node, index, offsets, thr, referenced, mtr);
01983 
01984     if (err == DB_SUCCESS) {
01985       node->state = UPD_NODE_UPDATE_ALL_SEC;
01986       node->index = dict_table_get_next_index(index);
01987     }
01988 exit_func:
01989     if (UNIV_LIKELY_NULL(heap)) {
01990       mem_heap_free(heap);
01991     }
01992     return(err);
01993   }
01994 
01995   /* If the update is made for MySQL, we already have the update vector
01996   ready, else we have to do some evaluation: */
01997 
01998   if (UNIV_UNLIKELY(!node->in_mysql_interface)) {
01999     /* Copy the necessary columns from clust_rec and calculate the
02000     new values to set */
02001     row_upd_copy_columns(rec, offsets,
02002              UT_LIST_GET_FIRST(node->columns));
02003     row_upd_eval_new_vals(node->update);
02004   }
02005 
02006   if (UNIV_LIKELY_NULL(heap)) {
02007     mem_heap_free(heap);
02008   }
02009 
02010   if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
02011 
02012     err = row_upd_clust_rec(node, index, thr, mtr);
02013     return(err);
02014   }
02015 
02016   row_upd_store_row(node);
02017 
02018   if (row_upd_changes_ord_field_binary(node->row, index, node->update)) {
02019 
02020     /* Update causes an ordering field (ordering fields within
02021     the B-tree) of the clustered index record to change: perform
02022     the update by delete marking and inserting.
02023 
02024     TODO! What to do to the 'Halloween problem', where an update
02025     moves the record forward in index so that it is again
02026     updated when the cursor arrives there? Solution: the
02027     read operation must check the undo record undo number when
02028     choosing records to update. MySQL solves now the problem
02029     externally! */
02030 
02031     err = row_upd_clust_rec_by_insert(
02032       node, index, thr, referenced, mtr);
02033 
02034     if (err != DB_SUCCESS) {
02035 
02036       return(err);
02037     }
02038 
02039     node->state = UPD_NODE_UPDATE_ALL_SEC;
02040   } else {
02041     err = row_upd_clust_rec(node, index, thr, mtr);
02042 
02043     if (err != DB_SUCCESS) {
02044 
02045       return(err);
02046     }
02047 
02048     node->state = UPD_NODE_UPDATE_SOME_SEC;
02049   }
02050 
02051   node->index = dict_table_get_next_index(index);
02052 
02053   return(err);
02054 }
02055 
02056 /***********************************************************/
02062 static
02063 ulint
02064 row_upd(
02065 /*====*/
02066   upd_node_t* node, 
02067   que_thr_t*  thr)  
02068 {
02069   ulint err = DB_SUCCESS;
02070 
02071   ut_ad(node && thr);
02072 
02073   if (UNIV_LIKELY(node->in_mysql_interface)) {
02074 
02075     /* We do not get the cmpl_info value from the MySQL
02076     interpreter: we must calculate it on the fly: */
02077 
02078     if (node->is_delete
02079         || row_upd_changes_some_index_ord_field_binary(
02080           node->table, node->update)) {
02081       node->cmpl_info = 0;
02082     } else {
02083       node->cmpl_info = UPD_NODE_NO_ORD_CHANGE;
02084     }
02085   }
02086 
02087   if (node->state == UPD_NODE_UPDATE_CLUSTERED
02088       || node->state == UPD_NODE_INSERT_CLUSTERED) {
02089 
02090     log_free_check();
02091     err = row_upd_clust_step(node, thr);
02092 
02093     if (err != DB_SUCCESS) {
02094 
02095       goto function_exit;
02096     }
02097   }
02098 
02099   if (!node->is_delete && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
02100 
02101     goto function_exit;
02102   }
02103 
02104   while (node->index != NULL) {
02105 
02106     log_free_check();
02107     err = row_upd_sec_step(node, thr);
02108 
02109     if (err != DB_SUCCESS) {
02110 
02111       goto function_exit;
02112     }
02113 
02114     node->index = dict_table_get_next_index(node->index);
02115   }
02116 
02117 function_exit:
02118   if (err == DB_SUCCESS) {
02119     /* Do some cleanup */
02120 
02121     if (node->row != NULL) {
02122       node->row = NULL;
02123       node->ext = NULL;
02124       node->upd_row = NULL;
02125       node->upd_ext = NULL;
02126       mem_heap_empty(node->heap);
02127     }
02128 
02129     node->state = UPD_NODE_UPDATE_CLUSTERED;
02130   }
02131 
02132   return(err);
02133 }
02134 
02135 /***********************************************************/
02139 UNIV_INTERN
02140 que_thr_t*
02141 row_upd_step(
02142 /*=========*/
02143   que_thr_t*  thr)  
02144 {
02145   upd_node_t* node;
02146   sel_node_t* sel_node;
02147   que_node_t* parent;
02148   ulint   err   = DB_SUCCESS;
02149   trx_t*    trx;
02150 
02151   ut_ad(thr);
02152 
02153   trx = thr_get_trx(thr);
02154 
02155   trx_start_if_not_started(trx);
02156 
02157         node = static_cast<upd_node_t *>(thr->run_node);
02158 
02159   sel_node = node->select;
02160 
02161   parent = que_node_get_parent(node);
02162 
02163   ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE);
02164 
02165   if (thr->prev_node == parent) {
02166     node->state = UPD_NODE_SET_IX_LOCK;
02167   }
02168 
02169   if (node->state == UPD_NODE_SET_IX_LOCK) {
02170 
02171     if (!node->has_clust_rec_x_lock) {
02172       /* It may be that the current session has not yet
02173       started its transaction, or it has been committed: */
02174 
02175       err = lock_table(0, node->table, LOCK_IX, thr);
02176 
02177       if (err != DB_SUCCESS) {
02178 
02179         goto error_handling;
02180       }
02181     }
02182 
02183     node->state = UPD_NODE_UPDATE_CLUSTERED;
02184 
02185     if (node->searched_update) {
02186       /* Reset the cursor */
02187       sel_node->state = SEL_NODE_OPEN;
02188 
02189       /* Fetch a row to update */
02190 
02191       thr->run_node = sel_node;
02192 
02193       return(thr);
02194     }
02195   }
02196 
02197   /* sel_node is NULL if we are in the MySQL interface */
02198 
02199   if (sel_node && (sel_node->state != SEL_NODE_FETCH)) {
02200 
02201     if (!node->searched_update) {
02202       /* An explicit cursor should be positioned on a row
02203       to update */
02204 
02205       ut_error;
02206 
02207       err = DB_ERROR;
02208 
02209       goto error_handling;
02210     }
02211 
02212     ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS);
02213 
02214     /* No more rows to update, or the select node performed the
02215     updates directly in-place */
02216 
02217     thr->run_node = parent;
02218 
02219     return(thr);
02220   }
02221 
02222   /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
02223 
02224   err = row_upd(node, thr);
02225 
02226 error_handling:
02227   trx->error_state = err;
02228 
02229   if (err != DB_SUCCESS) {
02230     return(NULL);
02231   }
02232 
02233   /* DO THE TRIGGER ACTIONS HERE */
02234 
02235   if (node->searched_update) {
02236     /* Fetch next row to update */
02237 
02238     thr->run_node = sel_node;
02239   } else {
02240     /* It was an explicit cursor update */
02241 
02242     thr->run_node = parent;
02243   }
02244 
02245   node->state = UPD_NODE_UPDATE_CLUSTERED;
02246 
02247   return(thr);
02248 }
02249 #endif /* !UNIV_HOTBACKUP */