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_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 
_ma_setup_live_state(MARIA_HA * info)54 my_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*) 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 
111 end:
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 
157 MARIA_STATE_HISTORY
_ma_remove_not_visible_states(MARIA_STATE_HISTORY * org_history,my_bool all,my_bool trnman_is_locked)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 
_ma_remove_not_visible_states_with_lock(MARIA_SHARE * share,my_bool all)217 void _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 
_ma_reset_state(MARIA_HA * info)241 void _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 
_ma_get_status(void * param,my_bool concurrent_insert)284 void _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 
_ma_update_status(void * param)307 void _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 
_ma_update_status_with_lock(MARIA_HA * info)353 void _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 
_ma_restore_status(void * param)367 void _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 
_ma_copy_status(void * to,void * from)375 void _ma_copy_status(void* to, void *from)
376 {
377   ((MARIA_HA*) to)->state= &((MARIA_HA*) from)->state_save;
378 }
379 
380 
_ma_reset_update_flag(void * param,my_bool concurrent_insert)381 void _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 
_ma_start_trans(void * param)388 my_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 
_ma_check_status(void * param)421 my_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 
_ma_trnman_end_trans_hook(TRN * trn,my_bool commit,my_bool active_transactions)451 my_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   trn->used_instances= 0;
555   DBUG_RETURN(error);
556 }
557 
558 
559 /**
560    Remove table from trnman_list
561 
562    @notes
563      This is used when we unlock a table from a group of locked tables
564      just before doing a rename or drop table.
565 
566      share->internal_lock must be locked when function is called
567 */
568 
_ma_remove_table_from_trnman(MARIA_HA * info)569 void _ma_remove_table_from_trnman(MARIA_HA *info)
570 {
571   MARIA_SHARE *share= info->s;
572   TRN *trn= info->trn;
573   MARIA_USED_TABLES *tables, **prev;
574   MARIA_HA *handler, **prev_file;
575   uint unlinked= 0;
576   DBUG_ENTER("_ma_remove_table_from_trnman");
577   DBUG_PRINT("enter", ("trn: %p  used_tables: %p  share: %p  in_trans: %d",
578                        trn, trn->used_tables, share, share->in_trans));
579 
580   mysql_mutex_assert_owner(&share->intern_lock);
581 
582   if (trn == &dummy_transaction_object)
583     DBUG_VOID_RETURN;
584 
585   /* First remove share from used_tables */
586   for (prev= (MARIA_USED_TABLES**) (char*) &trn->used_tables;
587        (tables= *prev);
588        prev= &tables->next)
589   {
590     if (tables->share == share)
591     {
592       *prev= tables->next;
593       share->in_trans--;
594       my_free(tables);
595       break;
596     }
597   }
598   if (!tables)
599   {
600     /*
601       This can only happens in case of rename of intermediate table as
602       part of alter table
603     */
604     DBUG_PRINT("warning", ("share: %p where not in used_tables_list", share));
605   }
606 
607   /* unlink all instances of the table from used_instances */
608   prev_file= (MARIA_HA**) &trn->used_instances;
609   while ((handler= *prev_file))
610   {
611     if (handler->s == share)
612     {
613       unlinked++;
614       *prev_file= handler->trn_next;  /* Remove instance */
615     }
616     else
617       prev_file= &handler->trn_next;  /* Continue with next instance */
618   }
619 
620   DBUG_PRINT("note", ("unlinked tables: %u", unlinked));
621   if (!unlinked)
622   {
623     /*
624       This can only happens in case of rename of intermediate table as
625       part of alter table
626     */
627     DBUG_PRINT("warning", ("table: %p where not in used_instances", info));
628   }
629   info->trn= 0;                                 /* Not part of trans anymore */
630   DBUG_VOID_RETURN;
631 }
632 
633 
634 
635 /****************************************************************************
636   The following functions are called by thr_lock() in threaded applications
637   for transactional tables.
638 ****************************************************************************/
639 
640 /*
641   Create a copy of the current status for the table
642 
643   SYNOPSIS
644     _ma_get_status()
645     param		Pointer to Aria handler
646     concurrent_insert	Set to 1 if we are going to do concurrent inserts
647 			(THR_WRITE_CONCURRENT_INSERT was used)
648 */
649 
_ma_block_get_status(void * param,my_bool concurrent_insert)650 void _ma_block_get_status(void* param, my_bool concurrent_insert)
651 {
652   MARIA_HA *info=(MARIA_HA*) param;
653   DBUG_ENTER("_ma_block_get_status");
654   DBUG_PRINT("enter", ("concurrent_insert %d", concurrent_insert));
655 
656   info->row_base_length= info->s->base_length;
657   info->row_flag= info->s->base.default_row_flag;
658   if (concurrent_insert)
659   {
660     DBUG_ASSERT(info->lock.type == TL_WRITE_CONCURRENT_INSERT);
661     info->row_flag|= ROW_FLAG_TRANSID;
662     info->row_base_length+= TRANSID_SIZE;
663   }
664   else
665   {
666     DBUG_ASSERT(info->lock.type != TL_WRITE_CONCURRENT_INSERT);
667   }
668   DBUG_VOID_RETURN;
669 }
670 
671 
_ma_block_start_trans(void * param)672 my_bool _ma_block_start_trans(void* param)
673 {
674   MARIA_HA *info=(MARIA_HA*) param;
675   DBUG_ENTER("_ma_block_start_trans");
676 
677   if (info->s->lock_key_trees)
678   {
679     /*
680       Assume for now that this doesn't fail (It can only fail in
681       out of memory conditions)
682       TODO: Fix this by having one extra state pre-allocated
683     */
684     DBUG_RETURN(_ma_setup_live_state(info));
685   }
686   else
687   {
688     /*
689       We come here in the following cases:
690       - The table is a temporary table
691       - It's a table which is crash safe but not yet versioned, for
692       example a table with fulltext or rtree keys
693 
694       Set the current state to point to save_state so that the
695       block_format code don't count the same record twice.
696       Copy also the current state. This may have been wrong if the
697       same file was used several times in the last statement
698     */
699     info->state=  info->state_start;
700     *info->state= info->s->state.state;
701   }
702 
703   /*
704     Info->trn is set if this table is already handled and we are
705     called from maria_versioning()
706   */
707   if (info->s->base.born_transactional && !info->trn)
708   {
709     /*
710       Assume for now that this doesn't fail (It can only fail in
711       out of memory conditions)
712     */
713     DBUG_RETURN(maria_create_trn_hook(info) != 0);
714   }
715   DBUG_RETURN(0);
716 }
717 
718 
_ma_block_update_status(void * param)719 void _ma_block_update_status(void *param __attribute__((unused)))
720 {
721 }
722 
_ma_block_restore_status(void * param)723 void _ma_block_restore_status(void *param __attribute__((unused)))
724 {
725 }
726 
727 
728 /**
729   Check if should allow concurrent inserts
730 
731   @return
732   @retval 0  ok to use concurrent inserts
733   @retval 1  not ok
734 */
735 
_ma_block_check_status(void * param)736 my_bool _ma_block_check_status(void *param __attribute__((unused)))
737 {
738   return (my_bool) 0;
739 }
740 
741 
742 /* Get status when transactional but not versioned */
743 
_ma_block_start_trans_no_versioning(void * param)744 my_bool _ma_block_start_trans_no_versioning(void* param)
745 {
746   MARIA_HA *info=(MARIA_HA*) param;
747   DBUG_ENTER("_ma_block_start_trans_no_versioning");
748   DBUG_ASSERT(info->s->base.born_transactional && !info->s->lock_key_trees);
749 
750   info->state->changed= 0;              /* from _ma_reset_update_flag() */
751   info->state=  info->state_start;
752   *info->state= info->s->state.state;
753   if (!info->trn)
754   {
755     /*
756       Assume for now that this doesn't fail (It can only fail in
757       out of memory conditions)
758     */
759     DBUG_RETURN(maria_create_trn_hook(info));
760   }
761   DBUG_RETURN(0);
762 }
763 
764 
765 /**
766   Enable/disable versioning
767 */
768 
maria_versioning(MARIA_HA * info,my_bool versioning)769 void maria_versioning(MARIA_HA *info, my_bool versioning)
770 {
771   MARIA_SHARE *share= info->s;
772   DBUG_ENTER("maria_versioning");
773 
774   /* For now, this is a hack */
775   if (share->have_versioning)
776   {
777     enum thr_lock_type save_lock_type;
778     share->lock_key_trees= versioning;
779     /* Set up info->lock.type temporary for _ma_block_get_status() */
780     save_lock_type= info->lock.type;
781     info->lock.type= versioning ? TL_WRITE_CONCURRENT_INSERT : TL_WRITE;
782     _ma_block_get_status((void*) info, versioning);
783     info->lock.type= save_lock_type;
784     if (versioning)
785       info->state= &share->state.common;
786     else
787       info->state= &share->state.state;	/* Change global values by default */
788     info->state_start= info->state;             /* Initial values */
789   }
790   DBUG_VOID_RETURN;
791 }
792 
793 
794 /**
795    Update data_file_length to new length
796 
797    NOTES
798      Only used by block records
799 */
800 
_ma_set_share_data_file_length(MARIA_SHARE * share,ulonglong new_length)801 void _ma_set_share_data_file_length(MARIA_SHARE *share, ulonglong new_length)
802 {
803   if (!share->internal_table)
804     mysql_mutex_lock(&share->intern_lock);
805   if (share->state.state.data_file_length < new_length)
806   {
807     share->state.state.data_file_length= new_length;
808     if (new_length >= share->base.max_data_file_length)
809     {
810       /* Give an error on next insert */
811       share->state.changed|= STATE_DATA_FILE_FULL;
812     }
813   }
814   if (!share->internal_table)
815   mysql_mutex_unlock(&share->intern_lock);
816 }
817 
818 
819 /**
820    Copy state information that where updated while the table was used
821    in not transactional mode
822 */
823 
_ma_copy_nontrans_state_information(MARIA_HA * info)824 void _ma_copy_nontrans_state_information(MARIA_HA *info)
825 {
826   info->s->state.state.records=          info->state->records;
827   info->s->state.state.checksum=         info->state->checksum;
828 }
829 
830 /**
831    Reset history
832    This is only called during repair when we are the only one using the table.
833 */
834 
_ma_reset_history(MARIA_SHARE * share)835 void _ma_reset_history(MARIA_SHARE *share)
836 {
837   MARIA_STATE_HISTORY *history, *next;
838   DBUG_ENTER("_ma_reset_history");
839 
840   share->state_history->trid= 0;          /* Visibly by all */
841   share->state_history->state= share->state.state;
842   history= share->state_history->next;
843   share->state_history->next= 0;
844 
845   for (; history; history= next)
846   {
847     next= history->next;
848     my_free(history);
849   }
850   DBUG_VOID_RETURN;
851 }
852 
853 
854 /****************************************************************************
855   Virtual functions to check if row is visible
856 ****************************************************************************/
857 
858 /**
859    Row is always visible
860    This is for tables without concurrent insert
861 */
862 
_ma_row_visible_always(MARIA_HA * info)863 my_bool _ma_row_visible_always(MARIA_HA *info __attribute__((unused)))
864 {
865   return 1;
866 }
867 
868 
869 /**
870    Row visibility for non transactional tables with concurrent insert
871 
872    @implementation
873    When we got our table lock, we saved the current
874    data_file_length. Concurrent inserts always go to the end of the
875    file. So we can test if the found key references a new record.
876 */
877 
_ma_row_visible_non_transactional_table(MARIA_HA * info)878 my_bool _ma_row_visible_non_transactional_table(MARIA_HA *info)
879 {
880   return info->cur_row.lastpos < info->state->data_file_length;
881 }
882 
883 
884 /**
885    Row visibility for transactional tables with versioning
886 
887 
888    @TODO
889    Add test if found key was marked deleted and it was deleted by
890    us. In that case we should return 0
891 */
892 
_ma_row_visible_transactional_table(MARIA_HA * info)893 my_bool _ma_row_visible_transactional_table(MARIA_HA *info)
894 {
895   return trnman_can_read_from(info->trn, info->cur_row.trid);
896 }
897