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