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