~drizzle-trunk/drizzle/development

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