1 /* Copyright (C) 2008 Sun AB and Michael Widenius
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15
16 /*
17 Functions to maintain live statistics for Maria transactional tables
18 and versioning for not transactional tables
19
20 See WL#3138; Maria - fast "SELECT COUNT(*) FROM t;" and "CHECKSUM TABLE t"
21 for details about live number of rows and live checksums
22
23 TODO
24 - Allocate MA_USED_TABLES and MA_HISTORY_STATE from a global pool (to
25 avoid calls to malloc()
26 - In trnamn_end_trans_hook(), don't call _ma_remove_not_visible_states()
27 every time. One could for example call it if there has been more than
28 10 ended transactions since last time it was called.
29 */
30
31 #include "maria_def.h"
32 #include "trnman.h"
33 #include "ma_trnman.h"
34 #include "ma_blockrec.h"
35
36 /**
37 @brief Setup initial start-of-transaction state for a table
38
39 @fn _ma_setup_live_state
40 @param info Maria handler
41
42 @notes
43 This function ensures that trn->used_tables contains a list of
44 start and live states for tables that are part of the transaction
45 and that info->state points to the current live state for the table.
46
47 @TODO
48 Change trn->table_list to a hash and share->state_history to a binary tree
49
50 @return
51 @retval 0 ok
52 @retval 1 error (out of memory)
53 */
54
_ma_setup_live_state(MARIA_HA * info)55 my_bool _ma_setup_live_state(MARIA_HA *info)
56 {
57 TRN *trn;
58 MARIA_SHARE *share= info->s;
59 MARIA_USED_TABLES *tables;
60 MARIA_STATE_HISTORY *history;
61 DBUG_ENTER("_ma_setup_live_state");
62 DBUG_PRINT("enter", ("info: %p", info));
63
64 DBUG_ASSERT(share->lock_key_trees);
65
66 if (maria_create_trn_hook(info))
67 DBUG_RETURN(1);
68
69 trn= info->trn;
70 for (tables= (MARIA_USED_TABLES*) trn->used_tables;
71 tables;
72 tables= tables->next)
73 {
74 if (tables->share == share)
75 {
76 /* Table is already used by transaction */
77 goto end;
78 }
79 }
80
81 /* Table was not used before, create new table state entry */
82 if (!(tables= (MARIA_USED_TABLES*) my_malloc(sizeof(*tables),
83 MYF(MY_WME | MY_ZEROFILL))))
84 DBUG_RETURN(1);
85 tables->next= trn->used_tables;
86 trn->used_tables= tables;
87 tables->share= share;
88
89 mysql_mutex_lock(&share->intern_lock);
90 share->in_trans++;
91 DBUG_PRINT("info", ("share: %p in_trans: %d",
92 share, share->in_trans));
93
94 history= share->state_history;
95
96 /*
97 We must keep share locked to ensure that we don't access a history
98 link that is deleted by concurrently running checkpoint.
99
100 It's enough to compare trids here (instead of calling
101 tranman_can_read_from) as history->trid is a commit_trid
102 */
103 while (trn->trid <= history->trid)
104 history= history->next;
105 mysql_mutex_unlock(&share->intern_lock);
106 /* The current item can't be deleted as it's the first one visible for us */
107 tables->state_start= tables->state_current= history->state;
108 tables->state_current.changed= tables->state_current.no_transid= 0;
109
110 DBUG_PRINT("info", ("records: %ld", (ulong) tables->state_start.records));
111
112 end:
113 info->state_start= &tables->state_start;
114 info->state= &tables->state_current;
115 info->used_tables= tables;
116 tables->use_count++;
117
118 /*
119 Mark in transaction state if we are not using transid (versioning)
120 on rows. If not, then we will in _ma_trnman_end_trans_hook()
121 ensure that the state is visible for all at end of transaction
122 */
123 tables->state_current.no_transid|= !(info->row_flag & ROW_FLAG_TRANSID);
124
125 DBUG_PRINT("exit", ("tables: %p info->state: %p", tables, info->state));
126 DBUG_RETURN(0);
127 }
128
129
130 /**
131 @brief Remove states that are not visible by anyone
132
133 @fn _ma_remove_not_visible_states()
134 @param org_history List to history
135 @param all 1 if we should delete the first state if it's
136 visible for all. For the moment this is only used
137 on close() of table.
138 @param trnman_is_locked Set to 1 if we have already a lock on trnman.
139
140 @notes
141 The assumption is that items in the history list is ordered by
142 commit_trid.
143
144 A state is not visible anymore if there is no new transaction
145 that has been started between the commit_trid's of two states
146
147 As long as some states exists, we keep the newest = (last commit)
148 state as first state in the history. This is to allow us to just move
149 the history from the global list to the share when we open the table.
150
151 Note that if 'all' is set trnman_is_locked must be 0, becasue
152 trnman_get_min_trid() will take a lock on trnman.
153
154 @return
155 @retval Pointer to new history list
156 */
157
158 MARIA_STATE_HISTORY
_ma_remove_not_visible_states(MARIA_STATE_HISTORY * org_history,my_bool all,my_bool trnman_is_locked)159 *_ma_remove_not_visible_states(MARIA_STATE_HISTORY *org_history,
160 my_bool all,
161 my_bool trnman_is_locked)
162 {
163 TrID last_trid;
164 MARIA_STATE_HISTORY *history, **parent, *next;
165 DBUG_ENTER("_ma_remove_not_visible_states");
166
167 if (!org_history)
168 DBUG_RETURN(0); /* Not versioned table */
169
170 last_trid= org_history->trid;
171 parent= &org_history->next;
172 for (history= org_history->next; history; history= next)
173 {
174 next= history->next;
175 if (!trnman_exists_active_transactions(history->trid, last_trid,
176 trnman_is_locked))
177 {
178 DBUG_PRINT("info", ("removing history->trid: %lu next: %lu",
179 (ulong) history->trid, (ulong) last_trid));
180 my_free(history);
181 continue;
182 }
183 *parent= history;
184 parent= &history->next;
185 last_trid= history->trid;
186 }
187 *parent= 0;
188
189 if (all && parent == &org_history->next)
190 {
191 /* There is only one state left. Delete this if it's visible for all */
192 if (last_trid < trnman_get_min_trid())
193 {
194 my_free(org_history);
195 org_history= 0;
196 }
197 }
198 DBUG_RETURN(org_history);
199 }
200
201
202 /**
203 @brief Remove not used state history
204
205 @param share Maria table information
206 @param all 1 if we should delete the first state if it's
207 visible for all. For the moment this is only used
208 on close() of table.
209
210 @notes
211 share and trnman are not locked.
212
213 We must first lock trnman and then share->intern_lock. This is becasue
214 _ma_trnman_end_trans_hook() has a lock on trnman and then
215 takes share->intern_lock.
216 */
217
_ma_remove_not_visible_states_with_lock(MARIA_SHARE * share,my_bool all)218 void _ma_remove_not_visible_states_with_lock(MARIA_SHARE *share,
219 my_bool all)
220 {
221 my_bool is_lock_trman;
222 if ((is_lock_trman= trman_is_inited()))
223 trnman_lock();
224
225 mysql_mutex_lock(&share->intern_lock);
226 share->state_history= _ma_remove_not_visible_states(share->state_history,
227 all, 1);
228 mysql_mutex_unlock(&share->intern_lock);
229 if (is_lock_trman)
230 trnman_unlock();
231 }
232
233
234 /*
235 Free state history information from share->history and reset information
236 to current state.
237
238 @notes
239 Used after repair/rename/drop as then all rows are visible for everyone
240 */
241
_ma_reset_state(MARIA_HA * info)242 void _ma_reset_state(MARIA_HA *info)
243 {
244 MARIA_SHARE *share= info->s;
245 MARIA_STATE_HISTORY *history= share->state_history;
246 DBUG_ENTER("_ma_reset_state");
247
248 /* Always true if share->now_transactional is set */
249 if (history && share->have_versioning)
250 {
251 MARIA_STATE_HISTORY *next;
252 DBUG_PRINT("info", ("resetting history"));
253
254 /* Set the current history to current state */
255 share->state_history->state= share->state.state;
256 /* Set current table handler to point to new history state */
257 info->state= info->state_start= &share->state_history->state;
258 for (history= history->next ; history ; history= next)
259 {
260 next= history->next;
261 my_free(history);
262 }
263 share->state_history->next= 0;
264 share->state_history->trid= 0; /* Visible for all */
265 }
266 DBUG_VOID_RETURN;
267 }
268
269
270 /****************************************************************************
271 The following functions are called by thr_lock() in threaded applications
272 for not transactional tables
273 ****************************************************************************/
274
275 /*
276 Create a copy of the current status for the table
277
278 SYNOPSIS
279 _ma_get_status()
280 param Pointer to Myisam handler
281 concurrent_insert Set to 1 if we are going to do concurrent inserts
282 (THR_WRITE_CONCURRENT_INSERT was used)
283 */
284
_ma_get_status(void * param,my_bool concurrent_insert)285 void _ma_get_status(void* param, my_bool concurrent_insert)
286 {
287 MARIA_HA *info=(MARIA_HA*) param;
288 DBUG_ENTER("_ma_get_status");
289 DBUG_PRINT("info",("key_file: %ld data_file: %ld concurrent_insert: %d",
290 (long) info->s->state.state.key_file_length,
291 (long) info->s->state.state.data_file_length,
292 concurrent_insert));
293 #ifndef DBUG_OFF
294 if (info->state->key_file_length > info->s->state.state.key_file_length ||
295 info->state->data_file_length > info->s->state.state.data_file_length)
296 DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
297 (long) info->state->key_file_length,
298 (long) info->state->data_file_length));
299 #endif
300 info->state_save= info->s->state.state;
301 info->state= &info->state_save;
302 info->state->changed= 0;
303 info->append_insert_at_end= concurrent_insert;
304 DBUG_VOID_RETURN;
305 }
306
307
_ma_update_status(void * param)308 void _ma_update_status(void* param)
309 {
310 MARIA_HA *info=(MARIA_HA*) param;
311 /*
312 Because someone may have closed the table we point at, we only
313 update the state if its our own state. This isn't a problem as
314 we are always pointing at our own lock or at a read lock.
315 (This is enforced by thr_multi_lock.c)
316 */
317 if (info->state == &info->state_save)
318 {
319 MARIA_SHARE *share= info->s;
320 #ifndef DBUG_OFF
321 DBUG_PRINT("info",("updating status: key_file: %ld data_file: %ld",
322 (long) info->state->key_file_length,
323 (long) info->state->data_file_length));
324 if (info->state->key_file_length < share->state.state.key_file_length ||
325 info->state->data_file_length < share->state.state.data_file_length)
326 DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld",
327 (long) share->state.state.key_file_length,
328 (long) share->state.state.data_file_length));
329 #endif
330 /*
331 we are going to modify the state without lock's log, this would break
332 recovery if done with a transactional table.
333 */
334 DBUG_ASSERT(!info->s->base.born_transactional);
335 share->state.state= *info->state;
336 info->state= &share->state.state;
337 #ifdef HAVE_QUERY_CACHE
338 DBUG_PRINT("info", ("invalidator... '%s' (status update)",
339 info->s->data_file_name.str));
340 DBUG_ASSERT(info->s->chst_invalidator != NULL);
341 (*info->s->chst_invalidator)((const char *)info->s->data_file_name.str);
342 #endif
343
344 }
345 info->append_insert_at_end= 0;
346 }
347
348
349 /*
350 Same as ma_update_status() but take a lock in the table lock, to protect
351 against someone calling ma_get_status() from thr_lock() at the same time.
352 */
353
_ma_update_status_with_lock(MARIA_HA * info)354 void _ma_update_status_with_lock(MARIA_HA *info)
355 {
356 my_bool locked= 0;
357 if (info->state == &info->state_save)
358 {
359 locked= 1;
360 mysql_mutex_lock(&info->s->lock.mutex);
361 }
362 (*info->s->lock.update_status)(info);
363 if (locked)
364 mysql_mutex_unlock(&info->s->lock.mutex);
365 }
366
367
_ma_restore_status(void * param)368 void _ma_restore_status(void *param)
369 {
370 MARIA_HA *info= (MARIA_HA*) param;
371 info->state= &info->s->state.state;
372 info->append_insert_at_end= 0;
373 }
374
375
_ma_copy_status(void * to,void * from)376 void _ma_copy_status(void* to, void *from)
377 {
378 ((MARIA_HA*) to)->state= &((MARIA_HA*) from)->state_save;
379 }
380
381
_ma_reset_update_flag(void * param,my_bool concurrent_insert)382 void _ma_reset_update_flag(void *param,
383 my_bool concurrent_insert __attribute__((unused)))
384 {
385 MARIA_HA *info=(MARIA_HA*) param;
386 info->state->changed= 0;
387 }
388
_ma_start_trans(void * param)389 my_bool _ma_start_trans(void* param)
390 {
391 MARIA_HA *info=(MARIA_HA*) param;
392 if (!info->s->lock_key_trees)
393 {
394 info->state= info->state_start;
395 *info->state= info->s->state.state;
396 }
397 return 0;
398 }
399
400
401 /**
402 @brief Check if should allow concurrent inserts
403
404 @implementation
405 Allow concurrent inserts if we don't have a hole in the table or
406 if there is no active write lock and there is active read locks and
407 maria_concurrent_insert == 2. In this last case the new
408 row('s) are inserted at end of file instead of filling up the hole.
409
410 The last case is to allow one to inserts into a heavily read-used table
411 even if there is holes.
412
413 @notes
414 If there is a an rtree indexes in the table, concurrent inserts are
415 disabled in maria_open()
416
417 @return
418 @retval 0 ok to use concurrent inserts
419 @retval 1 not ok
420 */
421
_ma_check_status(void * param)422 my_bool _ma_check_status(void *param)
423 {
424 MARIA_HA *info=(MARIA_HA*) param;
425 /*
426 The test for w_locks == 1 is here because this thread has already done an
427 external lock (in other words: w_locks == 1 means no other threads has
428 a write lock)
429 */
430 DBUG_PRINT("info",("dellink: %ld r_locks: %u w_locks: %u",
431 (long) info->s->state.dellink, (uint) info->s->r_locks,
432 (uint) info->s->w_locks));
433 return (my_bool) !(info->s->state.dellink == HA_OFFSET_ERROR ||
434 (maria_concurrent_insert == 2 && info->s->r_locks &&
435 info->s->w_locks == 1));
436 }
437
438
439 /**
440 @brief write hook at end of trans to store status for all used table
441
442 @Notes
443 This function must be called under trnman_lock in trnman_end_trn()
444 because of the following reasons:
445 - After trnman_end_trn() is called, the current transaction will be
446 regarded as committed and all used tables state_history will be
447 visible to other transactions. To do this, we loop over all used
448 tables and create/update a history entries that contains the correct
449 state_history for them.
450 */
451
_ma_trnman_end_trans_hook(TRN * trn,my_bool commit,my_bool active_transactions)452 my_bool _ma_trnman_end_trans_hook(TRN *trn, my_bool commit,
453 my_bool active_transactions)
454 {
455 my_bool error= 0;
456 MARIA_USED_TABLES *tables, *next;
457 DBUG_ENTER("_ma_trnman_end_trans_hook");
458 DBUG_PRINT("enter", ("trn: %p used_tables: %p", trn, trn->used_tables));
459
460 for (tables= (MARIA_USED_TABLES*) trn->used_tables;
461 tables;
462 tables= next)
463 {
464 MARIA_SHARE *share= tables->share;
465 next= tables->next;
466 if (commit)
467 {
468 MARIA_STATE_HISTORY *history;
469
470 mysql_mutex_lock(&share->intern_lock);
471
472 /* We only have to update history state if something changed */
473 if (tables->state_current.changed)
474 {
475 if (tables->state_current.no_transid)
476 {
477 /*
478 The change was done without using transid on rows (like in
479 bulk insert). In this case this thread is the only one
480 that is using the table and all rows will be visble
481 for all transactions.
482 */
483 _ma_reset_history(share);
484 }
485 else
486 {
487 if (active_transactions && share->now_transactional &&
488 trnman_exists_active_transactions(share->state_history->trid,
489 trn->commit_trid, 1))
490 {
491 /*
492 There exist transactions that are still using the current
493 share->state_history. Create a new history item for this
494 commit and add it first in the state_history list. This
495 ensures that all history items are stored in the list in
496 decresing trid order.
497 */
498 if (!(history= my_malloc(sizeof(*history), MYF(MY_WME))))
499 {
500 /* purecov: begin inspected */
501 error= 1;
502 mysql_mutex_unlock(&share->intern_lock);
503 my_free(tables);
504 continue;
505 /* purecov: end */
506 }
507 history->state= share->state_history->state;
508 history->next= share->state_history;
509 share->state_history= history;
510 }
511 else
512 {
513 /* Previous history can't be seen by anyone, reuse old memory */
514 history= share->state_history;
515 DBUG_PRINT("info", ("removing history->trid: %lu new: %lu",
516 (ulong) history->trid,
517 (ulong) trn->commit_trid));
518 }
519
520 history->state.records+= (tables->state_current.records -
521 tables->state_start.records);
522 history->state.checksum+= (tables->state_current.checksum -
523 tables->state_start.checksum);
524 history->trid= trn->commit_trid;
525
526 share->state.last_change_trn= trn->commit_trid;
527
528 if (history->next)
529 {
530 /* Remove not visible states */
531 share->state_history= _ma_remove_not_visible_states(history, 0, 1);
532 }
533 DBUG_PRINT("info", ("share: %p in_trans: %d",
534 share, share->in_trans));
535 }
536 }
537 share->in_trans--;
538 mysql_mutex_unlock(&share->intern_lock);
539 }
540 else
541 {
542 #ifdef DBUG_ASSERT_EXISTS
543 /*
544 We need to keep share->in_trans correct in the debug library
545 because of the assert in maria_close()
546 */
547 mysql_mutex_lock(&share->intern_lock);
548 share->in_trans--;
549 mysql_mutex_unlock(&share->intern_lock);
550 #endif
551 }
552 my_free(tables);
553 }
554 trn->used_tables= 0;
555 trn->used_instances= 0;
556 DBUG_RETURN(error);
557 }
558
559
560 /**
561 Remove table from trnman_list
562
563 @notes
564 This is used when we unlock a table from a group of locked tables
565 just before doing a rename or drop table.
566
567 share->internal_lock must be locked when function is called
568 */
569
_ma_remove_table_from_trnman(MARIA_HA * info)570 void _ma_remove_table_from_trnman(MARIA_HA *info)
571 {
572 MARIA_SHARE *share= info->s;
573 TRN *trn= info->trn;
574 MARIA_USED_TABLES *tables, **prev;
575 DBUG_ENTER("_ma_remove_table_from_trnman");
576 DBUG_PRINT("enter", ("trn: %p used_tables: %p share: %p in_trans: %d",
577 trn, trn->used_tables, share, share->in_trans));
578
579 mysql_mutex_assert_owner(&share->intern_lock);
580
581 if (trn == &dummy_transaction_object)
582 DBUG_VOID_RETURN;
583
584 /* First remove share from used_tables */
585 for (prev= (MARIA_USED_TABLES**) (char*) &trn->used_tables;
586 (tables= *prev);
587 prev= &tables->next)
588 {
589 if (tables->share == share)
590 {
591 *prev= tables->next;
592 share->in_trans--;
593 my_free(tables);
594 break;
595 }
596 }
597 if (!tables)
598 {
599 /*
600 This can only happens in case of rename of intermediate table as
601 part of alter table
602 */
603 DBUG_PRINT("warning", ("share: %p where not in used_tables_list", share));
604 }
605
606 /* Reset trn and remove table from used_instances */
607 _ma_reset_trn_for_table(info);
608
609 DBUG_VOID_RETURN;
610 }
611
612
613
614 /****************************************************************************
615 The following functions are called by thr_lock() in threaded applications
616 for transactional tables.
617 ****************************************************************************/
618
619 /*
620 Create a copy of the current status for the table
621
622 SYNOPSIS
623 _ma_get_status()
624 param Pointer to Aria handler
625 concurrent_insert Set to 1 if we are going to do concurrent inserts
626 (THR_WRITE_CONCURRENT_INSERT was used)
627 */
628
_ma_block_get_status(void * param,my_bool concurrent_insert)629 void _ma_block_get_status(void* param, my_bool concurrent_insert)
630 {
631 MARIA_HA *info=(MARIA_HA*) param;
632 DBUG_ENTER("_ma_block_get_status");
633 DBUG_PRINT("enter", ("concurrent_insert %d", concurrent_insert));
634
635 info->row_base_length= info->s->base_length;
636 info->row_flag= info->s->base.default_row_flag;
637 if (concurrent_insert)
638 {
639 DBUG_ASSERT(info->lock.type == TL_WRITE_CONCURRENT_INSERT);
640 info->row_flag|= ROW_FLAG_TRANSID;
641 info->row_base_length+= TRANSID_SIZE;
642 }
643 else
644 {
645 DBUG_ASSERT(info->lock.type != TL_WRITE_CONCURRENT_INSERT);
646 }
647 DBUG_VOID_RETURN;
648 }
649
650
_ma_block_start_trans(void * param)651 my_bool _ma_block_start_trans(void* param)
652 {
653 MARIA_HA *info=(MARIA_HA*) param;
654 DBUG_ENTER("_ma_block_start_trans");
655
656 if (info->s->lock_key_trees)
657 {
658 /*
659 Assume for now that this doesn't fail (It can only fail in
660 out of memory conditions)
661 TODO: Fix this by having one extra state pre-allocated
662 */
663 DBUG_RETURN(_ma_setup_live_state(info));
664 }
665 else
666 {
667 /*
668 We come here in the following cases:
669 - The table is a temporary table
670 - It's a table which is crash safe but not yet versioned, for
671 example a table with fulltext or rtree keys
672
673 Set the current state to point to save_state so that the
674 block_format code don't count the same record twice.
675 Copy also the current state. This may have been wrong if the
676 same file was used several times in the last statement
677 */
678 info->state= info->state_start;
679 *info->state= info->s->state.state;
680 }
681
682 /*
683 Info->trn is set if this table is already handled and we are
684 called from maria_versioning()
685 */
686 if (info->s->base.born_transactional && !info->trn)
687 {
688 /*
689 Assume for now that this doesn't fail (It can only fail in
690 out of memory conditions)
691 */
692 DBUG_RETURN(maria_create_trn_hook(info) != 0);
693 }
694 DBUG_RETURN(0);
695 }
696
697
_ma_block_update_status(void * param)698 void _ma_block_update_status(void *param __attribute__((unused)))
699 {
700 }
701
_ma_block_restore_status(void * param)702 void _ma_block_restore_status(void *param __attribute__((unused)))
703 {
704 }
705
706
707 /**
708 Check if should allow concurrent inserts
709
710 @return
711 @retval 0 ok to use concurrent inserts
712 @retval 1 not ok
713 */
714
_ma_block_check_status(void * param)715 my_bool _ma_block_check_status(void *param __attribute__((unused)))
716 {
717 return (my_bool) 0;
718 }
719
720
721 /* Get status when transactional but not versioned */
722
_ma_block_start_trans_no_versioning(void * param)723 my_bool _ma_block_start_trans_no_versioning(void* param)
724 {
725 MARIA_HA *info=(MARIA_HA*) param;
726 DBUG_ENTER("_ma_block_start_trans_no_versioning");
727 DBUG_ASSERT(info->s->base.born_transactional && !info->s->lock_key_trees);
728
729 info->state->changed= 0; /* from _ma_reset_update_flag() */
730 info->state= info->state_start;
731 *info->state= info->s->state.state;
732 if (!info->trn)
733 {
734 /*
735 Assume for now that this doesn't fail (It can only fail in
736 out of memory conditions)
737 */
738 DBUG_RETURN(maria_create_trn_hook(info));
739 }
740 DBUG_RETURN(0);
741 }
742
743
744 /**
745 Enable/disable versioning
746 */
747
maria_versioning(MARIA_HA * info,my_bool versioning)748 void maria_versioning(MARIA_HA *info, my_bool versioning)
749 {
750 MARIA_SHARE *share= info->s;
751 DBUG_ENTER("maria_versioning");
752
753 /* For now, this is a hack */
754 if (share->have_versioning)
755 {
756 enum thr_lock_type save_lock_type;
757 share->lock_key_trees= versioning;
758 /* Set up info->lock.type temporary for _ma_block_get_status() */
759 save_lock_type= info->lock.type;
760 info->lock.type= versioning ? TL_WRITE_CONCURRENT_INSERT : TL_WRITE;
761 _ma_block_get_status((void*) info, versioning);
762 info->lock.type= save_lock_type;
763 if (versioning)
764 info->state= &share->state.common;
765 else
766 info->state= &share->state.state; /* Change global values by default */
767 info->state_start= info->state; /* Initial values */
768 }
769 DBUG_VOID_RETURN;
770 }
771
772
773 /**
774 Update data_file_length to new length
775
776 NOTES
777 Only used by block records
778 */
779
_ma_set_share_data_file_length(MARIA_SHARE * share,ulonglong new_length)780 void _ma_set_share_data_file_length(MARIA_SHARE *share, ulonglong new_length)
781 {
782 if (!share->internal_table)
783 mysql_mutex_lock(&share->intern_lock);
784 if (share->state.state.data_file_length < new_length)
785 {
786 share->state.state.data_file_length= new_length;
787 if (new_length >= share->base.max_data_file_length)
788 {
789 /* Give an error on next insert */
790 share->state.changed|= STATE_DATA_FILE_FULL;
791 }
792 }
793 if (!share->internal_table)
794 mysql_mutex_unlock(&share->intern_lock);
795 }
796
797
798 /**
799 Copy state information that where updated while the table was used
800 in not transactional mode
801 */
802
_ma_copy_nontrans_state_information(MARIA_HA * info)803 void _ma_copy_nontrans_state_information(MARIA_HA *info)
804 {
805 info->s->state.state.records= info->state->records;
806 info->s->state.state.checksum= info->state->checksum;
807 }
808
809 /**
810 Reset history
811 This is only called during repair when we are the only one using the table.
812 */
813
_ma_reset_history(MARIA_SHARE * share)814 void _ma_reset_history(MARIA_SHARE *share)
815 {
816 MARIA_STATE_HISTORY *history, *next;
817 DBUG_ENTER("_ma_reset_history");
818
819 share->state_history->trid= 0; /* Visibly by all */
820 share->state_history->state= share->state.state;
821 history= share->state_history->next;
822 share->state_history->next= 0;
823
824 for (; history; history= next)
825 {
826 next= history->next;
827 my_free(history);
828 }
829 DBUG_VOID_RETURN;
830 }
831
832
833 /****************************************************************************
834 Virtual functions to check if row is visible
835 ****************************************************************************/
836
837 /**
838 Row is always visible
839 This is for tables without concurrent insert
840 */
841
_ma_row_visible_always(MARIA_HA * info)842 my_bool _ma_row_visible_always(MARIA_HA *info __attribute__((unused)))
843 {
844 return 1;
845 }
846
847
848 /**
849 Row visibility for non transactional tables with concurrent insert
850
851 @implementation
852 When we got our table lock, we saved the current
853 data_file_length. Concurrent inserts always go to the end of the
854 file. So we can test if the found key references a new record.
855 */
856
_ma_row_visible_non_transactional_table(MARIA_HA * info)857 my_bool _ma_row_visible_non_transactional_table(MARIA_HA *info)
858 {
859 return info->cur_row.lastpos < info->state->data_file_length;
860 }
861
862
863 /**
864 Row visibility for transactional tables with versioning
865
866
867 @TODO
868 Add test if found key was marked deleted and it was deleted by
869 us. In that case we should return 0
870 */
871
_ma_row_visible_transactional_table(MARIA_HA * info)872 my_bool _ma_row_visible_transactional_table(MARIA_HA *info)
873 {
874 return trnman_can_read_from(info->trn, info->cur_row.trid);
875 }
876