~drizzle-trunk/drizzle/development

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