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/row0ins.c
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
21 |
Insert into a table
|
22 |
||
23 |
Created 4/20/1996 Heikki Tuuri
|
|
24 |
*******************************************************/
|
|
25 |
||
26 |
#include "row0ins.h" |
|
27 |
||
28 |
#ifdef UNIV_NONINL
|
|
29 |
#include "row0ins.ic" |
|
30 |
#endif
|
|
31 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
32 |
#include "ha_prototypes.h" |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
33 |
#include "dict0dict.h" |
34 |
#include "dict0boot.h" |
|
35 |
#include "trx0undo.h" |
|
36 |
#include "btr0btr.h" |
|
37 |
#include "btr0cur.h" |
|
38 |
#include "mach0data.h" |
|
39 |
#include "que0que.h" |
|
40 |
#include "row0upd.h" |
|
41 |
#include "row0sel.h" |
|
42 |
#include "row0row.h" |
|
43 |
#include "rem0cmp.h" |
|
44 |
#include "lock0lock.h" |
|
45 |
#include "log0log.h" |
|
46 |
#include "eval0eval.h" |
|
47 |
#include "data0data.h" |
|
48 |
#include "usr0sess.h" |
|
49 |
#include "buf0lru.h" |
|
50 |
||
51 |
#define ROW_INS_PREV 1
|
|
52 |
#define ROW_INS_NEXT 2
|
|
53 |
||
1819.9.13
by Inaam Rana
Merge Revision revid:inaam.rana@oracle.com-20100610135811-1jxs81q050wpabyc from MySQL InnoDB |
54 |
/*************************************************************************
|
1819.9.12
by Inaam Rana
Merge Revision revid:inaam.rana@oracle.com-20100608181408-c5xsjlom5olr4mxt from MySQL InnoDB |
55 |
IMPORTANT NOTE: Any operation that generates redo MUST check that there
|
56 |
is enough space in the redo log before for that operation. This is
|
|
57 |
done by calling log_free_check(). The reason for checking the
|
|
58 |
availability of the redo log space before the start of the operation is
|
|
59 |
that we MUST not hold any synchonization objects when performing the
|
|
60 |
check.
|
|
61 |
If you make a change in this module make sure that no codepath is
|
|
62 |
introduced where a call to log_free_check() is bypassed. */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
63 |
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
64 |
/*********************************************************************//**
|
65 |
Creates an insert node struct.
|
|
66 |
@return own: insert node struct */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
67 |
UNIV_INTERN
|
68 |
ins_node_t* |
|
69 |
ins_node_create( |
|
70 |
/*============*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
71 |
ulint ins_type, /*!< in: INS_VALUES, ... */ |
72 |
dict_table_t* table, /*!< in: table where to insert */ |
|
73 |
mem_heap_t* heap) /*!< in: mem heap where created */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
74 |
{
|
75 |
ins_node_t* node; |
|
76 |
||
2023.3.13
by Monty Taylor
More. |
77 |
node = static_cast<ins_node_t *>(mem_heap_alloc(heap, sizeof(ins_node_t))); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
78 |
|
79 |
node->common.type = QUE_NODE_INSERT; |
|
80 |
||
81 |
node->ins_type = ins_type; |
|
82 |
||
83 |
node->state = INS_NODE_SET_IX_LOCK; |
|
84 |
node->table = table; |
|
85 |
node->index = NULL; |
|
86 |
node->entry = NULL; |
|
87 |
||
88 |
node->select = NULL; |
|
89 |
||
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
90 |
node->trx_id = 0; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
91 |
|
92 |
node->entry_sys_heap = mem_heap_create(128); |
|
93 |
||
94 |
node->magic_n = INS_NODE_MAGIC_N; |
|
95 |
||
96 |
return(node); |
|
97 |
}
|
|
98 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
99 |
/***********************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
100 |
Creates an entry template for each index of a table. */
|
101 |
UNIV_INTERN
|
|
102 |
void
|
|
103 |
ins_node_create_entry_list( |
|
104 |
/*=======================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
105 |
ins_node_t* node) /*!< in: row insert node */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
106 |
{
|
107 |
dict_index_t* index; |
|
108 |
dtuple_t* entry; |
|
109 |
||
110 |
ut_ad(node->entry_sys_heap); |
|
111 |
||
112 |
UT_LIST_INIT(node->entry_list); |
|
113 |
||
114 |
index = dict_table_get_first_index(node->table); |
|
115 |
||
116 |
while (index != NULL) { |
|
117 |
entry = row_build_index_entry(node->row, NULL, index, |
|
118 |
node->entry_sys_heap); |
|
119 |
UT_LIST_ADD_LAST(tuple_list, node->entry_list, entry); |
|
120 |
||
121 |
index = dict_table_get_next_index(index); |
|
122 |
}
|
|
123 |
}
|
|
124 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
125 |
/*****************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
126 |
Adds system field buffers to a row. */
|
127 |
static
|
|
128 |
void
|
|
129 |
row_ins_alloc_sys_fields( |
|
130 |
/*=====================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
131 |
ins_node_t* node) /*!< in: insert node */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
132 |
{
|
133 |
dtuple_t* row; |
|
134 |
dict_table_t* table; |
|
135 |
mem_heap_t* heap; |
|
136 |
const dict_col_t* col; |
|
137 |
dfield_t* dfield; |
|
138 |
byte* ptr; |
|
139 |
||
140 |
row = node->row; |
|
141 |
table = node->table; |
|
142 |
heap = node->entry_sys_heap; |
|
143 |
||
144 |
ut_ad(row && table && heap); |
|
145 |
ut_ad(dtuple_get_n_fields(row) == dict_table_get_n_cols(table)); |
|
146 |
||
147 |
/* 1. Allocate buffer for row id */
|
|
148 |
||
149 |
col = dict_table_get_sys_col(table, DATA_ROW_ID); |
|
150 |
||
151 |
dfield = dtuple_get_nth_field(row, dict_col_get_no(col)); |
|
152 |
||
2023.3.13
by Monty Taylor
More. |
153 |
ptr = static_cast<byte *>(mem_heap_zalloc(heap, DATA_ROW_ID_LEN)); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
154 |
|
155 |
dfield_set_data(dfield, ptr, DATA_ROW_ID_LEN); |
|
156 |
||
157 |
node->row_id_buf = ptr; |
|
158 |
||
159 |
/* 3. Allocate buffer for trx id */
|
|
160 |
||
161 |
col = dict_table_get_sys_col(table, DATA_TRX_ID); |
|
162 |
||
163 |
dfield = dtuple_get_nth_field(row, dict_col_get_no(col)); |
|
2023.3.13
by Monty Taylor
More. |
164 |
ptr = static_cast<byte *>(mem_heap_zalloc(heap, DATA_TRX_ID_LEN)); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
165 |
|
166 |
dfield_set_data(dfield, ptr, DATA_TRX_ID_LEN); |
|
167 |
||
168 |
node->trx_id_buf = ptr; |
|
169 |
||
170 |
/* 4. Allocate buffer for roll ptr */
|
|
171 |
||
172 |
col = dict_table_get_sys_col(table, DATA_ROLL_PTR); |
|
173 |
||
174 |
dfield = dtuple_get_nth_field(row, dict_col_get_no(col)); |
|
2023.3.13
by Monty Taylor
More. |
175 |
ptr = static_cast<byte *>(mem_heap_zalloc(heap, DATA_ROLL_PTR_LEN)); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
176 |
|
177 |
dfield_set_data(dfield, ptr, DATA_ROLL_PTR_LEN); |
|
178 |
}
|
|
179 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
180 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
181 |
Sets a new row to insert for an INS_DIRECT node. This function is only used
|
182 |
if we have constructed the row separately, which is a rare case; this
|
|
183 |
function is quite slow. */
|
|
184 |
UNIV_INTERN
|
|
185 |
void
|
|
186 |
ins_node_set_new_row( |
|
187 |
/*=================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
188 |
ins_node_t* node, /*!< in: insert node */ |
189 |
dtuple_t* row) /*!< in: new row (or first row) for the node */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
190 |
{
|
191 |
node->state = INS_NODE_SET_IX_LOCK; |
|
192 |
node->index = NULL; |
|
193 |
node->entry = NULL; |
|
194 |
||
195 |
node->row = row; |
|
196 |
||
197 |
mem_heap_empty(node->entry_sys_heap); |
|
198 |
||
199 |
/* Create templates for index entries */
|
|
200 |
||
201 |
ins_node_create_entry_list(node); |
|
202 |
||
203 |
/* Allocate from entry_sys_heap buffers for sys fields */
|
|
204 |
||
205 |
row_ins_alloc_sys_fields(node); |
|
206 |
||
207 |
/* As we allocated a new trx id buf, the trx id should be written
|
|
208 |
there again: */
|
|
209 |
||
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
210 |
node->trx_id = 0; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
211 |
}
|
212 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
213 |
/*******************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
214 |
Does an insert operation by updating a delete-marked existing record
|
215 |
in the index. This situation can occur if the delete-marked record is
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
216 |
kept in the index for consistent reads.
|
217 |
@return DB_SUCCESS or error code */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
218 |
static
|
219 |
ulint
|
|
220 |
row_ins_sec_index_entry_by_modify( |
|
221 |
/*==============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
222 |
ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
223 |
depending on whether mtr holds just a leaf
|
224 |
latch or also a tree latch */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
225 |
btr_cur_t* cursor, /*!< in: B-tree cursor */ |
226 |
const dtuple_t* entry, /*!< in: index entry to insert */ |
|
227 |
que_thr_t* thr, /*!< in: query thread */ |
|
228 |
mtr_t* mtr) /*!< in: mtr; must be committed before |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
229 |
latching any further pages */
|
230 |
{
|
|
231 |
big_rec_t* dummy_big_rec; |
|
232 |
mem_heap_t* heap; |
|
233 |
upd_t* update; |
|
234 |
rec_t* rec; |
|
235 |
ulint err; |
|
236 |
||
237 |
rec = btr_cur_get_rec(cursor); |
|
238 |
||
239 |
ut_ad(!dict_index_is_clust(cursor->index)); |
|
240 |
ut_ad(rec_get_deleted_flag(rec, |
|
241 |
dict_table_is_comp(cursor->index->table))); |
|
242 |
||
243 |
/* We know that in the alphabetical ordering, entry and rec are
|
|
244 |
identified. But in their binary form there may be differences if
|
|
245 |
there are char fields in them. Therefore we have to calculate the
|
|
246 |
difference. */
|
|
247 |
||
248 |
heap = mem_heap_create(1024); |
|
249 |
||
250 |
update = row_upd_build_sec_rec_difference_binary( |
|
251 |
cursor->index, entry, rec, thr_get_trx(thr), heap); |
|
252 |
if (mode == BTR_MODIFY_LEAF) { |
|
253 |
/* Try an optimistic updating of the record, keeping changes
|
|
254 |
within the page */
|
|
255 |
||
256 |
err = btr_cur_optimistic_update(BTR_KEEP_SYS_FLAG, cursor, |
|
257 |
update, 0, thr, mtr); |
|
258 |
switch (err) { |
|
259 |
case DB_OVERFLOW: |
|
260 |
case DB_UNDERFLOW: |
|
261 |
case DB_ZIP_OVERFLOW: |
|
262 |
err = DB_FAIL; |
|
263 |
}
|
|
264 |
} else { |
|
265 |
ut_a(mode == BTR_MODIFY_TREE); |
|
266 |
if (buf_LRU_buf_pool_running_out()) { |
|
267 |
||
268 |
err = DB_LOCK_TABLE_FULL; |
|
269 |
||
270 |
goto func_exit; |
|
271 |
}
|
|
272 |
||
273 |
err = btr_cur_pessimistic_update(BTR_KEEP_SYS_FLAG, cursor, |
|
274 |
&heap, &dummy_big_rec, update, |
|
275 |
0, thr, mtr); |
|
276 |
ut_ad(!dummy_big_rec); |
|
277 |
}
|
|
278 |
func_exit: |
|
279 |
mem_heap_free(heap); |
|
280 |
||
281 |
return(err); |
|
282 |
}
|
|
283 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
284 |
/*******************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
285 |
Does an insert operation by delete unmarking and updating a delete marked
|
286 |
existing record in the index. This situation can occur if the delete marked
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
287 |
record is kept in the index for consistent reads.
|
288 |
@return DB_SUCCESS, DB_FAIL, or error code */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
289 |
static
|
290 |
ulint
|
|
291 |
row_ins_clust_index_entry_by_modify( |
|
292 |
/*================================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
293 |
ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
294 |
depending on whether mtr holds just a leaf
|
295 |
latch or also a tree latch */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
296 |
btr_cur_t* cursor, /*!< in: B-tree cursor */ |
297 |
mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */ |
|
298 |
big_rec_t** big_rec,/*!< out: possible big rec vector of fields |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
299 |
which have to be stored externally by the
|
300 |
caller */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
301 |
const dtuple_t* entry, /*!< in: index entry to insert */ |
302 |
que_thr_t* thr, /*!< in: query thread */ |
|
303 |
mtr_t* mtr) /*!< in: mtr; must be committed before |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
304 |
latching any further pages */
|
305 |
{
|
|
306 |
rec_t* rec; |
|
307 |
upd_t* update; |
|
308 |
ulint err; |
|
309 |
||
310 |
ut_ad(dict_index_is_clust(cursor->index)); |
|
311 |
||
312 |
*big_rec = NULL; |
|
313 |
||
314 |
rec = btr_cur_get_rec(cursor); |
|
315 |
||
316 |
ut_ad(rec_get_deleted_flag(rec, |
|
317 |
dict_table_is_comp(cursor->index->table))); |
|
318 |
||
319 |
if (!*heap) { |
|
320 |
*heap = mem_heap_create(1024); |
|
321 |
}
|
|
322 |
||
323 |
/* Build an update vector containing all the fields to be modified;
|
|
324 |
NOTE that this vector may NOT contain system columns trx_id or
|
|
325 |
roll_ptr */
|
|
326 |
||
327 |
update = row_upd_build_difference_binary(cursor->index, entry, rec, |
|
328 |
thr_get_trx(thr), *heap); |
|
329 |
if (mode == BTR_MODIFY_LEAF) { |
|
330 |
/* Try optimistic updating of the record, keeping changes
|
|
331 |
within the page */
|
|
332 |
||
333 |
err = btr_cur_optimistic_update(0, cursor, update, 0, thr, |
|
334 |
mtr); |
|
335 |
switch (err) { |
|
336 |
case DB_OVERFLOW: |
|
337 |
case DB_UNDERFLOW: |
|
338 |
case DB_ZIP_OVERFLOW: |
|
339 |
err = DB_FAIL; |
|
340 |
}
|
|
341 |
} else { |
|
342 |
ut_a(mode == BTR_MODIFY_TREE); |
|
343 |
if (buf_LRU_buf_pool_running_out()) { |
|
344 |
||
345 |
return(DB_LOCK_TABLE_FULL); |
|
346 |
||
347 |
}
|
|
348 |
err = btr_cur_pessimistic_update(0, cursor, |
|
349 |
heap, big_rec, update, |
|
350 |
0, thr, mtr); |
|
351 |
}
|
|
352 |
||
353 |
return(err); |
|
354 |
}
|
|
355 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
356 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
357 |
Returns TRUE if in a cascaded update/delete an ancestor node of node
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
358 |
updates (not DELETE, but UPDATE) table.
|
359 |
@return TRUE if an ancestor updates table */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
360 |
static
|
361 |
ibool
|
|
362 |
row_ins_cascade_ancestor_updates_table( |
|
363 |
/*===================================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
364 |
que_node_t* node, /*!< in: node in a query graph */ |
365 |
dict_table_t* table) /*!< in: table */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
366 |
{
|
367 |
que_node_t* parent; |
|
368 |
upd_node_t* upd_node; |
|
369 |
||
370 |
parent = que_node_get_parent(node); |
|
371 |
||
372 |
while (que_node_get_type(parent) == QUE_NODE_UPDATE) { |
|
373 |
||
2023.3.13
by Monty Taylor
More. |
374 |
upd_node = static_cast<upd_node_t *>(parent); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
375 |
|
376 |
if (upd_node->table == table && upd_node->is_delete == FALSE) { |
|
377 |
||
378 |
return(TRUE); |
|
379 |
}
|
|
380 |
||
381 |
parent = que_node_get_parent(parent); |
|
382 |
||
383 |
ut_a(parent); |
|
384 |
}
|
|
385 |
||
386 |
return(FALSE); |
|
387 |
}
|
|
388 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
389 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
390 |
Returns the number of ancestor UPDATE or DELETE nodes of a
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
391 |
cascaded update/delete node.
|
392 |
@return number of ancestors */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
393 |
static
|
394 |
ulint
|
|
395 |
row_ins_cascade_n_ancestors( |
|
396 |
/*========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
397 |
que_node_t* node) /*!< in: node in a query graph */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
398 |
{
|
399 |
que_node_t* parent; |
|
400 |
ulint n_ancestors = 0; |
|
401 |
||
402 |
parent = que_node_get_parent(node); |
|
403 |
||
404 |
while (que_node_get_type(parent) == QUE_NODE_UPDATE) { |
|
405 |
n_ancestors++; |
|
406 |
||
407 |
parent = que_node_get_parent(parent); |
|
408 |
||
409 |
ut_a(parent); |
|
410 |
}
|
|
411 |
||
412 |
return(n_ancestors); |
|
413 |
}
|
|
414 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
415 |
/******************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
416 |
Calculates the update vector node->cascade->update for a child table in
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
417 |
a cascaded update.
|
418 |
@return number of fields in the calculated update vector; the value
|
|
419 |
can also be 0 if no foreign key fields changed; the returned value is
|
|
420 |
ULINT_UNDEFINED if the column type in the child table is too short to
|
|
421 |
fit the new value in the parent table: that means the update fails */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
422 |
static
|
423 |
ulint
|
|
424 |
row_ins_cascade_calc_update_vec( |
|
425 |
/*============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
426 |
upd_node_t* node, /*!< in: update node of the parent |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
427 |
table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
428 |
dict_foreign_t* foreign, /*!< in: foreign key constraint whose |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
429 |
type is != 0 */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
430 |
mem_heap_t* heap) /*!< in: memory heap to use as |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
431 |
temporary storage */
|
432 |
{
|
|
433 |
upd_node_t* cascade = node->cascade_node; |
|
434 |
dict_table_t* table = foreign->foreign_table; |
|
435 |
dict_index_t* index = foreign->foreign_index; |
|
436 |
upd_t* update; |
|
437 |
upd_field_t* ufield; |
|
438 |
dict_table_t* parent_table; |
|
439 |
dict_index_t* parent_index; |
|
440 |
upd_t* parent_update; |
|
441 |
upd_field_t* parent_ufield; |
|
442 |
ulint n_fields_updated; |
|
443 |
ulint parent_field_no; |
|
444 |
ulint i; |
|
445 |
ulint j; |
|
446 |
||
447 |
ut_a(node); |
|
448 |
ut_a(foreign); |
|
449 |
ut_a(cascade); |
|
450 |
ut_a(table); |
|
451 |
ut_a(index); |
|
452 |
||
453 |
/* Calculate the appropriate update vector which will set the fields
|
|
454 |
in the child index record to the same value (possibly padded with
|
|
455 |
spaces if the column is a fixed length CHAR or FIXBINARY column) as
|
|
456 |
the referenced index record will get in the update. */
|
|
457 |
||
458 |
parent_table = node->table; |
|
459 |
ut_a(parent_table == foreign->referenced_table); |
|
460 |
parent_index = foreign->referenced_index; |
|
461 |
parent_update = node->update; |
|
462 |
||
463 |
update = cascade->update; |
|
464 |
||
465 |
update->info_bits = 0; |
|
466 |
update->n_fields = foreign->n_fields; |
|
467 |
||
468 |
n_fields_updated = 0; |
|
469 |
||
470 |
for (i = 0; i < foreign->n_fields; i++) { |
|
471 |
||
472 |
parent_field_no = dict_table_get_nth_col_pos( |
|
473 |
parent_table, |
|
474 |
dict_index_get_nth_col_no(parent_index, i)); |
|
475 |
||
476 |
for (j = 0; j < parent_update->n_fields; j++) { |
|
477 |
parent_ufield = parent_update->fields + j; |
|
478 |
||
479 |
if (parent_ufield->field_no == parent_field_no) { |
|
480 |
||
481 |
ulint min_size; |
|
482 |
const dict_col_t* col; |
|
483 |
ulint ufield_len; |
|
484 |
||
485 |
col = dict_index_get_nth_col(index, i); |
|
486 |
||
487 |
/* A field in the parent index record is
|
|
488 |
updated. Let us make the update vector
|
|
489 |
field for the child table. */
|
|
490 |
||
491 |
ufield = update->fields + n_fields_updated; |
|
492 |
||
493 |
ufield->field_no |
|
494 |
= dict_table_get_nth_col_pos( |
|
495 |
table, dict_col_get_no(col)); |
|
496 |
ufield->exp = NULL; |
|
497 |
||
498 |
ufield->new_val = parent_ufield->new_val; |
|
499 |
ufield_len = dfield_get_len(&ufield->new_val); |
|
500 |
||
501 |
/* Clear the "external storage" flag */
|
|
502 |
dfield_set_len(&ufield->new_val, ufield_len); |
|
503 |
||
504 |
/* Do not allow a NOT NULL column to be
|
|
505 |
updated as NULL */
|
|
506 |
||
507 |
if (dfield_is_null(&ufield->new_val) |
|
508 |
&& (col->prtype & DATA_NOT_NULL)) { |
|
509 |
||
510 |
return(ULINT_UNDEFINED); |
|
511 |
}
|
|
512 |
||
513 |
/* If the new value would not fit in the
|
|
514 |
column, do not allow the update */
|
|
515 |
||
516 |
if (!dfield_is_null(&ufield->new_val) |
|
517 |
&& dtype_get_at_most_n_mbchars( |
|
1819.9.36
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100629113248-fvl48lnzr44z94gg from MySQL InnoDB |
518 |
col->prtype, col->mbminmaxlen, |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
519 |
col->len, |
520 |
ufield_len, |
|
2023.3.13
by Monty Taylor
More. |
521 |
static_cast<const char *>(dfield_get_data(&ufield->new_val))) |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
522 |
< ufield_len) { |
523 |
||
524 |
return(ULINT_UNDEFINED); |
|
525 |
}
|
|
526 |
||
527 |
/* If the parent column type has a different
|
|
528 |
length than the child column type, we may
|
|
529 |
need to pad with spaces the new value of the
|
|
530 |
child column */
|
|
531 |
||
532 |
min_size = dict_col_get_min_size(col); |
|
533 |
||
534 |
/* Because UNIV_SQL_NULL (the marker
|
|
535 |
of SQL NULL values) exceeds all possible
|
|
536 |
values of min_size, the test below will
|
|
537 |
not hold for SQL NULL columns. */
|
|
538 |
||
539 |
if (min_size > ufield_len) { |
|
540 |
||
1819.9.36
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100629113248-fvl48lnzr44z94gg from MySQL InnoDB |
541 |
byte* pad; |
542 |
ulint pad_len; |
|
543 |
byte* padded_data; |
|
544 |
ulint mbminlen; |
|
545 |
||
2023.3.13
by Monty Taylor
More. |
546 |
padded_data = static_cast<unsigned char *>(mem_heap_alloc( |
547 |
heap, min_size)); |
|
1819.9.36
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100629113248-fvl48lnzr44z94gg from MySQL InnoDB |
548 |
|
549 |
pad = padded_data + ufield_len; |
|
550 |
pad_len = min_size - ufield_len; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
551 |
|
552 |
memcpy(padded_data, |
|
553 |
dfield_get_data(&ufield |
|
554 |
->new_val), |
|
1819.9.36
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100629113248-fvl48lnzr44z94gg from MySQL InnoDB |
555 |
ufield_len); |
556 |
||
557 |
mbminlen = dict_col_get_mbminlen(col); |
|
558 |
||
559 |
ut_ad(!(ufield_len % mbminlen)); |
|
560 |
ut_ad(!(min_size % mbminlen)); |
|
561 |
||
562 |
if (mbminlen == 1 |
|
563 |
&& dtype_get_charset_coll( |
|
564 |
col->prtype) |
|
565 |
== DATA_MYSQL_BINARY_CHARSET_COLL) { |
|
566 |
/* Do not pad BINARY columns */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
567 |
return(ULINT_UNDEFINED); |
568 |
}
|
|
569 |
||
1819.9.36
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100629113248-fvl48lnzr44z94gg from MySQL InnoDB |
570 |
row_mysql_pad_col(mbminlen, |
571 |
pad, pad_len); |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
572 |
dfield_set_data(&ufield->new_val, |
573 |
padded_data, min_size); |
|
574 |
}
|
|
575 |
||
576 |
n_fields_updated++; |
|
577 |
}
|
|
578 |
}
|
|
579 |
}
|
|
580 |
||
581 |
update->n_fields = n_fields_updated; |
|
582 |
||
583 |
return(n_fields_updated); |
|
584 |
}
|
|
585 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
586 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
587 |
Set detailed error message associated with foreign key errors for
|
588 |
the given transaction. */
|
|
589 |
static
|
|
590 |
void
|
|
591 |
row_ins_set_detailed( |
|
592 |
/*=================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
593 |
trx_t* trx, /*!< in: transaction */ |
594 |
dict_foreign_t* foreign) /*!< in: foreign key constraint */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
595 |
{
|
596 |
mutex_enter(&srv_misc_tmpfile_mutex); |
|
597 |
rewind(srv_misc_tmpfile); |
|
598 |
||
599 |
if (os_file_set_eof(srv_misc_tmpfile)) { |
|
600 |
ut_print_name(srv_misc_tmpfile, trx, TRUE, |
|
601 |
foreign->foreign_table_name); |
|
602 |
dict_print_info_on_foreign_key_in_create_format( |
|
603 |
srv_misc_tmpfile, trx, foreign, FALSE); |
|
604 |
trx_set_detailed_error_from_file(trx, srv_misc_tmpfile); |
|
605 |
} else { |
|
606 |
trx_set_detailed_error(trx, "temp file operation failed"); |
|
607 |
}
|
|
608 |
||
609 |
mutex_exit(&srv_misc_tmpfile_mutex); |
|
610 |
}
|
|
611 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
612 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
613 |
Reports a foreign key error associated with an update or a delete of a
|
614 |
parent table index entry. */
|
|
615 |
static
|
|
616 |
void
|
|
617 |
row_ins_foreign_report_err( |
|
618 |
/*=======================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
619 |
const char* errstr, /*!< in: error string from the viewpoint |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
620 |
of the parent table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
621 |
que_thr_t* thr, /*!< in: query thread whose run_node |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
622 |
is an update node */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
623 |
dict_foreign_t* foreign, /*!< in: foreign key constraint */ |
624 |
const rec_t* rec, /*!< in: a matching index record in the |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
625 |
child table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
626 |
const dtuple_t* entry) /*!< in: index entry in the parent |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
627 |
table */
|
628 |
{
|
|
629 |
FILE* ef = dict_foreign_err_file; |
|
630 |
trx_t* trx = thr_get_trx(thr); |
|
631 |
||
632 |
row_ins_set_detailed(trx, foreign); |
|
633 |
||
634 |
mutex_enter(&dict_foreign_err_mutex); |
|
635 |
rewind(ef); |
|
636 |
ut_print_timestamp(ef); |
|
637 |
fputs(" Transaction:\n", ef); |
|
638 |
trx_print(ef, trx, 600); |
|
639 |
||
640 |
fputs("Foreign key constraint fails for table ", ef); |
|
641 |
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); |
|
642 |
fputs(":\n", ef); |
|
643 |
dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign, |
|
644 |
TRUE); |
|
645 |
putc('\n', ef); |
|
646 |
fputs(errstr, ef); |
|
647 |
fputs(" in parent table, in index ", ef); |
|
648 |
ut_print_name(ef, trx, FALSE, foreign->referenced_index->name); |
|
649 |
if (entry) { |
|
650 |
fputs(" tuple:\n", ef); |
|
651 |
dtuple_print(ef, entry); |
|
652 |
}
|
|
653 |
fputs("\nBut in child table ", ef); |
|
654 |
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); |
|
655 |
fputs(", in index ", ef); |
|
656 |
ut_print_name(ef, trx, FALSE, foreign->foreign_index->name); |
|
657 |
if (rec) { |
|
658 |
fputs(", there is a record:\n", ef); |
|
659 |
rec_print(ef, rec, foreign->foreign_index); |
|
660 |
} else { |
|
661 |
fputs(", the record is not available\n", ef); |
|
662 |
}
|
|
663 |
putc('\n', ef); |
|
664 |
||
665 |
mutex_exit(&dict_foreign_err_mutex); |
|
666 |
}
|
|
667 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
668 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
669 |
Reports a foreign key error to dict_foreign_err_file when we are trying
|
670 |
to add an index entry to a child table. Note that the adding may be the result
|
|
671 |
of an update, too. */
|
|
672 |
static
|
|
673 |
void
|
|
674 |
row_ins_foreign_report_add_err( |
|
675 |
/*===========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
676 |
trx_t* trx, /*!< in: transaction */ |
677 |
dict_foreign_t* foreign, /*!< in: foreign key constraint */ |
|
678 |
const rec_t* rec, /*!< in: a record in the parent table: |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
679 |
it does not match entry because we
|
680 |
have an error! */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
681 |
const dtuple_t* entry) /*!< in: index entry to insert in the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
682 |
child table */
|
683 |
{
|
|
684 |
FILE* ef = dict_foreign_err_file; |
|
685 |
||
686 |
row_ins_set_detailed(trx, foreign); |
|
687 |
||
688 |
mutex_enter(&dict_foreign_err_mutex); |
|
689 |
rewind(ef); |
|
690 |
ut_print_timestamp(ef); |
|
691 |
fputs(" Transaction:\n", ef); |
|
692 |
trx_print(ef, trx, 600); |
|
693 |
fputs("Foreign key constraint fails for table ", ef); |
|
694 |
ut_print_name(ef, trx, TRUE, foreign->foreign_table_name); |
|
695 |
fputs(":\n", ef); |
|
696 |
dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign, |
|
697 |
TRUE); |
|
698 |
fputs("\nTrying to add in child table, in index ", ef); |
|
699 |
ut_print_name(ef, trx, FALSE, foreign->foreign_index->name); |
|
700 |
if (entry) { |
|
701 |
fputs(" tuple:\n", ef); |
|
702 |
/* TODO: DB_TRX_ID and DB_ROLL_PTR may be uninitialized.
|
|
703 |
It would be better to only display the user columns. */
|
|
704 |
dtuple_print(ef, entry); |
|
705 |
}
|
|
706 |
fputs("\nBut in parent table ", ef); |
|
707 |
ut_print_name(ef, trx, TRUE, foreign->referenced_table_name); |
|
708 |
fputs(", in index ", ef); |
|
709 |
ut_print_name(ef, trx, FALSE, foreign->referenced_index->name); |
|
710 |
fputs(",\nthe closest match we can find is record:\n", ef); |
|
711 |
if (rec && page_rec_is_supremum(rec)) { |
|
712 |
/* If the cursor ended on a supremum record, it is better
|
|
713 |
to report the previous record in the error message, so that
|
|
714 |
the user gets a more descriptive error message. */
|
|
715 |
rec = page_rec_get_prev_const(rec); |
|
716 |
}
|
|
717 |
||
718 |
if (rec) { |
|
719 |
rec_print(ef, rec, foreign->referenced_index); |
|
720 |
}
|
|
721 |
putc('\n', ef); |
|
722 |
||
723 |
mutex_exit(&dict_foreign_err_mutex); |
|
724 |
}
|
|
725 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
726 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
727 |
Invalidate the query cache for the given table. */
|
728 |
static
|
|
729 |
void
|
|
730 |
row_ins_invalidate_query_cache( |
|
731 |
/*===========================*/
|
|
1114.1.1
by Monty Taylor
Merged InnoDB Plugin 1.0.4 |
732 |
que_thr_t* unused, /*!< in: query thread whose run_node |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
733 |
is an update node */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
734 |
const char* name) /*!< in: table name prefixed with |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
735 |
database name and a '/' character */
|
736 |
{
|
|
737 |
char* buf; |
|
738 |
char* ptr; |
|
739 |
ulint len = strlen(name) + 1; |
|
740 |
||
1003.1.19
by Brian Aker
Clean up final code bits (from previous builds). |
741 |
(void)unused; |
742 |
||
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
743 |
buf = mem_strdupl(name, len); |
744 |
||
745 |
ptr = strchr(buf, '/'); |
|
746 |
ut_a(ptr); |
|
747 |
*ptr = '\0'; |
|
748 |
||
749 |
mem_free(buf); |
|
750 |
}
|
|
751 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
752 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
753 |
Perform referential actions or checks when a parent row is deleted or updated
|
754 |
and the constraint had an ON DELETE or ON UPDATE condition which was not
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
755 |
RESTRICT.
|
756 |
@return DB_SUCCESS, DB_LOCK_WAIT, or error code */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
757 |
static
|
758 |
ulint
|
|
759 |
row_ins_foreign_check_on_constraint( |
|
760 |
/*================================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
761 |
que_thr_t* thr, /*!< in: query thread whose run_node |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
762 |
is an update node */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
763 |
dict_foreign_t* foreign, /*!< in: foreign key constraint whose |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
764 |
type is != 0 */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
765 |
btr_pcur_t* pcur, /*!< in: cursor placed on a matching |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
766 |
index record in the child table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
767 |
dtuple_t* entry, /*!< in: index entry in the parent |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
768 |
table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
769 |
mtr_t* mtr) /*!< in: mtr holding the latch of pcur |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
770 |
page */
|
771 |
{
|
|
772 |
upd_node_t* node; |
|
773 |
upd_node_t* cascade; |
|
774 |
dict_table_t* table = foreign->foreign_table; |
|
775 |
dict_index_t* index; |
|
776 |
dict_index_t* clust_index; |
|
777 |
dtuple_t* ref; |
|
778 |
mem_heap_t* upd_vec_heap = NULL; |
|
779 |
const rec_t* rec; |
|
780 |
const rec_t* clust_rec; |
|
781 |
const buf_block_t* clust_block; |
|
782 |
upd_t* update; |
|
783 |
ulint n_to_update; |
|
784 |
ulint err; |
|
785 |
ulint i; |
|
786 |
trx_t* trx; |
|
787 |
mem_heap_t* tmp_heap = NULL; |
|
788 |
||
789 |
ut_a(thr); |
|
790 |
ut_a(foreign); |
|
791 |
ut_a(pcur); |
|
792 |
ut_a(mtr); |
|
793 |
||
794 |
trx = thr_get_trx(thr); |
|
795 |
||
796 |
/* Since we are going to delete or update a row, we have to invalidate
|
|
797 |
the MySQL query cache for table. A deadlock of threads is not possible
|
|
798 |
here because the caller of this function does not hold any latches with
|
|
799 |
the sync0sync.h rank above the kernel mutex. The query cache mutex has
|
|
800 |
a rank just above the kernel mutex. */
|
|
801 |
||
802 |
row_ins_invalidate_query_cache(thr, table->name); |
|
803 |
||
2023.3.13
by Monty Taylor
More. |
804 |
node = static_cast<upd_node_t *>(thr->run_node); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
805 |
|
806 |
if (node->is_delete && 0 == (foreign->type |
|
807 |
& (DICT_FOREIGN_ON_DELETE_CASCADE |
|
808 |
| DICT_FOREIGN_ON_DELETE_SET_NULL))) { |
|
809 |
||
810 |
row_ins_foreign_report_err("Trying to delete", |
|
811 |
thr, foreign, |
|
812 |
btr_pcur_get_rec(pcur), entry); |
|
813 |
||
814 |
return(DB_ROW_IS_REFERENCED); |
|
815 |
}
|
|
816 |
||
817 |
if (!node->is_delete && 0 == (foreign->type |
|
818 |
& (DICT_FOREIGN_ON_UPDATE_CASCADE |
|
819 |
| DICT_FOREIGN_ON_UPDATE_SET_NULL))) { |
|
820 |
||
821 |
/* This is an UPDATE */
|
|
822 |
||
823 |
row_ins_foreign_report_err("Trying to update", |
|
824 |
thr, foreign, |
|
825 |
btr_pcur_get_rec(pcur), entry); |
|
826 |
||
827 |
return(DB_ROW_IS_REFERENCED); |
|
828 |
}
|
|
829 |
||
830 |
if (node->cascade_node == NULL) { |
|
831 |
/* Extend our query graph by creating a child to current
|
|
832 |
update node. The child is used in the cascade or set null
|
|
833 |
operation. */
|
|
834 |
||
835 |
node->cascade_heap = mem_heap_create(128); |
|
836 |
node->cascade_node = row_create_update_node_for_mysql( |
|
837 |
table, node->cascade_heap); |
|
838 |
que_node_set_parent(node->cascade_node, node); |
|
839 |
}
|
|
840 |
||
841 |
/* Initialize cascade_node to do the operation we want. Note that we
|
|
842 |
use the SAME cascade node to do all foreign key operations of the
|
|
843 |
SQL DELETE: the table of the cascade node may change if there are
|
|
844 |
several child tables to the table where the delete is done! */
|
|
845 |
||
846 |
cascade = node->cascade_node; |
|
847 |
||
848 |
cascade->table = table; |
|
849 |
||
850 |
cascade->foreign = foreign; |
|
851 |
||
852 |
if (node->is_delete |
|
853 |
&& (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)) { |
|
854 |
cascade->is_delete = TRUE; |
|
855 |
} else { |
|
856 |
cascade->is_delete = FALSE; |
|
857 |
||
858 |
if (foreign->n_fields > cascade->update_n_fields) { |
|
859 |
/* We have to make the update vector longer */
|
|
860 |
||
861 |
cascade->update = upd_create(foreign->n_fields, |
|
862 |
node->cascade_heap); |
|
863 |
cascade->update_n_fields = foreign->n_fields; |
|
864 |
}
|
|
865 |
}
|
|
866 |
||
867 |
/* We do not allow cyclic cascaded updating (DELETE is allowed,
|
|
868 |
but not UPDATE) of the same table, as this can lead to an infinite
|
|
869 |
cycle. Check that we are not updating the same table which is
|
|
870 |
already being modified in this cascade chain. We have to check
|
|
871 |
this also because the modification of the indexes of a 'parent'
|
|
872 |
table may still be incomplete, and we must avoid seeing the indexes
|
|
873 |
of the parent table in an inconsistent state! */
|
|
874 |
||
875 |
if (!cascade->is_delete |
|
876 |
&& row_ins_cascade_ancestor_updates_table(cascade, table)) { |
|
877 |
||
878 |
/* We do not know if this would break foreign key
|
|
879 |
constraints, but play safe and return an error */
|
|
880 |
||
881 |
err = DB_ROW_IS_REFERENCED; |
|
882 |
||
883 |
row_ins_foreign_report_err( |
|
884 |
"Trying an update, possibly causing a cyclic"
|
|
885 |
" cascaded update\n" |
|
886 |
"in the child table,", thr, foreign, |
|
887 |
btr_pcur_get_rec(pcur), entry); |
|
888 |
||
889 |
goto nonstandard_exit_func; |
|
890 |
}
|
|
891 |
||
892 |
if (row_ins_cascade_n_ancestors(cascade) >= 15) { |
|
893 |
err = DB_ROW_IS_REFERENCED; |
|
894 |
||
895 |
row_ins_foreign_report_err( |
|
896 |
"Trying a too deep cascaded delete or update\n", |
|
897 |
thr, foreign, btr_pcur_get_rec(pcur), entry); |
|
898 |
||
899 |
goto nonstandard_exit_func; |
|
900 |
}
|
|
901 |
||
902 |
index = btr_pcur_get_btr_cur(pcur)->index; |
|
903 |
||
904 |
ut_a(index == foreign->foreign_index); |
|
905 |
||
906 |
rec = btr_pcur_get_rec(pcur); |
|
907 |
||
908 |
if (dict_index_is_clust(index)) { |
|
909 |
/* pcur is already positioned in the clustered index of
|
|
910 |
the child table */
|
|
911 |
||
912 |
clust_index = index; |
|
913 |
clust_rec = rec; |
|
914 |
clust_block = btr_pcur_get_block(pcur); |
|
915 |
} else { |
|
916 |
/* We have to look for the record in the clustered index
|
|
917 |
in the child table */
|
|
918 |
||
919 |
clust_index = dict_table_get_first_index(table); |
|
920 |
||
921 |
tmp_heap = mem_heap_create(256); |
|
922 |
||
923 |
ref = row_build_row_ref(ROW_COPY_POINTERS, index, rec, |
|
924 |
tmp_heap); |
|
925 |
btr_pcur_open_with_no_init(clust_index, ref, |
|
926 |
PAGE_CUR_LE, BTR_SEARCH_LEAF, |
|
927 |
cascade->pcur, 0, mtr); |
|
928 |
||
929 |
clust_rec = btr_pcur_get_rec(cascade->pcur); |
|
930 |
clust_block = btr_pcur_get_block(cascade->pcur); |
|
931 |
||
932 |
if (!page_rec_is_user_rec(clust_rec) |
|
933 |
|| btr_pcur_get_low_match(cascade->pcur) |
|
934 |
< dict_index_get_n_unique(clust_index)) { |
|
935 |
||
936 |
fputs("InnoDB: error in cascade of a foreign key op\n" |
|
937 |
"InnoDB: ", stderr); |
|
938 |
dict_index_name_print(stderr, trx, index); |
|
939 |
||
940 |
fputs("\n" |
|
941 |
"InnoDB: record ", stderr); |
|
942 |
rec_print(stderr, rec, index); |
|
943 |
fputs("\n" |
|
944 |
"InnoDB: clustered record ", stderr); |
|
945 |
rec_print(stderr, clust_rec, clust_index); |
|
946 |
fputs("\n" |
|
947 |
"InnoDB: Submit a detailed bug report to"
|
|
948 |
" http://bugs.mysql.com\n", stderr); |
|
949 |
||
950 |
err = DB_SUCCESS; |
|
951 |
||
952 |
goto nonstandard_exit_func; |
|
953 |
}
|
|
954 |
}
|
|
955 |
||
956 |
/* Set an X-lock on the row to delete or update in the child table */
|
|
957 |
||
958 |
err = lock_table(0, table, LOCK_IX, thr); |
|
959 |
||
960 |
if (err == DB_SUCCESS) { |
|
961 |
/* Here it suffices to use a LOCK_REC_NOT_GAP type lock;
|
|
962 |
we already have a normal shared lock on the appropriate
|
|
963 |
gap if the search criterion was not unique */
|
|
964 |
||
965 |
err = lock_clust_rec_read_check_and_lock_alt( |
|
966 |
0, clust_block, clust_rec, clust_index, |
|
967 |
LOCK_X, LOCK_REC_NOT_GAP, thr); |
|
968 |
}
|
|
969 |
||
970 |
if (err != DB_SUCCESS) { |
|
971 |
||
972 |
goto nonstandard_exit_func; |
|
973 |
}
|
|
974 |
||
975 |
if (rec_get_deleted_flag(clust_rec, dict_table_is_comp(table))) { |
|
976 |
/* This can happen if there is a circular reference of
|
|
977 |
rows such that cascading delete comes to delete a row
|
|
978 |
already in the process of being delete marked */
|
|
979 |
err = DB_SUCCESS; |
|
980 |
||
981 |
goto nonstandard_exit_func; |
|
982 |
}
|
|
983 |
||
984 |
if ((node->is_delete |
|
985 |
&& (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)) |
|
986 |
|| (!node->is_delete |
|
987 |
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL))) { |
|
988 |
||
989 |
/* Build the appropriate update vector which sets
|
|
990 |
foreign->n_fields first fields in rec to SQL NULL */
|
|
991 |
||
992 |
update = cascade->update; |
|
993 |
||
994 |
update->info_bits = 0; |
|
995 |
update->n_fields = foreign->n_fields; |
|
996 |
||
997 |
for (i = 0; i < foreign->n_fields; i++) { |
|
998 |
upd_field_t* ufield = &update->fields[i]; |
|
999 |
||
1000 |
ufield->field_no = dict_table_get_nth_col_pos( |
|
1001 |
table, |
|
1002 |
dict_index_get_nth_col_no(index, i)); |
|
1003 |
ufield->orig_len = 0; |
|
1004 |
ufield->exp = NULL; |
|
1005 |
dfield_set_null(&ufield->new_val); |
|
1006 |
}
|
|
1007 |
}
|
|
1008 |
||
1009 |
if (!node->is_delete |
|
1010 |
&& (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)) { |
|
1011 |
||
1012 |
/* Build the appropriate update vector which sets changing
|
|
1013 |
foreign->n_fields first fields in rec to new values */
|
|
1014 |
||
1015 |
upd_vec_heap = mem_heap_create(256); |
|
1016 |
||
1017 |
n_to_update = row_ins_cascade_calc_update_vec(node, foreign, |
|
1018 |
upd_vec_heap); |
|
1019 |
if (n_to_update == ULINT_UNDEFINED) { |
|
1020 |
err = DB_ROW_IS_REFERENCED; |
|
1021 |
||
1022 |
row_ins_foreign_report_err( |
|
1023 |
"Trying a cascaded update where the"
|
|
1024 |
" updated value in the child\n" |
|
1025 |
"table would not fit in the length"
|
|
1026 |
" of the column, or the value would\n" |
|
1027 |
"be NULL and the column is"
|
|
1028 |
" declared as not NULL in the child table,", |
|
1029 |
thr, foreign, btr_pcur_get_rec(pcur), entry); |
|
1030 |
||
1031 |
goto nonstandard_exit_func; |
|
1032 |
}
|
|
1033 |
||
1034 |
if (cascade->update->n_fields == 0) { |
|
1035 |
||
1036 |
/* The update does not change any columns referred
|
|
1037 |
to in this foreign key constraint: no need to do
|
|
1038 |
anything */
|
|
1039 |
||
1040 |
err = DB_SUCCESS; |
|
1041 |
||
1042 |
goto nonstandard_exit_func; |
|
1043 |
}
|
|
1044 |
}
|
|
1045 |
||
1046 |
/* Store pcur position and initialize or store the cascade node
|
|
1047 |
pcur stored position */
|
|
1048 |
||
1049 |
btr_pcur_store_position(pcur, mtr); |
|
1050 |
||
1051 |
if (index == clust_index) { |
|
1052 |
btr_pcur_copy_stored_position(cascade->pcur, pcur); |
|
1053 |
} else { |
|
1054 |
btr_pcur_store_position(cascade->pcur, mtr); |
|
1055 |
}
|
|
1056 |
||
1057 |
mtr_commit(mtr); |
|
1058 |
||
1059 |
ut_a(cascade->pcur->rel_pos == BTR_PCUR_ON); |
|
1060 |
||
1061 |
cascade->state = UPD_NODE_UPDATE_CLUSTERED; |
|
1062 |
||
1063 |
err = row_update_cascade_for_mysql(thr, cascade, |
|
1064 |
foreign->foreign_table); |
|
1065 |
||
1066 |
if (foreign->foreign_table->n_foreign_key_checks_running == 0) { |
|
1067 |
fprintf(stderr, |
|
1068 |
"InnoDB: error: table %s has the counter 0"
|
|
1069 |
" though there is\n" |
|
1070 |
"InnoDB: a FOREIGN KEY check running on it.\n", |
|
1071 |
foreign->foreign_table->name); |
|
1072 |
}
|
|
1073 |
||
1074 |
/* Release the data dictionary latch for a while, so that we do not
|
|
1075 |
starve other threads from doing CREATE TABLE etc. if we have a huge
|
|
1076 |
cascaded operation running. The counter n_foreign_key_checks_running
|
|
1077 |
will prevent other users from dropping or ALTERing the table when we
|
|
1078 |
release the latch. */
|
|
1079 |
||
1080 |
row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); |
|
1081 |
row_mysql_freeze_data_dictionary(thr_get_trx(thr)); |
|
1082 |
||
1083 |
mtr_start(mtr); |
|
1084 |
||
1085 |
/* Restore pcur position */
|
|
1086 |
||
1087 |
btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); |
|
1088 |
||
1089 |
if (tmp_heap) { |
|
1090 |
mem_heap_free(tmp_heap); |
|
1091 |
}
|
|
1092 |
||
1093 |
if (upd_vec_heap) { |
|
1094 |
mem_heap_free(upd_vec_heap); |
|
1095 |
}
|
|
1096 |
||
1097 |
return(err); |
|
1098 |
||
1099 |
nonstandard_exit_func: |
|
1100 |
if (tmp_heap) { |
|
1101 |
mem_heap_free(tmp_heap); |
|
1102 |
}
|
|
1103 |
||
1104 |
if (upd_vec_heap) { |
|
1105 |
mem_heap_free(upd_vec_heap); |
|
1106 |
}
|
|
1107 |
||
1108 |
btr_pcur_store_position(pcur, mtr); |
|
1109 |
||
1110 |
mtr_commit(mtr); |
|
1111 |
mtr_start(mtr); |
|
1112 |
||
1113 |
btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); |
|
1114 |
||
1115 |
return(err); |
|
1116 |
}
|
|
1117 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1118 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1119 |
Sets a shared lock on a record. Used in locking possible duplicate key
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1120 |
records and also in checking foreign key constraints.
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1121 |
@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1122 |
static
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1123 |
enum db_err |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1124 |
row_ins_set_shared_rec_lock( |
1125 |
/*========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1126 |
ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1127 |
LOCK_REC_NOT_GAP type lock */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1128 |
const buf_block_t* block, /*!< in: buffer block of rec */ |
1129 |
const rec_t* rec, /*!< in: record */ |
|
1130 |
dict_index_t* index, /*!< in: index */ |
|
1131 |
const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ |
|
1132 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1133 |
{
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1134 |
enum db_err err; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1135 |
|
1136 |
ut_ad(rec_offs_validate(rec, index, offsets)); |
|
1137 |
||
1138 |
if (dict_index_is_clust(index)) { |
|
1139 |
err = lock_clust_rec_read_check_and_lock( |
|
1140 |
0, block, rec, index, offsets, LOCK_S, type, thr); |
|
1141 |
} else { |
|
1142 |
err = lock_sec_rec_read_check_and_lock( |
|
1143 |
0, block, rec, index, offsets, LOCK_S, type, thr); |
|
1144 |
}
|
|
1145 |
||
1146 |
return(err); |
|
1147 |
}
|
|
1148 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1149 |
/*********************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1150 |
Sets a exclusive lock on a record. Used in locking possible duplicate key
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1151 |
records
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1152 |
@return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, or error code */
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1153 |
static
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1154 |
enum db_err |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1155 |
row_ins_set_exclusive_rec_lock( |
1156 |
/*===========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1157 |
ulint type, /*!< in: LOCK_ORDINARY, LOCK_GAP, or |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1158 |
LOCK_REC_NOT_GAP type lock */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1159 |
const buf_block_t* block, /*!< in: buffer block of rec */ |
1160 |
const rec_t* rec, /*!< in: record */ |
|
1161 |
dict_index_t* index, /*!< in: index */ |
|
1162 |
const ulint* offsets,/*!< in: rec_get_offsets(rec, index) */ |
|
1163 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1164 |
{
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1165 |
enum db_err err; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1166 |
|
1167 |
ut_ad(rec_offs_validate(rec, index, offsets)); |
|
1168 |
||
1169 |
if (dict_index_is_clust(index)) { |
|
1170 |
err = lock_clust_rec_read_check_and_lock( |
|
1171 |
0, block, rec, index, offsets, LOCK_X, type, thr); |
|
1172 |
} else { |
|
1173 |
err = lock_sec_rec_read_check_and_lock( |
|
1174 |
0, block, rec, index, offsets, LOCK_X, type, thr); |
|
1175 |
}
|
|
1176 |
||
1177 |
return(err); |
|
1178 |
}
|
|
1179 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1180 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1181 |
Checks if foreign key constraint fails for an index entry. Sets shared locks
|
1182 |
which lock either the success or the failure of the constraint. NOTE that
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1183 |
the caller must have a shared latch on dict_operation_lock.
|
1184 |
@return DB_SUCCESS, DB_NO_REFERENCED_ROW, or DB_ROW_IS_REFERENCED */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1185 |
UNIV_INTERN
|
1186 |
ulint
|
|
1187 |
row_ins_check_foreign_constraint( |
|
1188 |
/*=============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1189 |
ibool check_ref,/*!< in: TRUE if we want to check that |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1190 |
the referenced table is ok, FALSE if we
|
1819.5.85
by stewart at flamingspork
[patch 085/129] Merge patch for revision 1884 from InnoDB SVN: |
1191 |
want to check the foreign key table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1192 |
dict_foreign_t* foreign,/*!< in: foreign constraint; NOTE that the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1193 |
tables mentioned in it must be in the
|
1194 |
dictionary cache if they exist at all */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1195 |
dict_table_t* table, /*!< in: if check_ref is TRUE, then the foreign |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1196 |
table, else the referenced table */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1197 |
dtuple_t* entry, /*!< in: index entry for index */ |
1198 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1199 |
{
|
1200 |
upd_node_t* upd_node; |
|
1201 |
dict_table_t* check_table; |
|
1202 |
dict_index_t* check_index; |
|
1203 |
ulint n_fields_cmp; |
|
1204 |
btr_pcur_t pcur; |
|
1205 |
int cmp; |
|
1206 |
ulint err; |
|
1207 |
ulint i; |
|
1208 |
mtr_t mtr; |
|
1209 |
trx_t* trx = thr_get_trx(thr); |
|
1210 |
mem_heap_t* heap = NULL; |
|
1211 |
ulint offsets_[REC_OFFS_NORMAL_SIZE]; |
|
1212 |
ulint* offsets = offsets_; |
|
1213 |
rec_offs_init(offsets_); |
|
1214 |
||
1215 |
run_again: |
|
1216 |
#ifdef UNIV_SYNC_DEBUG
|
|
1217 |
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); |
|
1218 |
#endif /* UNIV_SYNC_DEBUG */ |
|
1219 |
||
1220 |
err = DB_SUCCESS; |
|
1221 |
||
1222 |
if (trx->check_foreigns == FALSE) { |
|
1223 |
/* The user has suppressed foreign key checks currently for
|
|
1224 |
this session */
|
|
1225 |
goto exit_func; |
|
1226 |
}
|
|
1227 |
||
1228 |
/* If any of the foreign key fields in entry is SQL NULL, we
|
|
1229 |
suppress the foreign key check: this is compatible with Oracle,
|
|
1230 |
for example */
|
|
1231 |
||
1232 |
for (i = 0; i < foreign->n_fields; i++) { |
|
1233 |
if (UNIV_SQL_NULL == dfield_get_len( |
|
1234 |
dtuple_get_nth_field(entry, i))) { |
|
1235 |
||
1236 |
goto exit_func; |
|
1237 |
}
|
|
1238 |
}
|
|
1239 |
||
1240 |
if (que_node_get_type(thr->run_node) == QUE_NODE_UPDATE) { |
|
2023.3.13
by Monty Taylor
More. |
1241 |
upd_node = static_cast<upd_node_t *>(thr->run_node); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1242 |
|
1243 |
if (!(upd_node->is_delete) && upd_node->foreign == foreign) { |
|
1244 |
/* If a cascaded update is done as defined by a
|
|
1245 |
foreign key constraint, do not check that
|
|
1246 |
constraint for the child row. In ON UPDATE CASCADE
|
|
1247 |
the update of the parent row is only half done when
|
|
1248 |
we come here: if we would check the constraint here
|
|
1249 |
for the child row it would fail.
|
|
1250 |
||
1251 |
A QUESTION remains: if in the child table there are
|
|
1252 |
several constraints which refer to the same parent
|
|
1253 |
table, we should merge all updates to the child as
|
|
1254 |
one update? And the updates can be contradictory!
|
|
1255 |
Currently we just perform the update associated
|
|
1256 |
with each foreign key constraint, one after
|
|
1257 |
another, and the user has problems predicting in
|
|
1258 |
which order they are performed. */
|
|
1259 |
||
1260 |
goto exit_func; |
|
1261 |
}
|
|
1262 |
}
|
|
1263 |
||
1264 |
if (check_ref) { |
|
1265 |
check_table = foreign->referenced_table; |
|
1266 |
check_index = foreign->referenced_index; |
|
1267 |
} else { |
|
1268 |
check_table = foreign->foreign_table; |
|
1269 |
check_index = foreign->foreign_index; |
|
1270 |
}
|
|
1271 |
||
1272 |
if (check_table == NULL || check_table->ibd_file_missing) { |
|
1273 |
if (check_ref) { |
|
1274 |
FILE* ef = dict_foreign_err_file; |
|
1275 |
||
1276 |
row_ins_set_detailed(trx, foreign); |
|
1277 |
||
1278 |
mutex_enter(&dict_foreign_err_mutex); |
|
1279 |
rewind(ef); |
|
1280 |
ut_print_timestamp(ef); |
|
1281 |
fputs(" Transaction:\n", ef); |
|
1282 |
trx_print(ef, trx, 600); |
|
1283 |
fputs("Foreign key constraint fails for table ", ef); |
|
1284 |
ut_print_name(ef, trx, TRUE, |
|
1285 |
foreign->foreign_table_name); |
|
1286 |
fputs(":\n", ef); |
|
1287 |
dict_print_info_on_foreign_key_in_create_format( |
|
1288 |
ef, trx, foreign, TRUE); |
|
1289 |
fputs("\nTrying to add to index ", ef); |
|
1290 |
ut_print_name(ef, trx, FALSE, |
|
1291 |
foreign->foreign_index->name); |
|
1292 |
fputs(" tuple:\n", ef); |
|
1293 |
dtuple_print(ef, entry); |
|
1294 |
fputs("\nBut the parent table ", ef); |
|
1295 |
ut_print_name(ef, trx, TRUE, |
|
1296 |
foreign->referenced_table_name); |
|
1297 |
fputs("\nor its .ibd file does" |
|
1298 |
" not currently exist!\n", ef); |
|
1299 |
mutex_exit(&dict_foreign_err_mutex); |
|
1300 |
||
1301 |
err = DB_NO_REFERENCED_ROW; |
|
1302 |
}
|
|
1303 |
||
1304 |
goto exit_func; |
|
1305 |
}
|
|
1306 |
||
1307 |
ut_a(check_table); |
|
1308 |
ut_a(check_index); |
|
1309 |
||
1310 |
if (check_table != table) { |
|
1311 |
/* We already have a LOCK_IX on table, but not necessarily
|
|
1312 |
on check_table */
|
|
1313 |
||
1314 |
err = lock_table(0, check_table, LOCK_IS, thr); |
|
1315 |
||
1316 |
if (err != DB_SUCCESS) { |
|
1317 |
||
1318 |
goto do_possible_lock_wait; |
|
1319 |
}
|
|
1320 |
}
|
|
1321 |
||
1322 |
mtr_start(&mtr); |
|
1323 |
||
1324 |
/* Store old value on n_fields_cmp */
|
|
1325 |
||
1326 |
n_fields_cmp = dtuple_get_n_fields_cmp(entry); |
|
1327 |
||
1328 |
dtuple_set_n_fields_cmp(entry, foreign->n_fields); |
|
1329 |
||
1330 |
btr_pcur_open(check_index, entry, PAGE_CUR_GE, |
|
1331 |
BTR_SEARCH_LEAF, &pcur, &mtr); |
|
1332 |
||
1333 |
/* Scan index records and check if there is a matching record */
|
|
1334 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1335 |
do { |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1336 |
const rec_t* rec = btr_pcur_get_rec(&pcur); |
1337 |
const buf_block_t* block = btr_pcur_get_block(&pcur); |
|
1338 |
||
1339 |
if (page_rec_is_infimum(rec)) { |
|
1340 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1341 |
continue; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1342 |
}
|
1343 |
||
1344 |
offsets = rec_get_offsets(rec, check_index, |
|
1345 |
offsets, ULINT_UNDEFINED, &heap); |
|
1346 |
||
1347 |
if (page_rec_is_supremum(rec)) { |
|
1348 |
||
1349 |
err = row_ins_set_shared_rec_lock(LOCK_ORDINARY, block, |
|
1350 |
rec, check_index, |
|
1351 |
offsets, thr); |
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1352 |
switch (err) { |
1353 |
case DB_SUCCESS_LOCKED_REC: |
|
1354 |
case DB_SUCCESS: |
|
1355 |
continue; |
|
1356 |
default: |
|
1357 |
goto end_scan; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1358 |
}
|
1359 |
}
|
|
1360 |
||
1361 |
cmp = cmp_dtuple_rec(entry, rec, offsets); |
|
1362 |
||
1363 |
if (cmp == 0) { |
|
1364 |
if (rec_get_deleted_flag(rec, |
|
1365 |
rec_offs_comp(offsets))) { |
|
1366 |
err = row_ins_set_shared_rec_lock( |
|
1367 |
LOCK_ORDINARY, block, |
|
1368 |
rec, check_index, offsets, thr); |
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1369 |
switch (err) { |
1370 |
case DB_SUCCESS_LOCKED_REC: |
|
1371 |
case DB_SUCCESS: |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1372 |
break; |
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1373 |
default: |
1374 |
goto end_scan; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1375 |
}
|
1376 |
} else { |
|
1377 |
/* Found a matching record. Lock only
|
|
1378 |
a record because we can allow inserts
|
|
1379 |
into gaps */
|
|
1380 |
||
1381 |
err = row_ins_set_shared_rec_lock( |
|
1382 |
LOCK_REC_NOT_GAP, block, |
|
1383 |
rec, check_index, offsets, thr); |
|
1384 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1385 |
switch (err) { |
1386 |
case DB_SUCCESS_LOCKED_REC: |
|
1387 |
case DB_SUCCESS: |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1388 |
break; |
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1389 |
default: |
1390 |
goto end_scan; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1391 |
}
|
1392 |
||
1393 |
if (check_ref) { |
|
1394 |
err = DB_SUCCESS; |
|
1395 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1396 |
goto end_scan; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1397 |
} else if (foreign->type != 0) { |
1398 |
/* There is an ON UPDATE or ON DELETE
|
|
1399 |
condition: check them in a separate
|
|
1400 |
function */
|
|
1401 |
||
1402 |
err = row_ins_foreign_check_on_constraint( |
|
1403 |
thr, foreign, &pcur, entry, |
|
1404 |
&mtr); |
|
1405 |
if (err != DB_SUCCESS) { |
|
1406 |
/* Since reporting a plain
|
|
1407 |
"duplicate key" error
|
|
1408 |
message to the user in
|
|
1409 |
cases where a long CASCADE
|
|
1410 |
operation would lead to a
|
|
1411 |
duplicate key in some
|
|
1412 |
other table is very
|
|
1413 |
confusing, map duplicate
|
|
1414 |
key errors resulting from
|
|
1415 |
FK constraints to a
|
|
1416 |
separate error code. */
|
|
1417 |
||
1418 |
if (err == DB_DUPLICATE_KEY) { |
|
1419 |
err = DB_FOREIGN_DUPLICATE_KEY; |
|
1420 |
}
|
|
1421 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1422 |
goto end_scan; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1423 |
}
|
1424 |
||
1425 |
/* row_ins_foreign_check_on_constraint
|
|
1426 |
may have repositioned pcur on a
|
|
1427 |
different block */
|
|
1428 |
block = btr_pcur_get_block(&pcur); |
|
1429 |
} else { |
|
1430 |
row_ins_foreign_report_err( |
|
1431 |
"Trying to delete or update", |
|
1432 |
thr, foreign, rec, entry); |
|
1433 |
||
1434 |
err = DB_ROW_IS_REFERENCED; |
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1435 |
goto end_scan; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1436 |
}
|
1437 |
}
|
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1438 |
} else { |
1439 |
ut_a(cmp < 0); |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1440 |
|
1441 |
err = row_ins_set_shared_rec_lock( |
|
1442 |
LOCK_GAP, block, |
|
1443 |
rec, check_index, offsets, thr); |
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1444 |
|
1445 |
switch (err) { |
|
1446 |
case DB_SUCCESS_LOCKED_REC: |
|
1447 |
case DB_SUCCESS: |
|
1448 |
if (check_ref) { |
|
1449 |
err = DB_NO_REFERENCED_ROW; |
|
1450 |
row_ins_foreign_report_add_err( |
|
1451 |
trx, foreign, rec, entry); |
|
1452 |
} else { |
|
1453 |
err = DB_SUCCESS; |
|
1454 |
}
|
|
1455 |
}
|
|
1456 |
||
1457 |
goto end_scan; |
|
1458 |
}
|
|
1459 |
} while (btr_pcur_move_to_next(&pcur, &mtr)); |
|
1460 |
||
1461 |
if (check_ref) { |
|
1462 |
row_ins_foreign_report_add_err( |
|
1463 |
trx, foreign, btr_pcur_get_rec(&pcur), entry); |
|
1464 |
err = DB_NO_REFERENCED_ROW; |
|
1465 |
} else { |
|
1466 |
err = DB_SUCCESS; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1467 |
}
|
1468 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1469 |
end_scan: |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1470 |
btr_pcur_close(&pcur); |
1471 |
||
1472 |
mtr_commit(&mtr); |
|
1473 |
||
1474 |
/* Restore old value */
|
|
1475 |
dtuple_set_n_fields_cmp(entry, n_fields_cmp); |
|
1476 |
||
1477 |
do_possible_lock_wait: |
|
1478 |
if (err == DB_LOCK_WAIT) { |
|
1479 |
trx->error_state = err; |
|
1480 |
||
1481 |
que_thr_stop_for_mysql(thr); |
|
1482 |
||
1483 |
srv_suspend_mysql_thread(thr); |
|
1484 |
||
1485 |
if (trx->error_state == DB_SUCCESS) { |
|
1486 |
||
1487 |
goto run_again; |
|
1488 |
}
|
|
1489 |
||
1490 |
err = trx->error_state; |
|
1491 |
}
|
|
1492 |
||
1493 |
exit_func: |
|
1494 |
if (UNIV_LIKELY_NULL(heap)) { |
|
1495 |
mem_heap_free(heap); |
|
1496 |
}
|
|
1497 |
return(err); |
|
1498 |
}
|
|
1499 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1500 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1501 |
Checks if foreign key constraints fail for an index entry. If index
|
1502 |
is not mentioned in any constraint, this function does nothing,
|
|
1503 |
Otherwise does searches to the indexes of referenced tables and
|
|
1504 |
sets shared locks which lock either the success or the failure of
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1505 |
a constraint.
|
1506 |
@return DB_SUCCESS or error code */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1507 |
static
|
1508 |
ulint
|
|
1509 |
row_ins_check_foreign_constraints( |
|
1510 |
/*==============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1511 |
dict_table_t* table, /*!< in: table */ |
1512 |
dict_index_t* index, /*!< in: index */ |
|
1513 |
dtuple_t* entry, /*!< in: index entry for index */ |
|
1514 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1515 |
{
|
1516 |
dict_foreign_t* foreign; |
|
1517 |
ulint err; |
|
1518 |
trx_t* trx; |
|
1519 |
ibool got_s_lock = FALSE; |
|
1520 |
||
1521 |
trx = thr_get_trx(thr); |
|
1522 |
||
1523 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
1524 |
||
1525 |
while (foreign) { |
|
1526 |
if (foreign->foreign_index == index) { |
|
1527 |
||
1528 |
if (foreign->referenced_table == NULL) { |
|
1529 |
dict_table_get(foreign->referenced_table_name, |
|
1530 |
FALSE); |
|
1531 |
}
|
|
1532 |
||
1533 |
if (0 == trx->dict_operation_lock_mode) { |
|
1534 |
got_s_lock = TRUE; |
|
1535 |
||
1536 |
row_mysql_freeze_data_dictionary(trx); |
|
1537 |
}
|
|
1538 |
||
1539 |
if (foreign->referenced_table) { |
|
1540 |
mutex_enter(&(dict_sys->mutex)); |
|
1541 |
||
1542 |
(foreign->referenced_table |
|
1543 |
->n_foreign_key_checks_running)++; |
|
1544 |
||
1545 |
mutex_exit(&(dict_sys->mutex)); |
|
1546 |
}
|
|
1547 |
||
1548 |
/* NOTE that if the thread ends up waiting for a lock
|
|
1549 |
we will release dict_operation_lock temporarily!
|
|
1550 |
But the counter on the table protects the referenced
|
|
1551 |
table from being dropped while the check is running. */
|
|
1552 |
||
1553 |
err = row_ins_check_foreign_constraint( |
|
1554 |
TRUE, foreign, table, entry, thr); |
|
1555 |
||
1556 |
if (foreign->referenced_table) { |
|
1557 |
mutex_enter(&(dict_sys->mutex)); |
|
1558 |
||
1559 |
ut_a(foreign->referenced_table |
|
1560 |
->n_foreign_key_checks_running > 0); |
|
1561 |
(foreign->referenced_table |
|
1562 |
->n_foreign_key_checks_running)--; |
|
1563 |
||
1564 |
mutex_exit(&(dict_sys->mutex)); |
|
1565 |
}
|
|
1566 |
||
1567 |
if (got_s_lock) { |
|
1568 |
row_mysql_unfreeze_data_dictionary(trx); |
|
1569 |
}
|
|
1570 |
||
1571 |
if (err != DB_SUCCESS) { |
|
1572 |
return(err); |
|
1573 |
}
|
|
1574 |
}
|
|
1575 |
||
1576 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
1577 |
}
|
|
1578 |
||
1579 |
return(DB_SUCCESS); |
|
1580 |
}
|
|
1581 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1582 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1583 |
Checks if a unique key violation to rec would occur at the index entry
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1584 |
insert.
|
1585 |
@return TRUE if error */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1586 |
static
|
1587 |
ibool
|
|
1588 |
row_ins_dupl_error_with_rec( |
|
1589 |
/*========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1590 |
const rec_t* rec, /*!< in: user record; NOTE that we assume |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1591 |
that the caller already has a record lock on
|
1592 |
the record! */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1593 |
const dtuple_t* entry, /*!< in: entry to insert */ |
1594 |
dict_index_t* index, /*!< in: index */ |
|
1595 |
const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1596 |
{
|
1597 |
ulint matched_fields; |
|
1598 |
ulint matched_bytes; |
|
1599 |
ulint n_unique; |
|
1600 |
ulint i; |
|
1601 |
||
1602 |
ut_ad(rec_offs_validate(rec, index, offsets)); |
|
1603 |
||
1604 |
n_unique = dict_index_get_n_unique(index); |
|
1605 |
||
1606 |
matched_fields = 0; |
|
1607 |
matched_bytes = 0; |
|
1608 |
||
1609 |
cmp_dtuple_rec_with_match(entry, rec, offsets, |
|
1610 |
&matched_fields, &matched_bytes); |
|
1611 |
||
1612 |
if (matched_fields < n_unique) { |
|
1613 |
||
1614 |
return(FALSE); |
|
1615 |
}
|
|
1616 |
||
1617 |
/* In a unique secondary index we allow equal key values if they
|
|
1618 |
contain SQL NULLs */
|
|
1619 |
||
1620 |
if (!dict_index_is_clust(index)) { |
|
1621 |
||
1622 |
for (i = 0; i < n_unique; i++) { |
|
1623 |
if (UNIV_SQL_NULL == dfield_get_len( |
|
1624 |
dtuple_get_nth_field(entry, i))) { |
|
1625 |
||
1626 |
return(FALSE); |
|
1627 |
}
|
|
1628 |
}
|
|
1629 |
}
|
|
1630 |
||
1631 |
return(!rec_get_deleted_flag(rec, rec_offs_comp(offsets))); |
|
1632 |
}
|
|
1633 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1634 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1635 |
Scans a unique non-clustered index at a given index entry to determine
|
1636 |
whether a uniqueness violation has occurred for the key value of the entry.
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1637 |
Set shared locks on possible duplicate records.
|
1638 |
@return DB_SUCCESS, DB_DUPLICATE_KEY, or DB_LOCK_WAIT */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1639 |
static
|
1640 |
ulint
|
|
1641 |
row_ins_scan_sec_index_for_duplicate( |
|
1642 |
/*=================================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1643 |
dict_index_t* index, /*!< in: non-clustered unique index */ |
1644 |
dtuple_t* entry, /*!< in: index entry */ |
|
1645 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1646 |
{
|
1647 |
ulint n_unique; |
|
1648 |
ulint i; |
|
1649 |
int cmp; |
|
1650 |
ulint n_fields_cmp; |
|
1651 |
btr_pcur_t pcur; |
|
1652 |
ulint err = DB_SUCCESS; |
|
1653 |
unsigned allow_duplicates; |
|
1654 |
mtr_t mtr; |
|
1655 |
mem_heap_t* heap = NULL; |
|
1656 |
ulint offsets_[REC_OFFS_NORMAL_SIZE]; |
|
1657 |
ulint* offsets = offsets_; |
|
1658 |
rec_offs_init(offsets_); |
|
1659 |
||
1660 |
n_unique = dict_index_get_n_unique(index); |
|
1661 |
||
1662 |
/* If the secondary index is unique, but one of the fields in the
|
|
1663 |
n_unique first fields is NULL, a unique key violation cannot occur,
|
|
1664 |
since we define NULL != NULL in this case */
|
|
1665 |
||
1666 |
for (i = 0; i < n_unique; i++) { |
|
1667 |
if (UNIV_SQL_NULL == dfield_get_len( |
|
1668 |
dtuple_get_nth_field(entry, i))) { |
|
1669 |
||
1670 |
return(DB_SUCCESS); |
|
1671 |
}
|
|
1672 |
}
|
|
1673 |
||
1674 |
mtr_start(&mtr); |
|
1675 |
||
1676 |
/* Store old value on n_fields_cmp */
|
|
1677 |
||
1678 |
n_fields_cmp = dtuple_get_n_fields_cmp(entry); |
|
1679 |
||
1680 |
dtuple_set_n_fields_cmp(entry, dict_index_get_n_unique(index)); |
|
1681 |
||
1682 |
btr_pcur_open(index, entry, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, &mtr); |
|
1683 |
||
1684 |
allow_duplicates = thr_get_trx(thr)->duplicates & TRX_DUP_IGNORE; |
|
1685 |
||
1686 |
/* Scan index records and check if there is a duplicate */
|
|
1687 |
||
1688 |
do { |
|
1689 |
const rec_t* rec = btr_pcur_get_rec(&pcur); |
|
1690 |
const buf_block_t* block = btr_pcur_get_block(&pcur); |
|
1691 |
||
1692 |
if (page_rec_is_infimum(rec)) { |
|
1693 |
||
1694 |
continue; |
|
1695 |
}
|
|
1696 |
||
1697 |
offsets = rec_get_offsets(rec, index, offsets, |
|
1698 |
ULINT_UNDEFINED, &heap); |
|
1699 |
||
1700 |
if (allow_duplicates) { |
|
1701 |
||
1702 |
/* If the SQL-query will update or replace
|
|
1703 |
duplicate key we will take X-lock for
|
|
1704 |
duplicates ( REPLACE, LOAD DATAFILE REPLACE,
|
|
1705 |
INSERT ON DUPLICATE KEY UPDATE). */
|
|
1706 |
||
1707 |
err = row_ins_set_exclusive_rec_lock( |
|
1708 |
LOCK_ORDINARY, block, |
|
1709 |
rec, index, offsets, thr); |
|
1710 |
} else { |
|
1711 |
||
1712 |
err = row_ins_set_shared_rec_lock( |
|
1713 |
LOCK_ORDINARY, block, |
|
1714 |
rec, index, offsets, thr); |
|
1715 |
}
|
|
1716 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1717 |
switch (err) { |
1718 |
case DB_SUCCESS_LOCKED_REC: |
|
1719 |
err = DB_SUCCESS; |
|
1720 |
case DB_SUCCESS: |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1721 |
break; |
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1722 |
default: |
1723 |
goto end_scan; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1724 |
}
|
1725 |
||
1726 |
if (page_rec_is_supremum(rec)) { |
|
1727 |
||
1728 |
continue; |
|
1729 |
}
|
|
1730 |
||
1731 |
cmp = cmp_dtuple_rec(entry, rec, offsets); |
|
1732 |
||
1733 |
if (cmp == 0) { |
|
1734 |
if (row_ins_dupl_error_with_rec(rec, entry, |
|
1735 |
index, offsets)) { |
|
1736 |
err = DB_DUPLICATE_KEY; |
|
1737 |
||
1738 |
thr_get_trx(thr)->error_info = index; |
|
1739 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1740 |
goto end_scan; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1741 |
}
|
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1742 |
} else { |
1743 |
ut_a(cmp < 0); |
|
1744 |
goto end_scan; |
|
1745 |
}
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1746 |
} while (btr_pcur_move_to_next(&pcur, &mtr)); |
1747 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1748 |
end_scan: |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1749 |
if (UNIV_LIKELY_NULL(heap)) { |
1750 |
mem_heap_free(heap); |
|
1751 |
}
|
|
1752 |
mtr_commit(&mtr); |
|
1753 |
||
1754 |
/* Restore old value */
|
|
1755 |
dtuple_set_n_fields_cmp(entry, n_fields_cmp); |
|
1756 |
||
1757 |
return(err); |
|
1758 |
}
|
|
1759 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1760 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1761 |
Checks if a unique key violation error would occur at an index entry
|
1762 |
insert. Sets shared locks on possible duplicate records. Works only
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1763 |
for a clustered index!
|
1764 |
@return DB_SUCCESS if no error, DB_DUPLICATE_KEY if error,
|
|
1765 |
DB_LOCK_WAIT if we have to wait for a lock on a possible duplicate
|
|
1766 |
record */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1767 |
static
|
1768 |
ulint
|
|
1769 |
row_ins_duplicate_error_in_clust( |
|
1770 |
/*=============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1771 |
btr_cur_t* cursor, /*!< in: B-tree cursor */ |
1819.9.197
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20101103091611-a3x9p0yivkvu5u9i from MySQL InnoDB |
1772 |
const dtuple_t* entry, /*!< in: entry to insert */ |
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1773 |
que_thr_t* thr, /*!< in: query thread */ |
1774 |
mtr_t* mtr) /*!< in: mtr */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1775 |
{
|
1776 |
ulint err; |
|
1777 |
rec_t* rec; |
|
1778 |
ulint n_unique; |
|
1779 |
trx_t* trx = thr_get_trx(thr); |
|
1780 |
mem_heap_t*heap = NULL; |
|
1781 |
ulint offsets_[REC_OFFS_NORMAL_SIZE]; |
|
1782 |
ulint* offsets = offsets_; |
|
1783 |
rec_offs_init(offsets_); |
|
1784 |
||
1785 |
UT_NOT_USED(mtr); |
|
1786 |
||
1787 |
ut_a(dict_index_is_clust(cursor->index)); |
|
1788 |
ut_ad(dict_index_is_unique(cursor->index)); |
|
1789 |
||
1790 |
/* NOTE: For unique non-clustered indexes there may be any number
|
|
1791 |
of delete marked records with the same value for the non-clustered
|
|
1792 |
index key (remember multiversioning), and which differ only in
|
|
1793 |
the row refererence part of the index record, containing the
|
|
1794 |
clustered index key fields. For such a secondary index record,
|
|
1795 |
to avoid race condition, we must FIRST do the insertion and after
|
|
1796 |
that check that the uniqueness condition is not breached! */
|
|
1797 |
||
1798 |
/* NOTE: A problem is that in the B-tree node pointers on an
|
|
1799 |
upper level may match more to the entry than the actual existing
|
|
1800 |
user records on the leaf level. So, even if low_match would suggest
|
|
1801 |
that a duplicate key violation may occur, this may not be the case. */
|
|
1802 |
||
1803 |
n_unique = dict_index_get_n_unique(cursor->index); |
|
1804 |
||
1805 |
if (cursor->low_match >= n_unique) { |
|
1806 |
||
1807 |
rec = btr_cur_get_rec(cursor); |
|
1808 |
||
1809 |
if (!page_rec_is_infimum(rec)) { |
|
1810 |
offsets = rec_get_offsets(rec, cursor->index, offsets, |
|
1811 |
ULINT_UNDEFINED, &heap); |
|
1812 |
||
1813 |
/* We set a lock on the possible duplicate: this
|
|
1814 |
is needed in logical logging of MySQL to make
|
|
1815 |
sure that in roll-forward we get the same duplicate
|
|
1816 |
errors as in original execution */
|
|
1817 |
||
1818 |
if (trx->duplicates & TRX_DUP_IGNORE) { |
|
1819 |
||
1820 |
/* If the SQL-query will update or replace
|
|
1821 |
duplicate key we will take X-lock for
|
|
1822 |
duplicates ( REPLACE, LOAD DATAFILE REPLACE,
|
|
1823 |
INSERT ON DUPLICATE KEY UPDATE). */
|
|
1824 |
||
1825 |
err = row_ins_set_exclusive_rec_lock( |
|
1826 |
LOCK_REC_NOT_GAP, |
|
1827 |
btr_cur_get_block(cursor), |
|
1828 |
rec, cursor->index, offsets, thr); |
|
1829 |
} else { |
|
1830 |
||
1831 |
err = row_ins_set_shared_rec_lock( |
|
1832 |
LOCK_REC_NOT_GAP, |
|
1833 |
btr_cur_get_block(cursor), rec, |
|
1834 |
cursor->index, offsets, thr); |
|
1835 |
}
|
|
1836 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1837 |
switch (err) { |
1838 |
case DB_SUCCESS_LOCKED_REC: |
|
1839 |
case DB_SUCCESS: |
|
1840 |
break; |
|
1841 |
default: |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1842 |
goto func_exit; |
1843 |
}
|
|
1844 |
||
1845 |
if (row_ins_dupl_error_with_rec( |
|
1846 |
rec, entry, cursor->index, offsets)) { |
|
1847 |
trx->error_info = cursor->index; |
|
1848 |
err = DB_DUPLICATE_KEY; |
|
1849 |
goto func_exit; |
|
1850 |
}
|
|
1851 |
}
|
|
1852 |
}
|
|
1853 |
||
1854 |
if (cursor->up_match >= n_unique) { |
|
1855 |
||
1856 |
rec = page_rec_get_next(btr_cur_get_rec(cursor)); |
|
1857 |
||
1858 |
if (!page_rec_is_supremum(rec)) { |
|
1859 |
offsets = rec_get_offsets(rec, cursor->index, offsets, |
|
1860 |
ULINT_UNDEFINED, &heap); |
|
1861 |
||
1862 |
if (trx->duplicates & TRX_DUP_IGNORE) { |
|
1863 |
||
1864 |
/* If the SQL-query will update or replace
|
|
1865 |
duplicate key we will take X-lock for
|
|
1866 |
duplicates ( REPLACE, LOAD DATAFILE REPLACE,
|
|
1867 |
INSERT ON DUPLICATE KEY UPDATE). */
|
|
1868 |
||
1869 |
err = row_ins_set_exclusive_rec_lock( |
|
1870 |
LOCK_REC_NOT_GAP, |
|
1871 |
btr_cur_get_block(cursor), |
|
1872 |
rec, cursor->index, offsets, thr); |
|
1873 |
} else { |
|
1874 |
||
1875 |
err = row_ins_set_shared_rec_lock( |
|
1876 |
LOCK_REC_NOT_GAP, |
|
1877 |
btr_cur_get_block(cursor), |
|
1878 |
rec, cursor->index, offsets, thr); |
|
1879 |
}
|
|
1880 |
||
1819.7.159
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602113733-fslfv73nhi0d17t4 from MySQL InnoDB |
1881 |
switch (err) { |
1882 |
case DB_SUCCESS_LOCKED_REC: |
|
1883 |
case DB_SUCCESS: |
|
1884 |
break; |
|
1885 |
default: |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1886 |
goto func_exit; |
1887 |
}
|
|
1888 |
||
1889 |
if (row_ins_dupl_error_with_rec( |
|
1890 |
rec, entry, cursor->index, offsets)) { |
|
1891 |
trx->error_info = cursor->index; |
|
1892 |
err = DB_DUPLICATE_KEY; |
|
1893 |
goto func_exit; |
|
1894 |
}
|
|
1895 |
}
|
|
1896 |
||
1897 |
ut_a(!dict_index_is_clust(cursor->index)); |
|
1898 |
/* This should never happen */
|
|
1899 |
}
|
|
1900 |
||
1901 |
err = DB_SUCCESS; |
|
1902 |
func_exit: |
|
1903 |
if (UNIV_LIKELY_NULL(heap)) { |
|
1904 |
mem_heap_free(heap); |
|
1905 |
}
|
|
1906 |
return(err); |
|
1907 |
}
|
|
1908 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1909 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1910 |
Checks if an index entry has long enough common prefix with an existing
|
1911 |
record so that the intended insert of the entry must be changed to a modify of
|
|
1912 |
the existing record. In the case of a clustered index, the prefix must be
|
|
1913 |
n_unique fields long, and in the case of a secondary index, all fields must be
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1914 |
equal.
|
1915 |
@return 0 if no update, ROW_INS_PREV if previous should be updated;
|
|
1916 |
currently we do the search so that only the low_match record can match
|
|
1917 |
enough to the search tuple, not the next record */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1918 |
UNIV_INLINE
|
1919 |
ulint
|
|
1920 |
row_ins_must_modify( |
|
1921 |
/*================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1922 |
btr_cur_t* cursor) /*!< in: B-tree cursor */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1923 |
{
|
1924 |
ulint enough_match; |
|
1925 |
rec_t* rec; |
|
1926 |
||
1927 |
/* NOTE: (compare to the note in row_ins_duplicate_error) Because node
|
|
1928 |
pointers on upper levels of the B-tree may match more to entry than
|
|
1929 |
to actual user records on the leaf level, we have to check if the
|
|
1930 |
candidate record is actually a user record. In a clustered index
|
|
1931 |
node pointers contain index->n_unique first fields, and in the case
|
|
1932 |
of a secondary index, all fields of the index. */
|
|
1933 |
||
1934 |
enough_match = dict_index_get_n_unique_in_tree(cursor->index); |
|
1935 |
||
1936 |
if (cursor->low_match >= enough_match) { |
|
1937 |
||
1938 |
rec = btr_cur_get_rec(cursor); |
|
1939 |
||
1940 |
if (!page_rec_is_infimum(rec)) { |
|
1941 |
||
1942 |
return(ROW_INS_PREV); |
|
1943 |
}
|
|
1944 |
}
|
|
1945 |
||
1946 |
return(0); |
|
1947 |
}
|
|
1948 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1949 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1950 |
Tries to insert an index entry to an index. If the index is clustered
|
1951 |
and a record with the same unique key is found, the other record is
|
|
1952 |
necessarily marked deleted by a committed transaction, or a unique key
|
|
1953 |
violation error occurs. The delete marked record is then updated to an
|
|
1954 |
existing record, and we must write an undo log record on the delete
|
|
1955 |
marked record. If the index is secondary, and a record with exactly the
|
|
1956 |
same fields is found, the other record is necessarily marked deleted.
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1957 |
It is then unmarked. Otherwise, the entry is just inserted to the index.
|
1958 |
@return DB_SUCCESS, DB_LOCK_WAIT, DB_FAIL if pessimistic retry needed,
|
|
1959 |
or error code */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1960 |
static
|
1961 |
ulint
|
|
1962 |
row_ins_index_entry_low( |
|
1963 |
/*====================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1964 |
ulint mode, /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1965 |
depending on whether we wish optimistic or
|
1966 |
pessimistic descent down the index tree */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1967 |
dict_index_t* index, /*!< in: index */ |
1819.9.197
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20101103091611-a3x9p0yivkvu5u9i from MySQL InnoDB |
1968 |
dtuple_t* entry, /*!< in/out: index entry to insert */ |
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
1969 |
ulint n_ext, /*!< in: number of externally stored columns */ |
1970 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1971 |
{
|
1972 |
btr_cur_t cursor; |
|
1819.7.68
by Stewart Smith
Merge initial InnoDB+ import. |
1973 |
ulint search_mode; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1974 |
ulint modify = 0; /* remove warning */ |
1975 |
rec_t* insert_rec; |
|
1976 |
rec_t* rec; |
|
1977 |
ulint err; |
|
1978 |
ulint n_unique; |
|
1979 |
big_rec_t* big_rec = NULL; |
|
1980 |
mtr_t mtr; |
|
1981 |
mem_heap_t* heap = NULL; |
|
1982 |
||
1983 |
log_free_check(); |
|
1984 |
||
1985 |
mtr_start(&mtr); |
|
1986 |
||
1987 |
cursor.thr = thr; |
|
1988 |
||
1989 |
/* Note that we use PAGE_CUR_LE as the search mode, because then
|
|
1990 |
the function will return in both low_match and up_match of the
|
|
1991 |
cursor sensible values */
|
|
1992 |
||
1819.7.68
by Stewart Smith
Merge initial InnoDB+ import. |
1993 |
if (dict_index_is_clust(index)) { |
1994 |
search_mode = mode; |
|
1995 |
} else if (!(thr_get_trx(thr)->check_unique_secondary)) { |
|
1996 |
search_mode = mode | BTR_INSERT | BTR_IGNORE_SEC_UNIQUE; |
|
1997 |
} else { |
|
1998 |
search_mode = mode | BTR_INSERT; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
1999 |
}
|
2000 |
||
2001 |
btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, |
|
1819.7.68
by Stewart Smith
Merge initial InnoDB+ import. |
2002 |
search_mode, |
1819.5.187
by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6559 from MySQL InnoDB |
2003 |
&cursor, 0, __FILE__, __LINE__, &mtr); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2004 |
|
2005 |
if (cursor.flag == BTR_CUR_INSERT_TO_IBUF) { |
|
2006 |
/* The insertion was made to the insert buffer already during
|
|
2007 |
the search: we are done */
|
|
2008 |
||
1819.7.68
by Stewart Smith
Merge initial InnoDB+ import. |
2009 |
ut_ad(search_mode & BTR_INSERT); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2010 |
err = DB_SUCCESS; |
2011 |
||
2012 |
goto function_exit; |
|
2013 |
}
|
|
2014 |
||
2015 |
#ifdef UNIV_DEBUG
|
|
2016 |
{
|
|
2017 |
page_t* page = btr_cur_get_page(&cursor); |
|
2018 |
rec_t* first_rec = page_rec_get_next( |
|
2019 |
page_get_infimum_rec(page)); |
|
2020 |
||
2021 |
ut_ad(page_rec_is_supremum(first_rec) |
|
2022 |
|| rec_get_n_fields(first_rec, index) |
|
2023 |
== dtuple_get_n_fields(entry)); |
|
2024 |
}
|
|
2025 |
#endif
|
|
2026 |
||
2027 |
n_unique = dict_index_get_n_unique(index); |
|
2028 |
||
2029 |
if (dict_index_is_unique(index) && (cursor.up_match >= n_unique |
|
2030 |
|| cursor.low_match >= n_unique)) { |
|
2031 |
||
2032 |
if (dict_index_is_clust(index)) { |
|
2033 |
/* Note that the following may return also
|
|
2034 |
DB_LOCK_WAIT */
|
|
2035 |
||
2036 |
err = row_ins_duplicate_error_in_clust( |
|
2037 |
&cursor, entry, thr, &mtr); |
|
2038 |
if (err != DB_SUCCESS) { |
|
2039 |
||
2040 |
goto function_exit; |
|
2041 |
}
|
|
2042 |
} else { |
|
2043 |
mtr_commit(&mtr); |
|
2044 |
err = row_ins_scan_sec_index_for_duplicate( |
|
2045 |
index, entry, thr); |
|
2046 |
mtr_start(&mtr); |
|
2047 |
||
2048 |
if (err != DB_SUCCESS) { |
|
2049 |
||
2050 |
goto function_exit; |
|
2051 |
}
|
|
2052 |
||
2053 |
/* We did not find a duplicate and we have now
|
|
2054 |
locked with s-locks the necessary records to
|
|
2055 |
prevent any insertion of a duplicate by another
|
|
2056 |
transaction. Let us now reposition the cursor and
|
|
2057 |
continue the insertion. */
|
|
2058 |
||
2059 |
btr_cur_search_to_nth_level(index, 0, entry, |
|
2060 |
PAGE_CUR_LE, |
|
2061 |
mode | BTR_INSERT, |
|
1819.5.187
by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6559 from MySQL InnoDB |
2062 |
&cursor, 0, |
2063 |
__FILE__, __LINE__, &mtr); |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2064 |
}
|
2065 |
}
|
|
2066 |
||
2067 |
modify = row_ins_must_modify(&cursor); |
|
2068 |
||
2069 |
if (modify != 0) { |
|
2070 |
/* There is already an index entry with a long enough common
|
|
2071 |
prefix, we must convert the insert into a modify of an
|
|
2072 |
existing record */
|
|
2073 |
||
2074 |
if (modify == ROW_INS_NEXT) { |
|
2075 |
rec = page_rec_get_next(btr_cur_get_rec(&cursor)); |
|
2076 |
||
2077 |
btr_cur_position(index, rec, |
|
2078 |
btr_cur_get_block(&cursor),&cursor); |
|
2079 |
}
|
|
2080 |
||
2081 |
if (dict_index_is_clust(index)) { |
|
2082 |
err = row_ins_clust_index_entry_by_modify( |
|
2083 |
mode, &cursor, &heap, &big_rec, entry, |
|
2084 |
thr, &mtr); |
|
2085 |
} else { |
|
2086 |
ut_ad(!n_ext); |
|
2087 |
err = row_ins_sec_index_entry_by_modify( |
|
2088 |
mode, &cursor, entry, thr, &mtr); |
|
2089 |
}
|
|
2090 |
} else { |
|
2091 |
if (mode == BTR_MODIFY_LEAF) { |
|
2092 |
err = btr_cur_optimistic_insert( |
|
2093 |
0, &cursor, entry, &insert_rec, &big_rec, |
|
2094 |
n_ext, thr, &mtr); |
|
2095 |
} else { |
|
2096 |
ut_a(mode == BTR_MODIFY_TREE); |
|
2097 |
if (buf_LRU_buf_pool_running_out()) { |
|
2098 |
||
2099 |
err = DB_LOCK_TABLE_FULL; |
|
2100 |
||
2101 |
goto function_exit; |
|
2102 |
}
|
|
2103 |
err = btr_cur_pessimistic_insert( |
|
2104 |
0, &cursor, entry, &insert_rec, &big_rec, |
|
2105 |
n_ext, thr, &mtr); |
|
2106 |
}
|
|
2107 |
}
|
|
2108 |
||
2109 |
function_exit: |
|
2110 |
mtr_commit(&mtr); |
|
2111 |
||
2112 |
if (UNIV_LIKELY_NULL(big_rec)) { |
|
2023.3.21
by Monty Taylor
Wow. Got rid of most of INNOBASE_SKIP_WARNINGS. |
2113 |
rec_t* exit_rec; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2114 |
ulint* offsets; |
2115 |
mtr_start(&mtr); |
|
2116 |
||
2117 |
btr_cur_search_to_nth_level(index, 0, entry, PAGE_CUR_LE, |
|
1819.5.187
by marko
Merge Revision revid:svn-v4:16c675df-0fcb-4bc9-8058-dcc011a37293:branches/zip:6559 from MySQL InnoDB |
2118 |
BTR_MODIFY_TREE, &cursor, 0, |
2119 |
__FILE__, __LINE__, &mtr); |
|
2023.3.21
by Monty Taylor
Wow. Got rid of most of INNOBASE_SKIP_WARNINGS. |
2120 |
exit_rec = btr_cur_get_rec(&cursor); |
2121 |
offsets = rec_get_offsets(exit_rec, index, NULL, |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2122 |
ULINT_UNDEFINED, &heap); |
2123 |
||
2124 |
err = btr_store_big_rec_extern_fields( |
|
2125 |
index, btr_cur_get_block(&cursor), |
|
2023.3.21
by Monty Taylor
Wow. Got rid of most of INNOBASE_SKIP_WARNINGS. |
2126 |
exit_rec, offsets, big_rec, &mtr); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2127 |
|
2128 |
if (modify) { |
|
2129 |
dtuple_big_rec_free(big_rec); |
|
2130 |
} else { |
|
2131 |
dtuple_convert_back_big_rec(index, entry, big_rec); |
|
2132 |
}
|
|
2133 |
||
2134 |
mtr_commit(&mtr); |
|
2135 |
}
|
|
2136 |
||
2137 |
if (UNIV_LIKELY_NULL(heap)) { |
|
2138 |
mem_heap_free(heap); |
|
2139 |
}
|
|
2140 |
return(err); |
|
2141 |
}
|
|
2142 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2143 |
/***************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2144 |
Inserts an index entry to index. Tries first optimistic, then pessimistic
|
2145 |
descent down the tree. If the entry matches enough to a delete marked record,
|
|
2146 |
performs the insert by updating or delete unmarking the delete marked
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2147 |
record.
|
2148 |
@return DB_SUCCESS, DB_LOCK_WAIT, DB_DUPLICATE_KEY, or some other error code */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2149 |
UNIV_INTERN
|
2150 |
ulint
|
|
2151 |
row_ins_index_entry( |
|
2152 |
/*================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2153 |
dict_index_t* index, /*!< in: index */ |
1819.9.197
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20101103091611-a3x9p0yivkvu5u9i from MySQL InnoDB |
2154 |
dtuple_t* entry, /*!< in/out: index entry to insert */ |
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2155 |
ulint n_ext, /*!< in: number of externally stored columns */ |
1819.9.201
by Marko Mäkelä
Merge Revision revid:marko.makela@oracle.com-20101104131215-pfxnpidlrzd4krg0 from MySQL InnoDB |
2156 |
ibool foreign,/*!< in: TRUE=check foreign key constraints |
2157 |
(foreign=FALSE only during CREATE INDEX) */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2158 |
que_thr_t* thr) /*!< in: query thread */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2159 |
{
|
1819.9.9
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602103714-0nwxdqskeb1ihozj from MySQL InnoDB |
2160 |
enum db_err err; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2161 |
|
2162 |
if (foreign && UT_LIST_GET_FIRST(index->table->foreign_list)) { |
|
2023.3.13
by Monty Taylor
More. |
2163 |
err = static_cast<db_err>(row_ins_check_foreign_constraints(index->table, index, |
2164 |
entry, thr)); |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2165 |
if (err != DB_SUCCESS) { |
2166 |
||
2167 |
return(err); |
|
2168 |
}
|
|
2169 |
}
|
|
2170 |
||
2171 |
/* Try first optimistic descent to the B-tree */
|
|
2172 |
||
2023.3.14
by Monty Taylor
More casting. |
2173 |
err = static_cast<db_err>(row_ins_index_entry_low(BTR_MODIFY_LEAF, index, entry, |
2023.3.13
by Monty Taylor
More. |
2174 |
n_ext, thr)); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2175 |
if (err != DB_FAIL) { |
2176 |
||
2177 |
return(err); |
|
2178 |
}
|
|
2179 |
||
2180 |
/* Try then pessimistic descent to the B-tree */
|
|
2181 |
||
2023.3.13
by Monty Taylor
More. |
2182 |
err = static_cast<db_err>(row_ins_index_entry_low(BTR_MODIFY_TREE, index, entry, |
2183 |
n_ext, thr)); |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2184 |
return(err); |
2185 |
}
|
|
2186 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2187 |
/***********************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2188 |
Sets the values of the dtuple fields in entry from the values of appropriate
|
2189 |
columns in row. */
|
|
2190 |
static
|
|
2191 |
void
|
|
2192 |
row_ins_index_entry_set_vals( |
|
2193 |
/*=========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2194 |
dict_index_t* index, /*!< in: index */ |
2195 |
dtuple_t* entry, /*!< in: index entry to make */ |
|
2196 |
const dtuple_t* row) /*!< in: row */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2197 |
{
|
2198 |
ulint n_fields; |
|
2199 |
ulint i; |
|
2200 |
||
2201 |
ut_ad(entry && row); |
|
2202 |
||
2203 |
n_fields = dtuple_get_n_fields(entry); |
|
2204 |
||
2205 |
for (i = 0; i < n_fields; i++) { |
|
2206 |
dict_field_t* ind_field; |
|
2207 |
dfield_t* field; |
|
2208 |
const dfield_t* row_field; |
|
2209 |
ulint len; |
|
2210 |
||
2211 |
field = dtuple_get_nth_field(entry, i); |
|
2212 |
ind_field = dict_index_get_nth_field(index, i); |
|
2213 |
row_field = dtuple_get_nth_field(row, ind_field->col->ind); |
|
2214 |
len = dfield_get_len(row_field); |
|
2215 |
||
2216 |
/* Check column prefix indexes */
|
|
2217 |
if (ind_field->prefix_len > 0 |
|
2218 |
&& dfield_get_len(row_field) != UNIV_SQL_NULL) { |
|
2219 |
||
2220 |
const dict_col_t* col |
|
2221 |
= dict_field_get_col(ind_field); |
|
2222 |
||
2223 |
len = dtype_get_at_most_n_mbchars( |
|
1819.9.36
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100629113248-fvl48lnzr44z94gg from MySQL InnoDB |
2224 |
col->prtype, col->mbminmaxlen, |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2225 |
ind_field->prefix_len, |
2023.3.13
by Monty Taylor
More. |
2226 |
len, static_cast<const char *>(dfield_get_data(row_field))); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2227 |
|
2228 |
ut_ad(!dfield_is_ext(row_field)); |
|
2229 |
}
|
|
2230 |
||
2231 |
dfield_set_data(field, dfield_get_data(row_field), len); |
|
2232 |
if (dfield_is_ext(row_field)) { |
|
2233 |
ut_ad(dict_index_is_clust(index)); |
|
2234 |
dfield_set_ext(field); |
|
2235 |
}
|
|
2236 |
}
|
|
2237 |
}
|
|
2238 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2239 |
/***********************************************************//**
|
2240 |
Inserts a single index entry to the table.
|
|
2241 |
@return DB_SUCCESS if operation successfully completed, else error
|
|
2242 |
code or DB_LOCK_WAIT */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2243 |
static
|
2244 |
ulint
|
|
2245 |
row_ins_index_entry_step( |
|
2246 |
/*=====================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2247 |
ins_node_t* node, /*!< in: row insert node */ |
2248 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2249 |
{
|
1819.9.9
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100602103714-0nwxdqskeb1ihozj from MySQL InnoDB |
2250 |
enum db_err err; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2251 |
|
2252 |
ut_ad(dtuple_check_typed(node->row)); |
|
2253 |
||
2254 |
row_ins_index_entry_set_vals(node->index, node->entry, node->row); |
|
2255 |
||
2256 |
ut_ad(dtuple_check_typed(node->entry)); |
|
2257 |
||
2023.3.13
by Monty Taylor
More. |
2258 |
err = static_cast<db_err>(row_ins_index_entry(node->index, node->entry, 0, TRUE, thr)); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2259 |
|
2260 |
return(err); |
|
2261 |
}
|
|
2262 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2263 |
/***********************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2264 |
Allocates a row id for row and inits the node->index field. */
|
2265 |
UNIV_INLINE
|
|
2266 |
void
|
|
2267 |
row_ins_alloc_row_id_step( |
|
2268 |
/*======================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2269 |
ins_node_t* node) /*!< in: row insert node */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2270 |
{
|
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
2271 |
row_id_t row_id; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2272 |
|
2273 |
ut_ad(node->state == INS_NODE_ALLOC_ROW_ID); |
|
2274 |
||
2275 |
if (dict_index_is_unique(dict_table_get_first_index(node->table))) { |
|
2276 |
||
2277 |
/* No row id is stored if the clustered index is unique */
|
|
2278 |
||
2279 |
return; |
|
2280 |
}
|
|
2281 |
||
2282 |
/* Fill in row id value to row */
|
|
2283 |
||
2284 |
row_id = dict_sys_get_new_row_id(); |
|
2285 |
||
2286 |
dict_sys_write_row_id(node->row_id_buf, row_id); |
|
2287 |
}
|
|
2288 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2289 |
/***********************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2290 |
Gets a row to insert from the values list. */
|
2291 |
UNIV_INLINE
|
|
2292 |
void
|
|
2293 |
row_ins_get_row_from_values( |
|
2294 |
/*========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2295 |
ins_node_t* node) /*!< in: row insert node */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2296 |
{
|
2297 |
que_node_t* list_node; |
|
2298 |
dfield_t* dfield; |
|
2299 |
dtuple_t* row; |
|
2300 |
ulint i; |
|
2301 |
||
2302 |
/* The field values are copied in the buffers of the select node and
|
|
2303 |
it is safe to use them until we fetch from select again: therefore
|
|
2304 |
we can just copy the pointers */
|
|
2305 |
||
2306 |
row = node->row; |
|
2307 |
||
2308 |
i = 0; |
|
2309 |
list_node = node->values_list; |
|
2310 |
||
2311 |
while (list_node) { |
|
2312 |
eval_exp(list_node); |
|
2313 |
||
2314 |
dfield = dtuple_get_nth_field(row, i); |
|
2315 |
dfield_copy_data(dfield, que_node_get_val(list_node)); |
|
2316 |
||
2317 |
i++; |
|
2318 |
list_node = que_node_get_next(list_node); |
|
2319 |
}
|
|
2320 |
}
|
|
2321 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2322 |
/***********************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2323 |
Gets a row to insert from the select list. */
|
2324 |
UNIV_INLINE
|
|
2325 |
void
|
|
2326 |
row_ins_get_row_from_select( |
|
2327 |
/*========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2328 |
ins_node_t* node) /*!< in: row insert node */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2329 |
{
|
2330 |
que_node_t* list_node; |
|
2331 |
dfield_t* dfield; |
|
2332 |
dtuple_t* row; |
|
2333 |
ulint i; |
|
2334 |
||
2335 |
/* The field values are copied in the buffers of the select node and
|
|
2336 |
it is safe to use them until we fetch from select again: therefore
|
|
2337 |
we can just copy the pointers */
|
|
2338 |
||
2339 |
row = node->row; |
|
2340 |
||
2341 |
i = 0; |
|
2342 |
list_node = node->select->select_list; |
|
2343 |
||
2344 |
while (list_node) { |
|
2345 |
dfield = dtuple_get_nth_field(row, i); |
|
2346 |
dfield_copy_data(dfield, que_node_get_val(list_node)); |
|
2347 |
||
2348 |
i++; |
|
2349 |
list_node = que_node_get_next(list_node); |
|
2350 |
}
|
|
2351 |
}
|
|
2352 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2353 |
/***********************************************************//**
|
2354 |
Inserts a row to a table.
|
|
2355 |
@return DB_SUCCESS if operation successfully completed, else error
|
|
2356 |
code or DB_LOCK_WAIT */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2357 |
static
|
2358 |
ulint
|
|
2359 |
row_ins( |
|
2360 |
/*====*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2361 |
ins_node_t* node, /*!< in: row insert node */ |
2362 |
que_thr_t* thr) /*!< in: query thread */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2363 |
{
|
2364 |
ulint err; |
|
2365 |
||
2366 |
ut_ad(node && thr); |
|
2367 |
||
2368 |
if (node->state == INS_NODE_ALLOC_ROW_ID) { |
|
2369 |
||
2370 |
row_ins_alloc_row_id_step(node); |
|
2371 |
||
2372 |
node->index = dict_table_get_first_index(node->table); |
|
2373 |
node->entry = UT_LIST_GET_FIRST(node->entry_list); |
|
2374 |
||
2375 |
if (node->ins_type == INS_SEARCHED) { |
|
2376 |
||
2377 |
row_ins_get_row_from_select(node); |
|
2378 |
||
2379 |
} else if (node->ins_type == INS_VALUES) { |
|
2380 |
||
2381 |
row_ins_get_row_from_values(node); |
|
2382 |
}
|
|
2383 |
||
2384 |
node->state = INS_NODE_INSERT_ENTRIES; |
|
2385 |
}
|
|
2386 |
||
2387 |
ut_ad(node->state == INS_NODE_INSERT_ENTRIES); |
|
2388 |
||
2389 |
while (node->index != NULL) { |
|
2390 |
err = row_ins_index_entry_step(node, thr); |
|
2391 |
||
2392 |
if (err != DB_SUCCESS) { |
|
2393 |
||
2394 |
return(err); |
|
2395 |
}
|
|
2396 |
||
2397 |
node->index = dict_table_get_next_index(node->index); |
|
2398 |
node->entry = UT_LIST_GET_NEXT(tuple_list, node->entry); |
|
2399 |
}
|
|
2400 |
||
2401 |
ut_ad(node->entry == NULL); |
|
2402 |
||
2403 |
node->state = INS_NODE_ALLOC_ROW_ID; |
|
2404 |
||
2405 |
return(DB_SUCCESS); |
|
2406 |
}
|
|
2407 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2408 |
/***********************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2409 |
Inserts a row to a table. This is a high-level function used in SQL execution
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2410 |
graphs.
|
2411 |
@return query thread to run next or NULL */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2412 |
UNIV_INTERN
|
2413 |
que_thr_t* |
|
2414 |
row_ins_step( |
|
2415 |
/*=========*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
2416 |
que_thr_t* thr) /*!< in: query thread */ |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2417 |
{
|
2418 |
ins_node_t* node; |
|
2419 |
que_node_t* parent; |
|
2420 |
sel_node_t* sel_node; |
|
2421 |
trx_t* trx; |
|
2422 |
ulint err; |
|
2423 |
||
2424 |
ut_ad(thr); |
|
2425 |
||
2426 |
trx = thr_get_trx(thr); |
|
2427 |
||
2428 |
trx_start_if_not_started(trx); |
|
2429 |
||
2023.3.13
by Monty Taylor
More. |
2430 |
node = static_cast<ins_node_t *>(thr->run_node); |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2431 |
|
2432 |
ut_ad(que_node_get_type(node) == QUE_NODE_INSERT); |
|
2433 |
||
2434 |
parent = que_node_get_parent(node); |
|
2435 |
sel_node = node->select; |
|
2436 |
||
2437 |
if (thr->prev_node == parent) { |
|
2438 |
node->state = INS_NODE_SET_IX_LOCK; |
|
2439 |
}
|
|
2440 |
||
2441 |
/* If this is the first time this node is executed (or when
|
|
2442 |
execution resumes after wait for the table IX lock), set an
|
|
2443 |
IX lock on the table and reset the possible select node. MySQL's
|
|
2444 |
partitioned table code may also call an insert within the same
|
|
2445 |
SQL statement AFTER it has used this table handle to do a search.
|
|
2446 |
This happens, for example, when a row update moves it to another
|
|
2447 |
partition. In that case, we have already set the IX lock on the
|
|
2448 |
table during the search operation, and there is no need to set
|
|
2449 |
it again here. But we must write trx->id to node->trx_id_buf. */
|
|
2450 |
||
2451 |
trx_write_trx_id(node->trx_id_buf, trx->id); |
|
2452 |
||
2453 |
if (node->state == INS_NODE_SET_IX_LOCK) { |
|
2454 |
||
2455 |
/* It may be that the current session has not yet started
|
|
2456 |
its transaction, or it has been committed: */
|
|
2457 |
||
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
2458 |
if (trx->id == node->trx_id) { |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
2459 |
/* No need to do IX-locking */
|
2460 |
||
2461 |
goto same_trx; |
|
2462 |
}
|
|
2463 |
||
2464 |
err = lock_table(0, node->table, LOCK_IX, thr); |
|
2465 |
||
2466 |
if (err != DB_SUCCESS) { |
|
2467 |
||
2468 |
goto error_handling; |
|
2469 |
}
|
|
2470 |
||
2471 |
node->trx_id = trx->id; |
|
2472 |
same_trx: |
|
2473 |
node->state = INS_NODE_ALLOC_ROW_ID; |
|
2474 |
||
2475 |
if (node->ins_type == INS_SEARCHED) { |
|
2476 |
/* Reset the cursor */
|
|
2477 |
sel_node->state = SEL_NODE_OPEN; |
|
2478 |
||
2479 |
/* Fetch a row to insert */
|
|
2480 |
||
2481 |
thr->run_node = sel_node; |
|
2482 |
||
2483 |
return(thr); |
|
2484 |
}
|
|
2485 |
}
|
|
2486 |
||
2487 |
if ((node->ins_type == INS_SEARCHED) |
|
2488 |
&& (sel_node->state != SEL_NODE_FETCH)) { |
|
2489 |
||
2490 |
ut_ad(sel_node->state == SEL_NODE_NO_MORE_ROWS); |
|
2491 |
||
2492 |
/* No more rows to insert */
|
|
2493 |
thr->run_node = parent; |
|
2494 |
||
2495 |
return(thr); |
|
2496 |
}
|
|
2497 |
||
2498 |
/* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */
|
|
2499 |
||
2500 |
err = row_ins(node, thr); |
|
2501 |
||
2502 |
error_handling: |
|
2503 |
trx->error_state = err; |
|
2504 |
||
2505 |
if (err != DB_SUCCESS) { |
|
2506 |
/* err == DB_LOCK_WAIT or SQL error detected */
|
|
2507 |
return(NULL); |
|
2508 |
}
|
|
2509 |
||
2510 |
/* DO THE TRIGGER ACTIONS HERE */
|
|
2511 |
||
2512 |
if (node->ins_type == INS_SEARCHED) { |
|
2513 |
/* Fetch a row to insert */
|
|
2514 |
||
2515 |
thr->run_node = sel_node; |
|
2516 |
} else { |
|
2517 |
thr->run_node = que_node_get_parent(node); |
|
2518 |
}
|
|
2519 |
||
2520 |
return(thr); |
|
2521 |
}
|