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